Merge "Add COMPANION_APPROVE_WIFI_CONNECTIONS to shell permissions" into rvc-dev
diff --git a/Android.bp b/Android.bp
index 4a1f96e..5e068ee 100644
--- a/Android.bp
+++ b/Android.bp
@@ -481,6 +481,7 @@
"framework-platform-compat-config",
"libcore-platform-compat-config",
"services-platform-compat-config",
+ "documents-ui-compat-config",
],
static_libs: [
// If MimeMap ever becomes its own APEX, then this dependency would need to be removed
diff --git a/apex/Android.bp b/apex/Android.bp
index 362cf95..051986e 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -31,12 +31,12 @@
priv_apps = " " +
"--show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," +
+ "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
"\\) "
module_libs = " " +
" --show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES," +
+ "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES" +
"\\) "
stubs_defaults {
@@ -48,6 +48,7 @@
stubs_defaults {
name: "framework-module-stubs-defaults-systemapi",
args: mainline_stubs_args + priv_apps,
+ srcs: [":framework-annotations"],
installable: false,
}
@@ -59,11 +60,13 @@
stubs_defaults {
name: "framework-module-api-defaults-module_libs_api",
args: mainline_stubs_args + module_libs,
+ srcs: [":framework-annotations"],
installable: false,
}
stubs_defaults {
name: "framework-module-stubs-defaults-module_libs_api",
args: mainline_stubs_args + module_libs + priv_apps,
+ srcs: [":framework-annotations"],
installable: false,
}
diff --git a/apex/extservices/Android.bp b/apex/extservices/Android.bp
index c89f694..021246c 100644
--- a/apex/extservices/Android.bp
+++ b/apex/extservices/Android.bp
@@ -20,6 +20,7 @@
apex_defaults {
name: "com.android.extservices-defaults",
+ updatable: true,
key: "com.android.extservices.key",
certificate: ":com.android.extservices.certificate",
apps: ["ExtServices"],
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
index 4c98b5f..62c90dfa 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
@@ -54,7 +54,8 @@
*
* @hide
*/
- public static final int REASON_RESTRAINED = JobProtoEnums.STOP_REASON_RESTRAINED; // 6.
+ public static final int REASON_RESTRICTED_BUCKET =
+ JobProtoEnums.STOP_REASON_RESTRICTED_BUCKET; // 6.
/**
* All the stop reason codes. This should be regarded as an immutable array at runtime.
@@ -72,7 +73,7 @@
REASON_TIMEOUT,
REASON_DEVICE_IDLE,
REASON_DEVICE_THERMAL,
- REASON_RESTRAINED,
+ REASON_RESTRICTED_BUCKET,
};
/**
@@ -88,7 +89,7 @@
case REASON_TIMEOUT: return "timeout";
case REASON_DEVICE_IDLE: return "device_idle";
case REASON_DEVICE_THERMAL: return "thermal";
- case REASON_RESTRAINED: return "restrained";
+ case REASON_RESTRICTED_BUCKET: return "restricted_bucket";
default: return "unknown:" + reasonCode;
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index c1e529f..8c08e75 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1074,7 +1074,15 @@
uId, null, jobStatus.getBatteryName(),
FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED,
JobProtoEnums.STOP_REASON_CANCELLED, jobStatus.getStandbyBucket(),
- jobStatus.getJobId());
+ jobStatus.getJobId(),
+ jobStatus.hasChargingConstraint(),
+ jobStatus.hasBatteryNotLowConstraint(),
+ jobStatus.hasStorageNotLowConstraint(),
+ jobStatus.hasTimingDelayConstraint(),
+ jobStatus.hasDeadlineConstraint(),
+ jobStatus.hasIdleConstraint(),
+ jobStatus.hasConnectivityConstraint(),
+ jobStatus.hasContentTriggerConstraint());
// If the job is immediately ready to run, then we can just immediately
// put it in the pending list and try to schedule it. This is especially
@@ -1955,9 +1963,19 @@
continue;
}
if (!running.isReady()) {
- serviceContext.cancelExecutingJobLocked(
- JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED,
- "cancelled due to unsatisfied constraints");
+ // If a restricted job doesn't have dynamic constraints satisfied, assume that's
+ // the reason the job is being stopped, instead of because of other constraints
+ // not being satisfied.
+ if (running.getEffectiveStandbyBucket() == RESTRICTED_INDEX
+ && !running.areDynamicConstraintsSatisfied()) {
+ serviceContext.cancelExecutingJobLocked(
+ JobParameters.REASON_RESTRICTED_BUCKET,
+ "cancelled due to restricted bucket");
+ } else {
+ serviceContext.cancelExecutingJobLocked(
+ JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED,
+ "cancelled due to unsatisfied constraints");
+ }
} else {
final JobRestriction restriction = checkIfRestricted(running);
if (restriction != null) {
@@ -3015,6 +3033,10 @@
return 0;
}
+ void resetExecutionQuota(@NonNull String pkgName, int userId) {
+ mQuotaController.clearAppStats(userId, pkgName);
+ }
+
void resetScheduleQuota() {
mQuotaTracker.clear();
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
index 6becf04..9571708 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java
@@ -66,6 +66,8 @@
return getJobState(pw);
case "heartbeat":
return doHeartbeat(pw);
+ case "reset-execution-quota":
+ return resetExecutionQuota(pw);
case "reset-schedule-quota":
return resetScheduleQuota(pw);
case "trigger-dock-state":
@@ -346,6 +348,40 @@
return -1;
}
+ private int resetExecutionQuota(PrintWriter pw) throws Exception {
+ checkPermission("reset execution quota");
+
+ int userId = UserHandle.USER_SYSTEM;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-u":
+ case "--user":
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ break;
+
+ default:
+ pw.println("Error: unknown option '" + opt + "'");
+ return -1;
+ }
+ }
+
+ if (userId == UserHandle.USER_CURRENT) {
+ userId = ActivityManager.getCurrentUser();
+ }
+
+ final String pkgName = getNextArgRequired();
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mInternal.resetExecutionQuota(pkgName, userId);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ return 0;
+ }
+
private int resetScheduleQuota(PrintWriter pw) throws Exception {
checkPermission("reset schedule quota");
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 26db4a3..565ed95 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -22,6 +22,7 @@
import android.app.job.IJobService;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
+import android.app.job.JobProtoEnums;
import android.app.job.JobWorkItem;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ComponentName;
@@ -45,6 +46,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.job.controllers.JobStatus;
@@ -273,9 +275,20 @@
return false;
}
mJobPackageTracker.noteActive(job);
+ FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
+ job.getSourceUid(), null, job.getBatteryName(),
+ FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__STARTED,
+ JobProtoEnums.STOP_REASON_UNKNOWN, job.getStandbyBucket(), job.getJobId(),
+ job.hasChargingConstraint(),
+ job.hasBatteryNotLowConstraint(),
+ job.hasStorageNotLowConstraint(),
+ job.hasTimingDelayConstraint(),
+ job.hasDeadlineConstraint(),
+ job.hasIdleConstraint(),
+ job.hasConnectivityConstraint(),
+ job.hasContentTriggerConstraint());
try {
- mBatteryStats.noteJobStart(job.getBatteryName(), job.getSourceUid(),
- job.getStandbyBucket(), job.getJobId());
+ mBatteryStats.noteJobStart(job.getBatteryName(), job.getSourceUid());
} catch (RemoteException e) {
// Whatever.
}
@@ -779,10 +792,21 @@
applyStoppedReasonLocked(reason);
completedJob = mRunningJob;
mJobPackageTracker.noteInactive(completedJob, mParams.getStopReason(), reason);
+ FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
+ completedJob.getSourceUid(), null, completedJob.getBatteryName(),
+ FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__FINISHED,
+ mParams.getStopReason(), completedJob.getStandbyBucket(), completedJob.getJobId(),
+ completedJob.hasChargingConstraint(),
+ completedJob.hasBatteryNotLowConstraint(),
+ completedJob.hasStorageNotLowConstraint(),
+ completedJob.hasTimingDelayConstraint(),
+ completedJob.hasDeadlineConstraint(),
+ completedJob.hasIdleConstraint(),
+ completedJob.hasConnectivityConstraint(),
+ completedJob.hasContentTriggerConstraint());
try {
- mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(),
- mRunningJob.getSourceUid(), mParams.getStopReason(),
- mRunningJob.getStandbyBucket(), mRunningJob.getJobId());
+ mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(), mRunningJob.getSourceUid(),
+ mParams.getStopReason());
} catch (RemoteException e) {
// Whatever.
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
new file mode 100644
index 0000000..9a7d55a
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
@@ -0,0 +1,19 @@
+{
+ "postsubmit": [
+ {
+ "name": "CtsJobSchedulerTestCases"
+ },
+ {
+ "name": "FrameworksMockingServicesTests",
+ "options": [
+ {"include-filter": "com.android.server.job"}
+ ]
+ },
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {"include-filter": "com.android.server.job"}
+ ]
+ }
+ ]
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
index aa3d74a..d7b1e07 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
@@ -16,6 +16,8 @@
package com.android.server.job.controllers;
+import static com.android.server.job.JobSchedulerService.NEVER_INDEX;
+
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Log;
@@ -196,6 +198,9 @@
} else {
isActive = (activeState == KNOWN_ACTIVE);
}
+ if (isActive && jobStatus.getStandbyBucket() == NEVER_INDEX) {
+ Slog.wtf(TAG, "App became active but still in NEVER bucket");
+ }
boolean didChange = jobStatus.setBackgroundNotRestrictedConstraintSatisfied(canRun);
didChange |= jobStatus.setUidActive(isActive);
return didChange;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index b63cc19..81dbc87 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -1271,7 +1271,8 @@
// sessions (exempt from dynamic restrictions), we need the additional check to ensure
// that NEVER jobs don't run.
// TODO: cleanup quota and standby bucket management so we don't need the additional checks
- if ((!mReadyWithinQuota && !mReadyDynamicSatisfied) || standbyBucket == NEVER_INDEX) {
+ if ((!mReadyWithinQuota && !mReadyDynamicSatisfied)
+ || getEffectiveStandbyBucket() == NEVER_INDEX) {
return false;
}
// Deadline constraint trumps other constraints besides quota and dynamic (except for
@@ -1293,6 +1294,11 @@
CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW | CONSTRAINT_STORAGE_NOT_LOW
| CONSTRAINT_TIMING_DELAY | CONSTRAINT_IDLE;
+ /** Returns true whenever all dynamically set constraints are satisfied. */
+ public boolean areDynamicConstraintsSatisfied() {
+ return mReadyDynamicSatisfied;
+ }
+
/**
* @return Whether the constraints set on this job are satisfied.
*/
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 8eefac8..7256371 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
@@ -54,12 +54,14 @@
import android.util.ArraySet;
import android.util.KeyValueListParser;
import android.util.Log;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArrayMap;
import android.util.SparseBooleanArray;
import android.util.SparseSetArray;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.IndentingPrintWriter;
@@ -74,6 +76,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import java.util.PriorityQueue;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -301,10 +304,10 @@
private final SparseArrayMap<List<TimingSession>> mTimingSessions = new SparseArrayMap<>();
/**
- * List of alarm listeners for each package that listen for when each package comes back within
- * quota.
+ * Listener to track and manage when each package comes back within quota.
*/
- private final SparseArrayMap<QcAlarmListener> mInQuotaAlarmListeners = new SparseArrayMap<>();
+ @GuardedBy("mLock")
+ private final InQuotaAlarmListener mInQuotaAlarmListener = new InQuotaAlarmListener();
/** Cached calculation results for each app, with the standby buckets as the array indices. */
private final SparseArrayMap<ExecutionStats[]> mExecutionStatsCache = new SparseArrayMap<>();
@@ -579,23 +582,7 @@
Slog.wtf(TAG, "Told app removed but given null package name.");
return;
}
- final int userId = UserHandle.getUserId(uid);
- mTrackedJobs.delete(userId, packageName);
- Timer timer = mPkgTimers.get(userId, packageName);
- if (timer != null) {
- if (timer.isActive()) {
- Slog.wtf(TAG, "onAppRemovedLocked called before Timer turned off.");
- timer.dropEverythingLocked();
- }
- mPkgTimers.delete(userId, packageName);
- }
- mTimingSessions.delete(userId, packageName);
- QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, packageName);
- if (alarmListener != null) {
- mAlarmManager.cancel(alarmListener);
- mInQuotaAlarmListeners.delete(userId, packageName);
- }
- mExecutionStatsCache.delete(userId, packageName);
+ clearAppStats(UserHandle.getUserId(uid), packageName);
mForegroundUids.delete(uid);
mUidToPackageCache.remove(uid);
}
@@ -605,11 +592,27 @@
mTrackedJobs.delete(userId);
mPkgTimers.delete(userId);
mTimingSessions.delete(userId);
- mInQuotaAlarmListeners.delete(userId);
+ mInQuotaAlarmListener.removeAlarmsLocked(userId);
mExecutionStatsCache.delete(userId);
mUidToPackageCache.clear();
}
+ /** Drop all historical stats and stop tracking any active sessions for the specified app. */
+ public void clearAppStats(int userId, @NonNull String packageName) {
+ mTrackedJobs.delete(userId, packageName);
+ Timer timer = mPkgTimers.get(userId, packageName);
+ if (timer != null) {
+ if (timer.isActive()) {
+ Slog.e(TAG, "clearAppStats called before Timer turned off.");
+ timer.dropEverythingLocked();
+ }
+ mPkgTimers.delete(userId, packageName);
+ }
+ mTimingSessions.delete(userId, packageName);
+ mInQuotaAlarmListener.removeAlarmLocked(userId, packageName);
+ mExecutionStatsCache.delete(userId, packageName);
+ }
+
private boolean isUidInForeground(int uid) {
if (UserHandle.isCore(uid)) {
return true;
@@ -1204,12 +1207,7 @@
// exempted.
maybeScheduleStartAlarmLocked(userId, packageName, realStandbyBucket);
} else {
- QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, packageName);
- if (alarmListener != null && alarmListener.isWaiting()) {
- mAlarmManager.cancel(alarmListener);
- // Set the trigger time to 0 so that the alarm doesn't think it's still waiting.
- alarmListener.setTriggerTime(0);
- }
+ mInQuotaAlarmListener.removeAlarmLocked(userId, packageName);
}
return changed;
}
@@ -1225,12 +1223,7 @@
final String packageName = jobStatus.getSourcePackageName();
final int realStandbyBucket = jobStatus.getStandbyBucket();
if (isWithinQuotaLocked(userId, packageName, realStandbyBucket)) {
- QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, packageName);
- if (alarmListener != null && alarmListener.isWaiting()) {
- mAlarmManager.cancel(alarmListener);
- // Set the trigger time to 0 so that the alarm doesn't think it's still waiting.
- alarmListener.setTriggerTime(0);
- }
+ mInQuotaAlarmListener.removeAlarmLocked(userId, packageName);
} else {
mToScheduleStartAlarms.add(userId, packageName, realStandbyBucket);
}
@@ -1281,7 +1274,6 @@
final boolean isUnderTimingSessionCountQuota = isUnderSessionCountQuotaLocked(stats,
standbyBucket);
- QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, packageName);
if (stats.executionTimeInWindowMs < mAllowedTimePerPeriodMs
&& stats.executionTimeInMaxPeriodMs < mMaxExecutionTimeMs
&& isUnderJobCountQuota
@@ -1293,21 +1285,11 @@
+ getRemainingExecutionTimeLocked(userId, packageName, standbyBucket)
+ "ms in its quota.");
}
- if (alarmListener != null) {
- // Cancel any pending alarm.
- mAlarmManager.cancel(alarmListener);
- // Set the trigger time to 0 so that the alarm doesn't think it's still waiting.
- alarmListener.setTriggerTime(0);
- }
+ mInQuotaAlarmListener.removeAlarmLocked(userId, packageName);
mHandler.obtainMessage(MSG_CHECK_PACKAGE, userId, 0, packageName).sendToTarget();
return;
}
- if (alarmListener == null) {
- alarmListener = new QcAlarmListener(userId, packageName);
- mInQuotaAlarmListeners.add(userId, packageName, alarmListener);
- }
-
// The time this app will have quota again.
long inQuotaTimeElapsed = stats.inQuotaTimeElapsed;
if (!isUnderJobCountQuota && stats.bgJobCountInWindow < stats.jobCountLimit) {
@@ -1321,27 +1303,7 @@
inQuotaTimeElapsed = Math.max(inQuotaTimeElapsed,
stats.sessionRateLimitExpirationTimeElapsed);
}
- // Only schedule the alarm if:
- // 1. There isn't one currently scheduled
- // 2. The new alarm is significantly earlier than the previous alarm (which could be the
- // case if the package moves into a higher standby bucket). If it's earlier but not
- // significantly so, then we essentially delay the job a few extra minutes.
- // 3. The alarm is after the current alarm by more than the quota buffer.
- // TODO: this might be overengineering. Simplify if proven safe.
- if (!alarmListener.isWaiting()
- || inQuotaTimeElapsed < alarmListener.getTriggerTimeElapsed() - 3 * MINUTE_IN_MILLIS
- || alarmListener.getTriggerTimeElapsed() < inQuotaTimeElapsed) {
- if (DEBUG) {
- Slog.d(TAG, "Scheduling start alarm for " + pkgString);
- }
- // If the next time this app will have quota is at least 3 minutes before the
- // alarm is supposed to go off, reschedule the alarm.
- mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, inQuotaTimeElapsed,
- ALARM_TAG_QUOTA_CHECK, alarmListener, mHandler);
- alarmListener.setTriggerTime(inQuotaTimeElapsed);
- } else if (DEBUG) {
- Slog.d(TAG, "No need to schedule start alarm for " + pkgString);
- }
+ mInQuotaAlarmListener.addAlarmLocked(userId, packageName, inQuotaTimeElapsed);
}
private boolean setConstraintSatisfied(@NonNull JobStatus jobStatus, boolean isWithinQuota) {
@@ -1871,32 +1833,161 @@
}
}
- private class QcAlarmListener implements AlarmManager.OnAlarmListener {
- private final int mUserId;
- private final String mPackageName;
- private volatile long mTriggerTimeElapsed;
-
- QcAlarmListener(int userId, String packageName) {
- mUserId = userId;
- mPackageName = packageName;
+ static class AlarmQueue extends PriorityQueue<Pair<Package, Long>> {
+ AlarmQueue() {
+ super(1, (o1, o2) -> (int) (o1.second - o2.second));
}
- boolean isWaiting() {
- return mTriggerTimeElapsed > 0;
+ /**
+ * Remove any instances of the Package from the queue.
+ *
+ * @return true if an instance was removed, false otherwise.
+ */
+ boolean remove(@NonNull Package pkg) {
+ boolean removed = false;
+ Pair[] alarms = toArray(new Pair[size()]);
+ for (int i = alarms.length - 1; i >= 0; --i) {
+ if (pkg.equals(alarms[i].first)) {
+ remove(alarms[i]);
+ removed = true;
+ }
+ }
+ return removed;
+ }
+ }
+
+ /** Track when UPTCs are expected to come back into quota. */
+ private class InQuotaAlarmListener implements AlarmManager.OnAlarmListener {
+ @GuardedBy("mLock")
+ private final AlarmQueue mAlarmQueue = new AlarmQueue();
+ /** The next time the alarm is set to go off, in the elapsed realtime timebase. */
+ @GuardedBy("mLock")
+ private long mTriggerTimeElapsed = 0;
+
+ @GuardedBy("mLock")
+ void addAlarmLocked(int userId, @NonNull String pkgName, long inQuotaTimeElapsed) {
+ final Package pkg = new Package(userId, pkgName);
+ mAlarmQueue.remove(pkg);
+ mAlarmQueue.offer(new Pair<>(pkg, inQuotaTimeElapsed));
+ setNextAlarmLocked();
}
- void setTriggerTime(long timeElapsed) {
- mTriggerTimeElapsed = timeElapsed;
+ @GuardedBy("mLock")
+ void removeAlarmLocked(@NonNull Package pkg) {
+ if (mAlarmQueue.remove(pkg)) {
+ setNextAlarmLocked();
+ }
}
- long getTriggerTimeElapsed() {
- return mTriggerTimeElapsed;
+ @GuardedBy("mLock")
+ void removeAlarmLocked(int userId, @NonNull String packageName) {
+ removeAlarmLocked(new Package(userId, packageName));
+ }
+
+ @GuardedBy("mLock")
+ void removeAlarmsLocked(int userId) {
+ boolean removed = false;
+ Pair[] alarms = mAlarmQueue.toArray(new Pair[mAlarmQueue.size()]);
+ for (int i = alarms.length - 1; i >= 0; --i) {
+ final Package pkg = (Package) alarms[i].first;
+ if (userId == pkg.userId) {
+ mAlarmQueue.remove(alarms[i]);
+ removed = true;
+ }
+ }
+ if (removed) {
+ setNextAlarmLocked();
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void setNextAlarmLocked() {
+ if (mAlarmQueue.size() > 0) {
+ final long nextTriggerTimeElapsed = mAlarmQueue.peek().second;
+ // Only schedule the alarm if one of the following is true:
+ // 1. There isn't one currently scheduled
+ // 2. The new alarm is significantly earlier than the previous alarm. If it's
+ // earlier but not significantly so, then we essentially delay the job a few extra
+ // minutes.
+ // 3. The alarm is after the current alarm.
+ if (mTriggerTimeElapsed == 0
+ || nextTriggerTimeElapsed < mTriggerTimeElapsed - 3 * MINUTE_IN_MILLIS
+ || mTriggerTimeElapsed < nextTriggerTimeElapsed) {
+ if (DEBUG) {
+ Slog.d(TAG, "Scheduling start alarm at " + nextTriggerTimeElapsed);
+ }
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, nextTriggerTimeElapsed,
+ ALARM_TAG_QUOTA_CHECK, this, mHandler);
+ mTriggerTimeElapsed = nextTriggerTimeElapsed;
+ }
+ } else {
+ mAlarmManager.cancel(this);
+ mTriggerTimeElapsed = 0;
+ }
}
@Override
public void onAlarm() {
- mHandler.obtainMessage(MSG_CHECK_PACKAGE, mUserId, 0, mPackageName).sendToTarget();
- mTriggerTimeElapsed = 0;
+ synchronized (mLock) {
+ while (mAlarmQueue.size() > 0) {
+ final Pair<Package, Long> alarm = mAlarmQueue.peek();
+ if (alarm.second <= sElapsedRealtimeClock.millis()) {
+ mHandler.obtainMessage(MSG_CHECK_PACKAGE, alarm.first.userId, 0,
+ alarm.first.packageName).sendToTarget();
+ mAlarmQueue.remove(alarm);
+ } else {
+ break;
+ }
+ }
+ setNextAlarmLocked();
+ }
+ }
+
+ @GuardedBy("mLock")
+ void dumpLocked(IndentingPrintWriter pw) {
+ pw.println("In quota alarms:");
+ pw.increaseIndent();
+
+ if (mAlarmQueue.size() == 0) {
+ pw.println("NOT WAITING");
+ } else {
+ Pair[] alarms = mAlarmQueue.toArray(new Pair[mAlarmQueue.size()]);
+ for (int i = 0; i < alarms.length; ++i) {
+ final Package pkg = (Package) alarms[i].first;
+ pw.print(pkg);
+ pw.print(": ");
+ pw.print(alarms[i].second);
+ pw.println();
+ }
+ }
+
+ pw.decreaseIndent();
+ }
+
+ @GuardedBy("mLock")
+ void dumpLocked(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+
+ proto.write(
+ StateControllerProto.QuotaController.InQuotaAlarmListener.TRIGGER_TIME_ELAPSED,
+ mTriggerTimeElapsed);
+
+ Pair[] alarms = mAlarmQueue.toArray(new Pair[mAlarmQueue.size()]);
+ for (int i = 0; i < alarms.length; ++i) {
+ final long aToken = proto.start(
+ StateControllerProto.QuotaController.InQuotaAlarmListener.ALARMS);
+
+ final Package pkg = (Package) alarms[i].first;
+ pkg.dumpDebug(proto,
+ StateControllerProto.QuotaController.InQuotaAlarmListener.Alarm.PKG);
+ proto.write(
+ StateControllerProto.QuotaController.InQuotaAlarmListener.Alarm.IN_QUOTA_TIME_ELAPSED,
+ (Long) alarms[i].second);
+
+ proto.end(aToken);
+ }
+
+ proto.end(token);
}
}
@@ -2614,23 +2705,7 @@
pw.decreaseIndent();
pw.println();
- pw.println("In quota alarms:");
- pw.increaseIndent();
- for (int u = 0; u < mInQuotaAlarmListeners.numMaps(); ++u) {
- final int userId = mInQuotaAlarmListeners.keyAt(u);
- for (int p = 0; p < mInQuotaAlarmListeners.numElementsForKey(userId); ++p) {
- final String pkgName = mInQuotaAlarmListeners.keyAt(u, p);
- QcAlarmListener alarmListener = mInQuotaAlarmListeners.valueAt(u, p);
-
- pw.print(string(userId, pkgName));
- pw.print(": ");
- if (alarmListener.isWaiting()) {
- pw.println(alarmListener.getTriggerTimeElapsed());
- } else {
- pw.println("NOT WAITING");
- }
- }
- }
+ mInQuotaAlarmListener.dumpLocked(pw);
pw.decreaseIndent();
}
@@ -2764,22 +2839,13 @@
}
}
- QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, pkgName);
- if (alarmListener != null) {
- final long alToken = proto.start(
- StateControllerProto.QuotaController.PackageStats.IN_QUOTA_ALARM_LISTENER);
- proto.write(StateControllerProto.QuotaController.AlarmListener.IS_WAITING,
- alarmListener.isWaiting());
- proto.write(
- StateControllerProto.QuotaController.AlarmListener.TRIGGER_TIME_ELAPSED,
- alarmListener.getTriggerTimeElapsed());
- proto.end(alToken);
- }
-
proto.end(psToken);
}
}
+ mInQuotaAlarmListener.dumpLocked(proto,
+ StateControllerProto.QuotaController.IN_QUOTA_ALARM_LISTENER);
+
proto.end(mToken);
proto.end(token);
}
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
index 932c25d..46d449a 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
@@ -21,6 +21,7 @@
import static android.app.usage.UsageStatsManager.REASON_MAIN_MASK;
import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_MASK;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACTION;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
@@ -43,6 +44,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import libcore.io.IoUtils;
@@ -244,9 +246,9 @@
* @param elapsedRealtime mark as used time if non-zero
* @param timeout set the timeout of the specified bucket, if non-zero. Can only be used
* with bucket values of ACTIVE and WORKING_SET.
- * @return
+ * @return {@code appUsageHistory}
*/
- public AppUsageHistory reportUsage(AppUsageHistory appUsageHistory, String packageName,
+ AppUsageHistory reportUsage(AppUsageHistory appUsageHistory, String packageName, int userId,
int newBucket, int usageReason, long elapsedRealtime, long timeout) {
int bucketingReason = REASON_MAIN_USAGE | usageReason;
final boolean isUserUsage = isUserUsage(bucketingReason);
@@ -284,11 +286,7 @@
if (appUsageHistory.currentBucket > newBucket) {
appUsageHistory.currentBucket = newBucket;
- if (DEBUG) {
- Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory
- .currentBucket
- + ", reason=0x0" + Integer.toHexString(appUsageHistory.bucketingReason));
- }
+ logAppStandbyBucketChanged(packageName, userId, newBucket, bucketingReason);
}
appUsageHistory.bucketingReason = bucketingReason;
@@ -313,7 +311,8 @@
int usageReason, long nowElapsed, long timeout) {
ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
AppUsageHistory history = getPackageHistory(userHistory, packageName, nowElapsed, true);
- return reportUsage(history, packageName, newBucket, usageReason, nowElapsed, timeout);
+ return reportUsage(history, packageName, userId, newBucket, usageReason, nowElapsed,
+ timeout);
}
private ArrayMap<String, AppUsageHistory> getUserHistory(int userId) {
@@ -372,6 +371,7 @@
ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
AppUsageHistory appUsageHistory =
getPackageHistory(userHistory, packageName, elapsedRealtime, true);
+ final boolean changed = appUsageHistory.currentBucket != bucket;
appUsageHistory.currentBucket = bucket;
appUsageHistory.bucketingReason = reason;
@@ -385,9 +385,8 @@
appUsageHistory.bucketActiveTimeoutTime = elapsed;
appUsageHistory.bucketWorkingSetTimeoutTime = elapsed;
}
- if (DEBUG) {
- Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
- + ", reason=0x0" + Integer.toHexString(appUsageHistory.bucketingReason));
+ if (changed) {
+ logAppStandbyBucketChanged(packageName, userId, bucket, reason);
}
}
@@ -485,18 +484,19 @@
/* Returns the new standby bucket the app is assigned to */
public int setIdle(String packageName, int userId, boolean idle, long elapsedRealtime) {
- ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
- AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
- elapsedRealtime, true);
+ final int newBucket;
+ final int reason;
if (idle) {
- appUsageHistory.currentBucket = STANDBY_BUCKET_RARE;
- appUsageHistory.bucketingReason = REASON_MAIN_FORCED_BY_USER;
+ newBucket = STANDBY_BUCKET_RARE;
+ reason = REASON_MAIN_FORCED_BY_USER;
} else {
- appUsageHistory.currentBucket = STANDBY_BUCKET_ACTIVE;
+ newBucket = STANDBY_BUCKET_ACTIVE;
// This is to pretend that the app was just used, don't freeze the state anymore.
- appUsageHistory.bucketingReason = REASON_MAIN_USAGE | REASON_SUB_USAGE_USER_INTERACTION;
+ reason = REASON_MAIN_USAGE | REASON_SUB_USAGE_USER_INTERACTION;
}
- return appUsageHistory.currentBucket;
+ setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket, reason, false);
+
+ return newBucket;
}
public void clearUsage(String packageName, int userId) {
@@ -551,13 +551,27 @@
return 0;
}
+ /**
+ * Log a standby bucket change to statsd, and also logcat if debug logging is enabled.
+ */
+ private void logAppStandbyBucketChanged(String packageName, int userId, int bucket,
+ int reason) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.APP_STANDBY_BUCKET_CHANGED,
+ packageName, userId, bucket,
+ (reason & REASON_MAIN_MASK), (reason & REASON_SUB_MASK));
+ if (DEBUG) {
+ Slog.d(TAG, "Moved " + packageName + " to bucket=" + bucket
+ + ", reason=0x0" + Integer.toHexString(reason));
+ }
+ }
+
@VisibleForTesting
File getUserFile(int userId) {
return new File(new File(new File(mStorageDir, "users"),
Integer.toString(userId)), APP_IDLE_FILENAME);
}
-
/**
* Check if App Idle File exists on disk
* @param userId
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 7a1b4f2..5992253 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -831,24 +831,24 @@
if (eventType == UsageEvents.Event.NOTIFICATION_SEEN
|| eventType == UsageEvents.Event.SLICE_PINNED) {
// Mild usage elevates to WORKING_SET but doesn't change usage time.
- mAppIdleHistory.reportUsage(appHistory, pkg,
+ mAppIdleHistory.reportUsage(appHistory, pkg, userId,
STANDBY_BUCKET_WORKING_SET, subReason,
0, elapsedRealtime + mNotificationSeenTimeoutMillis);
nextCheckDelay = mNotificationSeenTimeoutMillis;
} else if (eventType == UsageEvents.Event.SYSTEM_INTERACTION) {
- mAppIdleHistory.reportUsage(appHistory, pkg,
+ mAppIdleHistory.reportUsage(appHistory, pkg, userId,
STANDBY_BUCKET_ACTIVE, subReason,
0, elapsedRealtime + mSystemInteractionTimeoutMillis);
nextCheckDelay = mSystemInteractionTimeoutMillis;
} else if (eventType == UsageEvents.Event.FOREGROUND_SERVICE_START) {
// Only elevate bucket if this is the first usage of the app
if (prevBucket != STANDBY_BUCKET_NEVER) return;
- mAppIdleHistory.reportUsage(appHistory, pkg,
+ mAppIdleHistory.reportUsage(appHistory, pkg, userId,
STANDBY_BUCKET_ACTIVE, subReason,
0, elapsedRealtime + mInitialForegroundServiceStartTimeoutMillis);
nextCheckDelay = mInitialForegroundServiceStartTimeoutMillis;
} else {
- mAppIdleHistory.reportUsage(appHistory, pkg,
+ mAppIdleHistory.reportUsage(appHistory, pkg, userId,
STANDBY_BUCKET_ACTIVE, subReason,
elapsedRealtime, elapsedRealtime + mStrongUsageTimeoutMillis);
nextCheckDelay = mStrongUsageTimeoutMillis;
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
index cf70878..ba7572a 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
@@ -1,6 +1,13 @@
{
"presubmit": [
{
+ "name": "CtsUsageStatsTestCases",
+ "options": [
+ {"include-filter": "android.app.usage.cts.UsageStatsTest"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"}
+ ]
+ },
+ {
"name": "FrameworksServicesTests",
"options": [
{"include-filter": "com.android.server.usage"},
@@ -10,6 +17,9 @@
],
"postsubmit": [
{
+ "name": "CtsUsageStatsTestCases"
+ },
+ {
"name": "FrameworksServicesTests",
"options": [
{"include-filter": "com.android.server.usage"}
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
index 566f4cd..a4f7b5b 100644
--- a/apex/media/framework/java/android/media/MediaParser.java
+++ b/apex/media/framework/java/android/media/MediaParser.java
@@ -94,61 +94,84 @@
* which extracts and publishes all video samples:
*
* <pre>
- *
* class VideoOutputConsumer implements MediaParser.OutputConsumer {
*
- * private static final int MAXIMUM_SAMPLE_SIZE = ...;
- * private byte[] sampleDataBuffer = new byte[MAXIMUM_SAMPLE_SIZE];
+ * private byte[] sampleDataBuffer = new byte[4096];
+ * private byte[] discardedDataBuffer = new byte[4096];
* private int videoTrackIndex = -1;
* private int bytesWrittenCount = 0;
*
- * \@Override
- * public void onSeekMap(int i, @NonNull MediaFormat mediaFormat) { \/* Do nothing. *\/ }
+ * @Override
+ * public void onSeekMap(int i, @NonNull MediaFormat mediaFormat) {
+ * // Do nothing.
+ * }
*
- * \@Override
- * public void onTrackData(int i, @NonNull TrackData trackData) {
+ * @Override
+ * public void onTrackData(int i, @NonNull TrackData trackData) {
* MediaFormat mediaFormat = trackData.mediaFormat;
- * if (videoTrackIndex == -1 && mediaFormat
- * .getString(MediaFormat.KEY_MIME, \/* defaultValue= *\/ "").startsWith("video/")) {
+ * if (videoTrackIndex == -1 &&
+ * mediaFormat
+ * .getString(MediaFormat.KEY_MIME, /* defaultValue= */ "")
+ * .startsWith("video/")) {
* videoTrackIndex = i;
* }
* }
*
- * \@Override
- * public void onSampleData(int trackIndex, @NonNull InputReader inputReader)
+ * @Override
+ * public void onSampleData(int trackIndex, @NonNull InputReader inputReader)
* throws IOException, InterruptedException {
* int numberOfBytesToRead = (int) inputReader.getLength();
* if (videoTrackIndex != trackIndex) {
* // Discard contents.
- * inputReader.read(\/* bytes= *\/ null, \/* offset= *\/ 0, numberOfBytesToRead);
+ * inputReader.read(
+ * discardedDataBuffer,
+ * /* offset= */ 0,
+ * Math.min(discardDataBuffer.length, numberOfBytesToRead));
+ * } else {
+ * ensureSpaceInBuffer(numberOfBytesToRead);
+ * int bytesRead = inputReader.read(
+ * sampleDataBuffer, bytesWrittenCount, numberOfBytesToRead);
+ * bytesWrittenCount += bytesRead;
* }
- * int bytesRead = inputReader.read(sampleDataBuffer, bytesWrittenCount, numberOfBytesToRead);
- * bytesWrittenCount += bytesRead;
* }
*
- * \@Override
+ * @Override
* public void onSampleCompleted(
* int trackIndex,
* long timeUs,
* int flags,
* int size,
* int offset,
- * \@Nullable CryptoInfo cryptoData) {
+ * @Nullable CryptoInfo cryptoData) {
* if (videoTrackIndex != trackIndex) {
* return; // It's not the video track. Ignore.
* }
* byte[] sampleData = new byte[size];
- * System.arraycopy(sampleDataBuffer, bytesWrittenCount - size - offset, sampleData, \/*
- * destPos= *\/ 0, size);
+ * int sampleStartOffset = bytesWrittenCount - size - offset;
+ * System.arraycopy(
+ * sampleDataBuffer,
+ * sampleStartOffset,
+ * sampleData,
+ * /* destPos= */ 0,
+ * size);
* // Place trailing bytes at the start of the buffer.
* System.arraycopy(
* sampleDataBuffer,
* bytesWrittenCount - offset,
* sampleDataBuffer,
- * \/* destPos= *\/ 0,
- * \/* size= *\/ offset);
+ * /* destPos= */ 0,
+ * /* size= */ offset);
+ * bytesWrittenCount = bytesWrittenCount - offset;
* publishSample(sampleData, timeUs, flags);
* }
+ *
+ * private void ensureSpaceInBuffer(int numberOfBytesToRead) {
+ * int requiredLength = bytesWrittenCount + numberOfBytesToRead;
+ * if (requiredLength > sampleDataBuffer.length) {
+ * sampleDataBuffer = Arrays.copyOf(sampleDataBuffer, requiredLength);
+ * }
+ * }
+ *
* }
*
* </pre>
@@ -342,11 +365,16 @@
/**
* Called to write sample data to the output.
*
- * <p>Implementers must attempt to consume the entirety of the input, but should surface any
- * thrown {@link IOException} caused by reading from {@code input}.
+ * <p>If the invocation of this method returns before the entire {@code inputReader} {@link
+ * InputReader#getLength() length} is consumed, the method will be called again for the
+ * implementer to read the remaining data. Implementers should surface any thrown {@link
+ * IOException} caused by reading from {@code input}.
*
* @param trackIndex The index of the track to which the sample data corresponds.
* @param inputReader The {@link InputReader} from which to read the data.
+ * @throws IOException If an exception occurs while reading from {@code inputReader}.
+ * @throws InterruptedException If an interruption occurs while reading from {@code
+ * inputReader}.
*/
void onSampleData(int trackIndex, @NonNull InputReader inputReader)
throws IOException, InterruptedException;
@@ -361,8 +389,9 @@
* @param flags Flags associated with the sample. See {@link MediaCodec
* MediaCodec.BUFFER_FLAG_*}.
* @param size The size of the sample data, in bytes.
- * @param offset The number of bytes that have been passed to {@link #onSampleData} since
- * the last byte belonging to the sample whose metadata is being passed.
+ * @param offset The number of bytes that have been consumed by {@code onSampleData(int,
+ * MediaParser.InputReader)} for the specified track, since the last byte belonging to
+ * the sample whose metadata is being passed.
* @param cryptoData Encryption data required to decrypt the sample. May be null for
* unencrypted samples.
*/
@@ -684,7 +713,9 @@
* container data.
* @return Whether there is any data left to extract. Returns false if the end of input has been
* reached.
- * @throws UnrecognizedInputFormatException
+ * @throws IOException If an error occurs while reading from the {@link SeekableInputReader}.
+ * @throws UnrecognizedInputFormatException If the format cannot be recognized by any of the
+ * underlying parser implementations.
*/
public boolean advance(@NonNull SeekableInputReader seekableInputReader)
throws IOException, InterruptedException {
@@ -700,30 +731,34 @@
mDataSource.mInputReader = seekableInputReader;
// TODO: Apply parameters when creating extractor instances.
- if (mExtractorName != null) {
- mExtractor = EXTRACTOR_FACTORIES_BY_NAME.get(mExtractorName).createInstance();
- } else if (mExtractor == null) {
- for (String parserName : mParserNamesPool) {
- Extractor extractor = EXTRACTOR_FACTORIES_BY_NAME.get(parserName).createInstance();
- try {
- if (extractor.sniff(mExtractorInput)) {
- mExtractorName = parserName;
- mExtractor = extractor;
- mExtractor.init(new ExtractorOutputAdapter());
- break;
+ if (mExtractor == null) {
+ if (mExtractorName != null) {
+ mExtractor = EXTRACTOR_FACTORIES_BY_NAME.get(mExtractorName).createInstance();
+ mExtractor.init(new ExtractorOutputAdapter());
+ } else {
+ for (String parserName : mParserNamesPool) {
+ Extractor extractor =
+ EXTRACTOR_FACTORIES_BY_NAME.get(parserName).createInstance();
+ try {
+ if (extractor.sniff(mExtractorInput)) {
+ mExtractorName = parserName;
+ mExtractor = extractor;
+ mExtractor.init(new ExtractorOutputAdapter());
+ break;
+ }
+ } catch (EOFException e) {
+ // Do nothing.
+ } catch (IOException | InterruptedException e) {
+ throw new IllegalStateException(e);
+ } finally {
+ mExtractorInput.resetPeekPosition();
}
- } catch (EOFException e) {
- // Do nothing.
- } catch (IOException | InterruptedException e) {
- throw new IllegalStateException(e);
- } finally {
- mExtractorInput.resetPeekPosition();
}
+ if (mExtractor == null) {
+ throw UnrecognizedInputFormatException.createForExtractors(mParserNamesPool);
+ }
+ return true;
}
- if (mExtractor == null) {
- throw UnrecognizedInputFormatException.createForExtractors(mParserNamesPool);
- }
- return true;
}
if (isPendingSeek()) {
diff --git a/apex/permission/Android.bp b/apex/permission/Android.bp
index d746ea6..0171b0d 100644
--- a/apex/permission/Android.bp
+++ b/apex/permission/Android.bp
@@ -20,6 +20,7 @@
apex_defaults {
name: "com.android.permission-defaults",
+ updatable: true,
key: "com.android.permission.key",
certificate: ":com.android.permission.certificate",
java_libs: [
diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java
index 205ffc2..30a8b45 100644
--- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java
+++ b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java
@@ -246,7 +246,7 @@
permissionState.isGranted() && (permissionState.getFlags()
& PackageManager.FLAG_PERMISSION_ONE_TIME) == 0));
serializer.attribute(null, ATTRIBUTE_FLAGS, Integer.toHexString(
- permissionState.getFlags() & ~PackageManager.FLAG_PERMISSION_ONE_TIME));
+ permissionState.getFlags()));
serializer.endTag(null, TAG_PERMISSION);
}
}
diff --git a/apex/sdkextensions/Android.bp b/apex/sdkextensions/Android.bp
index 25765af..322d5e1 100644
--- a/apex/sdkextensions/Android.bp
+++ b/apex/sdkextensions/Android.bp
@@ -26,6 +26,7 @@
apex_defaults {
name: "com.android.sdkext-defaults",
+ updatable: true,
java_libs: [ "framework-sdkextensions" ],
prebuilts: [
"derive_sdk.rc",
diff --git a/apex/statsd/Android.bp b/apex/statsd/Android.bp
index 2f3e2ac..2df3eea 100644
--- a/apex/statsd/Android.bp
+++ b/apex/statsd/Android.bp
@@ -32,6 +32,7 @@
compile_multilib: "both",
prebuilts: ["com.android.os.statsd.init.rc"],
name: "com.android.os.statsd-defaults",
+ updatable: true,
key: "com.android.os.statsd.key",
certificate: ":com.android.os.statsd.certificate",
}
@@ -63,11 +64,7 @@
shared_libs: [
"libnativehelper", // Has stable abi - should not be copied into apex.
"liblog", // Has a stable abi - should not be copied into apex.
- ],
- static_libs: [
- //TODO: make shared - need libstatssocket to also live in the apex.
"libstatssocket",
- "libcutils", // TODO: remove - needed by libstatssocket
],
//TODO: is libc++_static correct?
stl: "libc++_static",
diff --git a/apex/statsd/aidl/Android.bp b/apex/statsd/aidl/Android.bp
index 487c8e1..404c632 100644
--- a/apex/statsd/aidl/Android.bp
+++ b/apex/statsd/aidl/Android.bp
@@ -31,7 +31,7 @@
],
backend: {
java: {
- enabled: false, // the platform uses statsd_java_aidl
+ enabled: false, // framework-statsd and service-statsd use framework-statsd-aidl-sources
},
cpp: {
enabled: false,
diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp
index 0f52f15..1bd770a 100644
--- a/apex/statsd/framework/Android.bp
+++ b/apex/statsd/framework/Android.bp
@@ -69,6 +69,8 @@
"android.util",
],
+ plugins: ["java_api_finder"],
+
hostdex: true, // for hiddenapi check
visibility: [
"//frameworks/base/apex/statsd:__subpackages__",
diff --git a/apex/statsd/service/Android.bp b/apex/statsd/service/Android.bp
index 0f325e3..ff56bb5 100644
--- a/apex/statsd/service/Android.bp
+++ b/apex/statsd/service/Android.bp
@@ -31,6 +31,8 @@
"android_module_lib_stubs_current",
],
+ plugins: ["java_api_finder"],
+
apex_available: [
"com.android.os.statsd",
"test_com.android.os.statsd",
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
index cb167c3..dc61f2ae 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
@@ -21,11 +21,13 @@
import android.app.AlarmManager.OnAlarmListener;
import android.app.StatsManager;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -85,12 +87,6 @@
public static final int DEATH_THRESHOLD = 10;
- // TODO(b/149090705): Implement an alternative to sending broadcast with @hide flag
- // FLAG_RECEIVER_INCLUDE_BACKGROUND. Instead of using the flag, find the
- // list of registered broadcast receivers and send them directed broadcasts
- // to wake them up. See b/147374337.
- private static final int FLAG_RECEIVER_INCLUDE_BACKGROUND = 0x01000000;
-
static final class CompanionHandler extends Handler {
CompanionHandler(Looper looper) {
super(looper);
@@ -498,9 +494,25 @@
Log.d(TAG, "learned that statsdReady");
}
sayHiToStatsd(); // tell statsd that we're ready too and link to it
- mContext.sendBroadcastAsUser(new Intent(StatsManager.ACTION_STATSD_STARTED)
- .addFlags(FLAG_RECEIVER_INCLUDE_BACKGROUND),
- UserHandle.SYSTEM, android.Manifest.permission.DUMP);
+
+ final Intent intent = new Intent(StatsManager.ACTION_STATSD_STARTED);
+ // Retrieve list of broadcast receivers for this broadcast & send them directed broadcasts
+ // to wake them up (if they're in background).
+ List<ResolveInfo> resolveInfos =
+ mContext.getPackageManager().queryBroadcastReceiversAsUser(
+ intent, 0, UserHandle.SYSTEM);
+ if (resolveInfos == null || resolveInfos.isEmpty()) {
+ return; // No need to send broadcast.
+ }
+
+ for (ResolveInfo resolveInfo : resolveInfos) {
+ Intent intentToSend = new Intent(intent);
+ intentToSend.setComponent(new ComponentName(
+ resolveInfo.activityInfo.applicationInfo.packageName,
+ resolveInfo.activityInfo.name));
+ mContext.sendBroadcastAsUser(intentToSend, UserHandle.SYSTEM,
+ android.Manifest.permission.DUMP);
+ }
}
@Override
diff --git a/apex/statsd/tests/libstatspull/Android.bp b/apex/statsd/tests/libstatspull/Android.bp
index e813964..2d64f19 100644
--- a/apex/statsd/tests/libstatspull/Android.bp
+++ b/apex/statsd/tests/libstatspull/Android.bp
@@ -48,9 +48,13 @@
"-Werror",
],
shared_libs: [
- "libbinder",
- "libutils",
- "libstatspull",
- "libstatssocket",
+ "libbinder_ndk",
+ "statsd-aidl-ndk_platform",
],
-}
\ No newline at end of file
+ static_libs: [
+ "libstatspull_private",
+ "libstatssocket_private",
+ "libutils",
+ "libcutils",
+ ],
+}
diff --git a/apex/statsd/tests/libstatspull/jni/stats_pull_helper.cpp b/apex/statsd/tests/libstatspull/jni/stats_pull_helper.cpp
index 22daa8e..eb97f65 100644
--- a/apex/statsd/tests/libstatspull/jni/stats_pull_helper.cpp
+++ b/apex/statsd/tests/libstatspull/jni/stats_pull_helper.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include <binder/ProcessState.h>
+#include <android/binder_process.h>
#include <jni.h>
#include <log/log.h>
#include <stats_event.h>
@@ -24,7 +24,6 @@
#include <thread>
using std::this_thread::sleep_for;
-using namespace android;
namespace {
static int32_t sAtomTag;
@@ -39,10 +38,8 @@
if (!initialized) {
initialized = true;
// Set up the binder
- sp<ProcessState> ps(ProcessState::self());
- ps->setThreadPoolMaxThreadCount(9);
- ps->startThreadPool();
- ps->giveThreadPoolName();
+ ABinderProcess_setThreadPoolMaxThreadCount(9);
+ ABinderProcess_startThreadPool();
}
}
diff --git a/api/current.txt b/api/current.txt
index a2f2c06..13af1d4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2992,9 +2992,7 @@
method public int getNonInteractiveUiTimeoutMillis();
method public android.content.pm.ResolveInfo getResolveInfo();
method public String getSettingsActivityName();
- method @Nullable public android.graphics.drawable.Drawable loadAnimatedImage(@NonNull android.content.Context);
method public String loadDescription(android.content.pm.PackageManager);
- method @Nullable public String loadHtmlDescription(@NonNull android.content.pm.PackageManager);
method public CharSequence loadSummary(android.content.pm.PackageManager);
method public void setInteractiveUiTimeoutMillis(@IntRange(from=0) int);
method public void setNonInteractiveUiTimeoutMillis(@IntRange(from=0) int);
@@ -10121,7 +10119,6 @@
method public abstract void sendOrderedBroadcast(@RequiresPermission android.content.Intent, @Nullable String);
method public abstract void sendOrderedBroadcast(@NonNull @RequiresPermission android.content.Intent, @Nullable String, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
method public void sendOrderedBroadcast(@NonNull android.content.Intent, @Nullable String, @Nullable String, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
- method public void sendOrderedBroadcast(@NonNull @RequiresPermission android.content.Intent, @Nullable String, @Nullable String, @Nullable android.os.Bundle, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
method @RequiresPermission("android.permission.INTERACT_ACROSS_USERS") public abstract void sendOrderedBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle, @Nullable String, android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
method @Deprecated @RequiresPermission(android.Manifest.permission.BROADCAST_STICKY) public abstract void sendStickyBroadcast(@RequiresPermission android.content.Intent);
method @Deprecated @RequiresPermission(allOf={"android.permission.INTERACT_ACROSS_USERS", android.Manifest.permission.BROADCAST_STICKY}) public abstract void sendStickyBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle);
@@ -10329,6 +10326,7 @@
method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle, String);
method public void sendOrderedBroadcast(android.content.Intent, String);
method public void sendOrderedBroadcast(android.content.Intent, String, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle);
+ method public void sendOrderedBroadcast(@NonNull @RequiresPermission android.content.Intent, int, @Nullable String, @Nullable String, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, @Nullable String, @Nullable android.os.Bundle, @Nullable android.os.Bundle);
method public void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, String, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle);
method @Deprecated public void sendStickyBroadcast(android.content.Intent);
method @Deprecated public void sendStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
@@ -27061,7 +27059,7 @@
method public void unregisterTransferCallback(@NonNull android.media.MediaRouter2.TransferCallback);
}
- public static class MediaRouter2.ControllerCallback {
+ public abstract static class MediaRouter2.ControllerCallback {
ctor public MediaRouter2.ControllerCallback();
method public void onControllerUpdated(@NonNull android.media.MediaRouter2.RoutingController);
}
@@ -27070,7 +27068,7 @@
method @Nullable public android.os.Bundle onGetControllerHints(@NonNull android.media.MediaRoute2Info);
}
- public static class MediaRouter2.RouteCallback {
+ public abstract static class MediaRouter2.RouteCallback {
ctor public MediaRouter2.RouteCallback();
method public void onRoutesAdded(@NonNull java.util.List<android.media.MediaRoute2Info>);
method public void onRoutesChanged(@NonNull java.util.List<android.media.MediaRoute2Info>);
@@ -27093,7 +27091,7 @@
method public void setVolume(int);
}
- public static class MediaRouter2.TransferCallback {
+ public abstract static class MediaRouter2.TransferCallback {
ctor public MediaRouter2.TransferCallback();
method public void onTransferFailed(@NonNull android.media.MediaRoute2Info);
method public void onTransferred(@NonNull android.media.MediaRouter2.RoutingController, @Nullable android.media.MediaRouter2.RoutingController);
@@ -31106,6 +31104,7 @@
public class ScanResult implements android.os.Parcelable {
ctor public ScanResult(@NonNull android.net.wifi.ScanResult);
+ ctor public ScanResult();
method public int describeContents();
method @NonNull public java.util.List<android.net.wifi.ScanResult.InformationElement> getInformationElements();
method public int getWifiStandard();
@@ -31377,6 +31376,15 @@
field public static final int LINK_SPEED_UNKNOWN = -1; // 0xffffffff
}
+ public static final class WifiInfo.Builder {
+ ctor public WifiInfo.Builder();
+ method @NonNull public android.net.wifi.WifiInfo build();
+ method @NonNull public android.net.wifi.WifiInfo.Builder setBssid(@NonNull String);
+ method @NonNull public android.net.wifi.WifiInfo.Builder setNetworkId(int);
+ method @NonNull public android.net.wifi.WifiInfo.Builder setRssi(int);
+ method @NonNull public android.net.wifi.WifiInfo.Builder setSsid(@NonNull byte[]);
+ }
+
public class WifiManager {
method @Deprecated public int addNetwork(android.net.wifi.WifiConfiguration);
method @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public int addNetworkSuggestions(@NonNull java.util.List<android.net.wifi.WifiNetworkSuggestion>);
@@ -31560,6 +31568,20 @@
public final class WifiNetworkSuggestion implements android.os.Parcelable {
method public int describeContents();
+ method @Nullable public android.net.MacAddress getBssid();
+ method @Nullable public android.net.wifi.WifiEnterpriseConfig getEnterpriseConfig();
+ method @Nullable public String getPassphrase();
+ method @Nullable public android.net.wifi.hotspot2.PasspointConfiguration getPasspointConfig();
+ method @IntRange(from=0) public int getPriority();
+ method @Nullable public String getSsid();
+ method public boolean isAppInteractionRequired();
+ method public boolean isCredentialSharedWithUser();
+ method public boolean isEnhancedOpen();
+ method public boolean isHiddenSsid();
+ method public boolean isInitialAutojoinEnabled();
+ method public boolean isMetered();
+ method public boolean isUntrusted();
+ method public boolean isUserInteractionRequired();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiNetworkSuggestion> CREATOR;
}
@@ -43426,12 +43448,12 @@
public abstract class ControlsProviderService extends android.app.Service {
ctor public ControlsProviderService();
- method @Deprecated public void loadAvailableControls(@NonNull java.util.function.Consumer<java.util.List<android.service.controls.Control>>);
+ method @NonNull public abstract java.util.concurrent.Flow.Publisher<android.service.controls.Control> createPublisherFor(@NonNull java.util.List<java.lang.String>);
+ method @NonNull public abstract java.util.concurrent.Flow.Publisher<android.service.controls.Control> createPublisherForAllAvailable();
+ method @Nullable public java.util.concurrent.Flow.Publisher<android.service.controls.Control> createPublisherForSuggested();
method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
+ method public final boolean onUnbind(@NonNull android.content.Intent);
method public abstract void performControlAction(@NonNull String, @NonNull android.service.controls.actions.ControlAction, @NonNull java.util.function.Consumer<java.lang.Integer>);
- method @NonNull public abstract java.util.concurrent.Flow.Publisher<android.service.controls.Control> publisherFor(@NonNull java.util.List<java.lang.String>);
- method @Nullable public java.util.concurrent.Flow.Publisher<android.service.controls.Control> publisherForAllAvailable();
- method @Nullable public java.util.concurrent.Flow.Publisher<android.service.controls.Control> publisherForSuggested();
method public static void requestAddControl(@NonNull android.content.Context, @NonNull android.content.ComponentName, @NonNull android.service.controls.Control);
field public static final String SERVICE_CONTROLS = "android.service.controls.ControlsProviderService";
field @NonNull public static final String TAG = "ControlsProviderService";
@@ -48215,6 +48237,7 @@
method public boolean setLine1NumberForDisplay(String, String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setNetworkSelectionModeAutomatic();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setNetworkSelectionModeManual(String, boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setNetworkSelectionModeManual(@NonNull String, boolean, int);
method public boolean setOperatorBrandOverride(String);
method public boolean setPreferredNetworkTypeToGlobal();
method public void setPreferredOpportunisticDataSubscription(int, boolean, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>);
@@ -56956,6 +56979,7 @@
method @NonNull public android.view.inline.InlinePresentationSpec getPresentationSpec();
method @NonNull public String getSource();
method @NonNull public String getType();
+ method public boolean isPinned();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.InlineSuggestionInfo> CREATOR;
field public static final String SOURCE_AUTOFILL = "android:autofill";
diff --git a/api/system-current.txt b/api/system-current.txt
index 3b95f32..ae893b5 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4944,8 +4944,24 @@
package android.media.tv.tuner.dvr {
- public class Dvr implements java.lang.AutoCloseable {
- ctor protected Dvr(int);
+ public class DvrPlayback implements java.lang.AutoCloseable {
+ method public int attachFilter(@NonNull android.media.tv.tuner.filter.Filter);
+ method public void close();
+ method public int configure(@NonNull android.media.tv.tuner.dvr.DvrSettings);
+ method public int detachFilter(@NonNull android.media.tv.tuner.filter.Filter);
+ method public int flush();
+ method public long read(long);
+ method public long read(@NonNull byte[], long, long);
+ method public void setFileDescriptor(@NonNull android.os.ParcelFileDescriptor);
+ method public int start();
+ method public int stop();
+ field public static final int PLAYBACK_STATUS_ALMOST_EMPTY = 2; // 0x2
+ field public static final int PLAYBACK_STATUS_ALMOST_FULL = 4; // 0x4
+ field public static final int PLAYBACK_STATUS_EMPTY = 1; // 0x1
+ field public static final int PLAYBACK_STATUS_FULL = 8; // 0x8
+ }
+
+ public class DvrRecorder implements java.lang.AutoCloseable {
method public int attachFilter(@NonNull android.media.tv.tuner.filter.Filter);
method public void close();
method public int configure(@NonNull android.media.tv.tuner.dvr.DvrSettings);
@@ -4954,20 +4970,6 @@
method public void setFileDescriptor(@NonNull android.os.ParcelFileDescriptor);
method public int start();
method public int stop();
- field public static final int TYPE_PLAYBACK = 1; // 0x1
- field public static final int TYPE_RECORD = 0; // 0x0
- }
-
- public class DvrPlayback extends android.media.tv.tuner.dvr.Dvr {
- method public long read(long);
- method public long read(@NonNull byte[], long, long);
- field public static final int PLAYBACK_STATUS_ALMOST_EMPTY = 2; // 0x2
- field public static final int PLAYBACK_STATUS_ALMOST_FULL = 4; // 0x4
- field public static final int PLAYBACK_STATUS_EMPTY = 1; // 0x1
- field public static final int PLAYBACK_STATUS_FULL = 8; // 0x8
- }
-
- public class DvrRecorder extends android.media.tv.tuner.dvr.Dvr {
method public long write(long);
method public long write(@NonNull byte[], long, long);
}
@@ -7169,7 +7171,6 @@
}
public class ScanResult implements android.os.Parcelable {
- ctor public ScanResult();
field public static final int CIPHER_CCMP = 3; // 0x3
field public static final int CIPHER_GCMP_256 = 4; // 0x4
field public static final int CIPHER_NONE = 0; // 0x0
@@ -7314,8 +7315,8 @@
method @Deprecated public int getDisableReasonCounter(int);
method @Deprecated public long getDisableTime();
method @Deprecated public static int getMaxNetworkSelectionDisableReason();
- method @Deprecated @Nullable public static String getNetworkDisableReasonString(int);
method @Deprecated public int getNetworkSelectionDisableReason();
+ method @Deprecated @Nullable public static String getNetworkSelectionDisableReasonString(int);
method @Deprecated public int getNetworkSelectionStatus();
method @Deprecated @NonNull public String getNetworkStatusString();
method @Deprecated public boolean hasEverConnected();
@@ -7377,15 +7378,6 @@
field public static final int INVALID_RSSI = -127; // 0xffffff81
}
- public static final class WifiInfo.Builder {
- ctor public WifiInfo.Builder();
- method @NonNull public android.net.wifi.WifiInfo build();
- method @NonNull public android.net.wifi.WifiInfo.Builder setBssid(@NonNull String);
- method @NonNull public android.net.wifi.WifiInfo.Builder setNetworkId(int);
- method @NonNull public android.net.wifi.WifiInfo.Builder setRssi(int);
- method @NonNull public android.net.wifi.WifiInfo.Builder setSsid(@NonNull byte[]);
- }
-
public class WifiManager {
method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void addOnWifiUsabilityStatsListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void allowAutojoin(int, boolean);
@@ -7431,7 +7423,7 @@
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setAutoWakeupEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE) public void setDeviceMobilityState(int);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMacRandomizationSettingPasspointEnabled(@NonNull String, boolean);
- method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMeteredOverridePasspoint(@NonNull String, int);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setPasspointMeteredOverride(@NonNull String, int);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setScanAlwaysAvailable(boolean);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setScanThrottleEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public boolean setSoftApConfiguration(@NonNull android.net.wifi.SoftApConfiguration);
@@ -7528,7 +7520,7 @@
}
public static interface WifiManager.ScoreChangeCallback {
- method public void onScoreChange(int, @NonNull android.net.NetworkScore);
+ method public void onScoreChange(int, int);
method public void onTriggerUpdateOfWifiUsabilityStats(int);
}
@@ -7554,6 +7546,52 @@
method public void stop(int);
}
+ public final class WifiMigration {
+ method @Nullable public static android.net.wifi.WifiMigration.ConfigStoreMigrationData loadFromConfigStore();
+ method @NonNull public static android.net.wifi.WifiMigration.SettingsMigrationData loadFromSettings(@NonNull android.content.Context);
+ method public static void removeConfigStore();
+ }
+
+ public static final class WifiMigration.ConfigStoreMigrationData implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public java.util.List<android.net.wifi.WifiConfiguration> getUserSavedNetworkConfigurations();
+ method @Nullable public android.net.wifi.SoftApConfiguration getUserSoftApConfiguration();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiMigration.ConfigStoreMigrationData> CREATOR;
+ }
+
+ public static final class WifiMigration.ConfigStoreMigrationData.Builder {
+ ctor public WifiMigration.ConfigStoreMigrationData.Builder();
+ method @NonNull public android.net.wifi.WifiMigration.ConfigStoreMigrationData build();
+ method @NonNull public android.net.wifi.WifiMigration.ConfigStoreMigrationData.Builder setUserSavedNetworkConfigurations(@NonNull java.util.List<android.net.wifi.WifiConfiguration>);
+ method @NonNull public android.net.wifi.WifiMigration.ConfigStoreMigrationData.Builder setUserSoftApConfiguration(@NonNull android.net.wifi.SoftApConfiguration);
+ }
+
+ public static final class WifiMigration.SettingsMigrationData implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public String getP2pDeviceName();
+ method public boolean isP2pFactoryResetPending();
+ method public boolean isScanAlwaysAvailable();
+ method public boolean isScanThrottleEnabled();
+ method public boolean isSoftApTimeoutEnabled();
+ method public boolean isVerboseLoggingEnabled();
+ method public boolean isWakeUpEnabled();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiMigration.SettingsMigrationData> CREATOR;
+ }
+
+ public static final class WifiMigration.SettingsMigrationData.Builder {
+ ctor public WifiMigration.SettingsMigrationData.Builder();
+ method @NonNull public android.net.wifi.WifiMigration.SettingsMigrationData build();
+ method @NonNull public android.net.wifi.WifiMigration.SettingsMigrationData.Builder setP2pDeviceName(@Nullable String);
+ method @NonNull public android.net.wifi.WifiMigration.SettingsMigrationData.Builder setP2pFactoryResetPending(boolean);
+ method @NonNull public android.net.wifi.WifiMigration.SettingsMigrationData.Builder setScanAlwaysAvailable(boolean);
+ method @NonNull public android.net.wifi.WifiMigration.SettingsMigrationData.Builder setScanThrottleEnabled(boolean);
+ method @NonNull public android.net.wifi.WifiMigration.SettingsMigrationData.Builder setSoftApTimeoutEnabled(boolean);
+ method @NonNull public android.net.wifi.WifiMigration.SettingsMigrationData.Builder setVerboseLoggingEnabled(boolean);
+ method @NonNull public android.net.wifi.WifiMigration.SettingsMigrationData.Builder setWakeUpEnabled(boolean);
+ }
+
public class WifiNetworkConnectionStatistics implements android.os.Parcelable {
ctor public WifiNetworkConnectionStatistics(int, int);
ctor public WifiNetworkConnectionStatistics();
@@ -7570,7 +7608,6 @@
}
public final class WifiNetworkSuggestion implements android.os.Parcelable {
- method @Nullable public android.net.wifi.hotspot2.PasspointConfiguration getPasspointConfiguration();
method @NonNull public android.net.wifi.WifiConfiguration getWifiConfiguration();
}
@@ -7578,51 +7615,6 @@
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_CARRIER_PROVISIONING) public android.net.wifi.WifiNetworkSuggestion.Builder setCarrierId(int);
}
- public final class WifiOemMigrationHook {
- method @Nullable public static android.net.wifi.WifiOemMigrationHook.ConfigStoreMigrationData loadFromConfigStore();
- method @NonNull public static android.net.wifi.WifiOemMigrationHook.SettingsMigrationData loadFromSettings(@NonNull android.content.Context);
- }
-
- public static final class WifiOemMigrationHook.ConfigStoreMigrationData implements android.os.Parcelable {
- method public int describeContents();
- method @Nullable public java.util.List<android.net.wifi.WifiConfiguration> getUserSavedNetworkConfigurations();
- method @Nullable public android.net.wifi.SoftApConfiguration getUserSoftApConfiguration();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiOemMigrationHook.ConfigStoreMigrationData> CREATOR;
- }
-
- public static final class WifiOemMigrationHook.ConfigStoreMigrationData.Builder {
- ctor public WifiOemMigrationHook.ConfigStoreMigrationData.Builder();
- method @NonNull public android.net.wifi.WifiOemMigrationHook.ConfigStoreMigrationData build();
- method @NonNull public android.net.wifi.WifiOemMigrationHook.ConfigStoreMigrationData.Builder setUserSavedNetworkConfigurations(@NonNull java.util.List<android.net.wifi.WifiConfiguration>);
- method @NonNull public android.net.wifi.WifiOemMigrationHook.ConfigStoreMigrationData.Builder setUserSoftApConfiguration(@NonNull android.net.wifi.SoftApConfiguration);
- }
-
- public static final class WifiOemMigrationHook.SettingsMigrationData implements android.os.Parcelable {
- method public int describeContents();
- method @Nullable public String getP2pDeviceName();
- method public boolean isP2pFactoryResetPending();
- method public boolean isScanAlwaysAvailable();
- method public boolean isScanThrottleEnabled();
- method public boolean isSoftApTimeoutEnabled();
- method public boolean isVerboseLoggingEnabled();
- method public boolean isWakeUpEnabled();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiOemMigrationHook.SettingsMigrationData> CREATOR;
- }
-
- public static final class WifiOemMigrationHook.SettingsMigrationData.Builder {
- ctor public WifiOemMigrationHook.SettingsMigrationData.Builder();
- method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData build();
- method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setP2pDeviceName(@Nullable String);
- method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setP2pFactoryResetPending(boolean);
- method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setScanAlwaysAvailable(boolean);
- method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setScanThrottleEnabled(boolean);
- method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setSoftApTimeoutEnabled(boolean);
- method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setVerboseLoggingEnabled(boolean);
- method @NonNull public android.net.wifi.WifiOemMigrationHook.SettingsMigrationData.Builder setWakeUpEnabled(boolean);
- }
-
public class WifiScanner {
method @Deprecated public void configureWifiChange(int, int, int, int, int, android.net.wifi.WifiScanner.BssidInfo[]);
method @Deprecated public void configureWifiChange(android.net.wifi.WifiScanner.WifiChangeSettings);
@@ -9939,6 +9931,7 @@
method @NonNull public android.service.autofill.augmented.FillResponse build();
method @NonNull public android.service.autofill.augmented.FillResponse.Builder setClientState(@Nullable android.os.Bundle);
method @NonNull public android.service.autofill.augmented.FillResponse.Builder setFillWindow(@Nullable android.service.autofill.augmented.FillWindow);
+ method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineActions(@Nullable java.util.List<android.service.autofill.InlinePresentation>);
method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineSuggestions(@Nullable java.util.List<android.service.autofill.Dataset>);
}
@@ -9966,28 +9959,6 @@
method @NonNull @WorkerThread public abstract java.util.List<android.content.ContentValues> onRestoreApns(int);
}
- public abstract class CarrierMessagingServiceWrapper {
- ctor public CarrierMessagingServiceWrapper();
- method public boolean bindToCarrierMessagingService(@NonNull android.content.Context, @NonNull String);
- method public void disposeConnection(@NonNull android.content.Context);
- method public void downloadMms(@NonNull android.net.Uri, int, @NonNull android.net.Uri, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper);
- method public void filterSms(@NonNull android.service.carrier.MessagePdu, @NonNull String, int, int, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper);
- method public abstract void onServiceReady();
- method public void sendDataSms(@NonNull byte[], int, @NonNull String, int, int, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper);
- method public void sendMms(@NonNull android.net.Uri, int, @NonNull android.net.Uri, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper);
- method public void sendMultipartTextSms(@NonNull java.util.List<java.lang.String>, int, @NonNull String, int, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper);
- method public void sendTextSms(@NonNull String, int, @NonNull String, int, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper);
- }
-
- public abstract static class CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper {
- ctor public CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper();
- method public void onDownloadMmsComplete(int);
- method public void onFilterComplete(int);
- method public void onSendMmsComplete(int, @Nullable byte[]);
- method public void onSendMultipartSmsComplete(int, @Nullable int[]);
- method public void onSendSmsComplete(int, int);
- }
-
}
package android.service.contentcapture {
@@ -11451,7 +11422,6 @@
}
public class ServiceState implements android.os.Parcelable {
- method @NonNull public android.telephony.ServiceState createLocationInfoSanitizedCopy(boolean);
method public void fillInNotifierBundle(@NonNull android.os.Bundle);
method public int getDataNetworkType();
method public int getDataRegistrationState();
@@ -11628,7 +11598,7 @@
public class SubscriptionManager {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean canDisablePhysicalSubscription();
- method public boolean canManageSubscription(@Nullable android.telephony.SubscriptionInfo, @Nullable String);
+ method public boolean canManageSubscription(@NonNull android.telephony.SubscriptionInfo, @NonNull String);
method @NonNull public int[] getActiveAndHiddenSubscriptionIdList();
method @NonNull public int[] getActiveSubscriptionIdList();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.SubscriptionInfo getActiveSubscriptionInfoForIcc(@NonNull String);
@@ -11645,7 +11615,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setIconTint(@ColorInt int, int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setPreferredDataSubscriptionId(int, boolean, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setSubscriptionEnabled(int, boolean);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUiccApplicationsEnabled(boolean, int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUiccApplicationsEnabled(int, boolean);
field @RequiresPermission(android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS) public static final String ACTION_SUBSCRIPTION_PLANS_CHANGED = "android.telephony.action.SUBSCRIPTION_PLANS_CHANGED";
field @NonNull public static final android.net.Uri ADVANCED_CALLING_ENABLED_CONTENT_URI;
field public static final int PROFILE_CLASS_DEFAULT = -1; // 0xffffffff
@@ -11795,7 +11765,6 @@
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setAllowedNetworkTypes(long);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setAlwaysAllowMmsData(boolean);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setAlwaysReportSignalStrength(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCallForwarding(@NonNull android.telephony.CallForwardingInfo);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCallWaitingStatus(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCarrierDataEnabled(boolean);
@@ -11808,9 +11777,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setIccLockEnabled(boolean, @NonNull String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setNetworkSelectionModeManual(@NonNull String, int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setPolicyDataEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setPreferredNetworkTypeBitmask(long);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setRadio(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setRadioEnabled(boolean);
@@ -11939,45 +11906,6 @@
field public static final int SRVCC_STATE_HANDOVER_STARTED = 0; // 0x0
}
- public class TelephonyRegistryManager {
- method public void addOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener, @NonNull java.util.concurrent.Executor);
- method public void addOnSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener, @NonNull java.util.concurrent.Executor);
- method public void listenForSubscriber(int, @NonNull String, @NonNull String, @NonNull android.telephony.PhoneStateListener, int, boolean);
- method public void notifyActiveDataSubIdChanged(int);
- method public void notifyBarringInfoChanged(int, int, @NonNull android.telephony.BarringInfo);
- method public void notifyCallForwardingChanged(int, boolean);
- method public void notifyCallQualityChanged(int, int, @NonNull android.telephony.CallQuality, int);
- method public void notifyCallStateChanged(int, int, int, @Nullable String);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyCallStateChangedForAllSubscriptions(int, @Nullable String);
- method public void notifyCarrierNetworkChange(boolean);
- method public void notifyCellInfoChanged(int, @NonNull java.util.List<android.telephony.CellInfo>);
- method public void notifyCellLocation(int, @NonNull android.telephony.CellIdentity);
- method public void notifyDataActivationStateChanged(int, int, int);
- method public void notifyDataActivityChanged(int, int);
- method public void notifyDataConnectionForSubscriber(int, int, int, @Nullable android.telephony.PreciseDataConnectionState);
- method public void notifyDisconnectCause(int, int, int, int);
- method public void notifyDisplayInfoChanged(int, int, @NonNull android.telephony.DisplayInfo);
- method public void notifyEmergencyNumberList(int, int);
- method public void notifyImsDisconnectCause(int, @NonNull android.telephony.ims.ImsReasonInfo);
- method public void notifyMessageWaitingChanged(int, int, boolean);
- method public void notifyOpportunisticSubscriptionInfoChanged();
- method public void notifyOutgoingEmergencyCall(int, int, @NonNull android.telephony.emergency.EmergencyNumber);
- method public void notifyOutgoingEmergencySms(int, int, @NonNull android.telephony.emergency.EmergencyNumber);
- method public void notifyPhoneCapabilityChanged(@NonNull android.telephony.PhoneCapability);
- method public void notifyPreciseCallState(int, int, int, int, int);
- method public void notifyPreciseDataConnectionFailed(int, int, int, @Nullable String, int);
- method public void notifyRadioPowerStateChanged(int, int, int);
- method public void notifyRegistrationFailed(int, int, @NonNull android.telephony.CellIdentity, @NonNull String, int, int, int);
- method public void notifyServiceStateChanged(int, int, @NonNull android.telephony.ServiceState);
- method public void notifySignalStrengthChanged(int, int, @NonNull android.telephony.SignalStrength);
- method public void notifySrvccStateChanged(int, int);
- method public void notifySubscriptionInfoChanged();
- method public void notifyUserMobileDataStateChanged(int, int, boolean);
- method public void notifyVoiceActivationStateChanged(int, int, int);
- method public void removeOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
- method public void removeOnSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
- }
-
public final class UiccAccessRule implements android.os.Parcelable {
ctor public UiccAccessRule(byte[], @Nullable String, long);
method public int describeContents();
diff --git a/api/test-current.txt b/api/test-current.txt
index 0f8694f..f25f108 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3270,6 +3270,7 @@
method @NonNull public android.service.autofill.augmented.FillResponse build();
method @NonNull public android.service.autofill.augmented.FillResponse.Builder setClientState(@Nullable android.os.Bundle);
method @NonNull public android.service.autofill.augmented.FillResponse.Builder setFillWindow(@Nullable android.service.autofill.augmented.FillWindow);
+ method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineActions(@Nullable java.util.List<android.service.autofill.InlinePresentation>);
method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineSuggestions(@Nullable java.util.List<android.service.autofill.Dataset>);
}
@@ -5211,7 +5212,7 @@
}
public final class InlineSuggestionInfo implements android.os.Parcelable {
- method @NonNull public static android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(@NonNull android.view.inline.InlinePresentationSpec, @NonNull String, @Nullable String[]);
+ method @NonNull public static android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(@NonNull android.view.inline.InlinePresentationSpec, @NonNull String, @Nullable String[], @NonNull String, boolean);
}
public final class InlineSuggestionsResponse implements android.os.Parcelable {
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index 66f5c39..645dc93 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -36,18 +36,7 @@
],
host_supported: true,
srcs: [
- "libidmap2/BinaryStreamVisitor.cpp",
- "libidmap2/CommandLineOptions.cpp",
- "libidmap2/FileUtils.cpp",
- "libidmap2/Idmap.cpp",
- "libidmap2/Policies.cpp",
- "libidmap2/PrettyPrintVisitor.cpp",
- "libidmap2/RawPrintVisitor.cpp",
- "libidmap2/ResourceMapping.cpp",
- "libidmap2/ResourceUtils.cpp",
- "libidmap2/Result.cpp",
- "libidmap2/XmlParser.cpp",
- "libidmap2/ZipFile.cpp",
+ "libidmap2/**/*.cpp",
],
export_include_dirs: ["include"],
target: {
@@ -61,6 +50,7 @@
"libcutils",
"libutils",
"libziparchive",
+ "libidmap2_policies",
],
},
host: {
@@ -73,6 +63,37 @@
"libcutils",
"libutils",
"libziparchive",
+ "libidmap2_policies",
+ ],
+ },
+ },
+}
+
+cc_library {
+ name: "libidmap2_policies",
+ defaults: [
+ "idmap2_defaults",
+ ],
+ host_supported: true,
+ export_include_dirs: ["libidmap2_policies/include"],
+ target: {
+ windows: {
+ enabled: true,
+ },
+ android: {
+ static: {
+ enabled: false,
+ },
+ shared_libs: [
+ "libandroidfw",
+ ],
+ },
+ host: {
+ shared: {
+ enabled: false,
+ },
+ static_libs: [
+ "libandroidfw",
],
},
},
@@ -118,6 +139,7 @@
"libutils",
"libz",
"libziparchive",
+ "libidmap2_policies",
],
},
host: {
@@ -129,6 +151,7 @@
"liblog",
"libutils",
"libziparchive",
+ "libidmap2_policies",
],
shared_libs: [
"libz",
@@ -162,6 +185,7 @@
"libidmap2",
"libutils",
"libziparchive",
+ "libidmap2_policies",
],
},
host: {
@@ -173,12 +197,14 @@
"liblog",
"libutils",
"libziparchive",
+ "libidmap2_policies",
],
shared_libs: [
"libz",
],
},
},
+
}
cc_binary {
@@ -199,6 +225,7 @@
"libidmap2",
"libutils",
"libziparchive",
+ "libidmap2_policies",
],
static_libs: [
"libidmap2daidl",
@@ -231,3 +258,16 @@
],
path: "idmap2d/aidl",
}
+
+aidl_interface {
+ name: "overlayable_policy_aidl",
+ srcs: [":overlayable_policy_aidl_files"],
+}
+
+filegroup {
+ name: "overlayable_policy_aidl_files",
+ srcs: [
+ "idmap2d/aidl/android/os/OverlayablePolicy.aidl",
+ ],
+ path: "idmap2d/aidl",
+}
diff --git a/cmds/idmap2/idmap2/Create.cpp b/cmds/idmap2/idmap2/Create.cpp
index 3ff6d35..9682b6e 100644
--- a/cmds/idmap2/idmap2/Create.cpp
+++ b/cmds/idmap2/idmap2/Create.cpp
@@ -20,15 +20,14 @@
#include <fstream>
#include <memory>
#include <ostream>
-#include <sstream>
-#include <string>
#include <vector>
+#include "androidfw/ResourceTypes.h"
#include "idmap2/BinaryStreamVisitor.h"
#include "idmap2/CommandLineOptions.h"
#include "idmap2/FileUtils.h"
#include "idmap2/Idmap.h"
-#include "idmap2/Policies.h"
+#include "idmap2/PolicyUtils.h"
#include "idmap2/SysTrace.h"
using android::ApkAssets;
@@ -36,14 +35,15 @@
using android::idmap2::CommandLineOptions;
using android::idmap2::Error;
using android::idmap2::Idmap;
-using android::idmap2::PoliciesToBitmask;
-using android::idmap2::PolicyBitmask;
-using android::idmap2::PolicyFlags;
using android::idmap2::Result;
using android::idmap2::Unit;
using android::idmap2::utils::kIdmapFilePermissionMask;
+using android::idmap2::utils::PoliciesToBitmaskResult;
using android::idmap2::utils::UidHasWriteAccessToPath;
+using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
Result<Unit> Create(const std::vector<std::string>& args) {
SYSTRACE << "Create " << args;
std::string target_apk_path;
@@ -78,7 +78,7 @@
}
PolicyBitmask fulfilled_policies = 0;
- auto conv_result = PoliciesToBitmask(policies);
+ auto conv_result = PoliciesToBitmaskResult(policies);
if (conv_result) {
fulfilled_policies |= *conv_result;
} else {
@@ -86,7 +86,7 @@
}
if (fulfilled_policies == 0) {
- fulfilled_policies |= PolicyFlags::POLICY_PUBLIC;
+ fulfilled_policies |= PolicyFlags::PUBLIC;
}
const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
diff --git a/cmds/idmap2/idmap2/CreateMultiple.cpp b/cmds/idmap2/idmap2/CreateMultiple.cpp
index 0b0541f..ba4ca62 100644
--- a/cmds/idmap2/idmap2/CreateMultiple.cpp
+++ b/cmds/idmap2/idmap2/CreateMultiple.cpp
@@ -20,8 +20,6 @@
#include <fstream>
#include <memory>
#include <ostream>
-#include <sstream>
-#include <string>
#include <vector>
#include "android-base/stringprintf.h"
@@ -30,7 +28,9 @@
#include "idmap2/FileUtils.h"
#include "idmap2/Idmap.h"
#include "idmap2/Policies.h"
+#include "idmap2/PolicyUtils.h"
#include "idmap2/SysTrace.h"
+#include "Commands.h"
using android::ApkAssets;
using android::base::StringPrintf;
@@ -38,13 +38,11 @@
using android::idmap2::CommandLineOptions;
using android::idmap2::Error;
using android::idmap2::Idmap;
-using android::idmap2::PoliciesToBitmask;
-using android::idmap2::PolicyBitmask;
-using android::idmap2::PolicyFlags;
using android::idmap2::Result;
using android::idmap2::Unit;
using android::idmap2::utils::kIdmapCacheDir;
using android::idmap2::utils::kIdmapFilePermissionMask;
+using android::idmap2::utils::PoliciesToBitmaskResult;
using android::idmap2::utils::UidHasWriteAccessToPath;
Result<Unit> CreateMultiple(const std::vector<std::string>& args) {
@@ -80,7 +78,7 @@
}
PolicyBitmask fulfilled_policies = 0;
- auto conv_result = PoliciesToBitmask(policies);
+ auto conv_result = PoliciesToBitmaskResult(policies);
if (conv_result) {
fulfilled_policies |= *conv_result;
} else {
@@ -88,7 +86,7 @@
}
if (fulfilled_policies == 0) {
- fulfilled_policies |= PolicyFlags::POLICY_PUBLIC;
+ fulfilled_policies |= PolicyFlags::PUBLIC;
}
const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
@@ -105,32 +103,34 @@
continue;
}
- const std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
- if (!overlay_apk) {
- LOG(WARNING) << "failed to load apk " << overlay_apk_path.c_str();
- continue;
- }
+ if (!Verify(std::vector<std::string>({"--idmap-path", idmap_path}))) {
+ const std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+ if (!overlay_apk) {
+ LOG(WARNING) << "failed to load apk " << overlay_apk_path.c_str();
+ continue;
+ }
- const auto idmap =
- Idmap::FromApkAssets(*target_apk, *overlay_apk, fulfilled_policies, !ignore_overlayable);
- if (!idmap) {
- LOG(WARNING) << "failed to create idmap";
- continue;
- }
+ const auto idmap =
+ Idmap::FromApkAssets(*target_apk, *overlay_apk, fulfilled_policies, !ignore_overlayable);
+ if (!idmap) {
+ LOG(WARNING) << "failed to create idmap";
+ continue;
+ }
- umask(kIdmapFilePermissionMask);
- std::ofstream fout(idmap_path);
- if (fout.fail()) {
- LOG(WARNING) << "failed to open idmap path " << idmap_path.c_str();
- continue;
- }
+ umask(kIdmapFilePermissionMask);
+ std::ofstream fout(idmap_path);
+ if (fout.fail()) {
+ LOG(WARNING) << "failed to open idmap path " << idmap_path.c_str();
+ continue;
+ }
- BinaryStreamVisitor visitor(fout);
- (*idmap)->accept(&visitor);
- fout.close();
- if (fout.fail()) {
- LOG(WARNING) << "failed to write to idmap path %s" << idmap_path.c_str();
- continue;
+ BinaryStreamVisitor visitor(fout);
+ (*idmap)->accept(&visitor);
+ fout.close();
+ if (fout.fail()) {
+ LOG(WARNING) << "failed to write to idmap path %s" << idmap_path.c_str();
+ continue;
+ }
}
idmap_paths.emplace_back(idmap_path);
diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp
index b4fdd0b..da04532 100644
--- a/cmds/idmap2/idmap2/Scan.cpp
+++ b/cmds/idmap2/idmap2/Scan.cpp
@@ -20,7 +20,6 @@
#include <memory>
#include <ostream>
#include <set>
-#include <sstream>
#include <string>
#include <utility>
#include <vector>
@@ -34,25 +33,24 @@
#include "idmap2/Result.h"
#include "idmap2/SysTrace.h"
#include "idmap2/XmlParser.h"
-#include "idmap2/ZipFile.h"
using android::idmap2::CommandLineOptions;
using android::idmap2::Error;
using android::idmap2::Idmap;
-using android::idmap2::kPolicyOdm;
-using android::idmap2::kPolicyOem;
-using android::idmap2::kPolicyProduct;
-using android::idmap2::kPolicyPublic;
-using android::idmap2::kPolicySystem;
-using android::idmap2::kPolicyVendor;
-using android::idmap2::PolicyBitmask;
-using android::idmap2::PolicyFlags;
using android::idmap2::Result;
using android::idmap2::Unit;
+using android::idmap2::policy::kPolicyOdm;
+using android::idmap2::policy::kPolicyOem;
+using android::idmap2::policy::kPolicyProduct;
+using android::idmap2::policy::kPolicyPublic;
+using android::idmap2::policy::kPolicySystem;
+using android::idmap2::policy::kPolicyVendor;
using android::idmap2::utils::ExtractOverlayManifestInfo;
using android::idmap2::utils::FindFiles;
using android::idmap2::utils::OverlayManifestInfo;
+using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
+
namespace {
struct InputOverlay {
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
index 4aabf83..f84e4b5 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -33,7 +33,6 @@
#include "idmap2/BinaryStreamVisitor.h"
#include "idmap2/FileUtils.h"
#include "idmap2/Idmap.h"
-#include "idmap2/Policies.h"
#include "idmap2/SysTrace.h"
#include "utils/String8.h"
@@ -42,11 +41,12 @@
using android::idmap2::BinaryStreamVisitor;
using android::idmap2::Idmap;
using android::idmap2::IdmapHeader;
-using android::idmap2::PolicyBitmask;
using android::idmap2::utils::kIdmapCacheDir;
using android::idmap2::utils::kIdmapFilePermissionMask;
using android::idmap2::utils::UidHasWriteAccessToPath;
+using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
+
namespace {
Status ok() {
diff --git a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
index cd474c0..f4cf651 100644
--- a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
+++ b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
@@ -20,14 +20,6 @@
* @hide
*/
interface IIdmap2 {
- const int POLICY_PUBLIC = 0x00000001;
- const int POLICY_SYSTEM_PARTITION = 0x00000002;
- const int POLICY_VENDOR_PARTITION = 0x00000004;
- const int POLICY_PRODUCT_PARTITION = 0x00000008;
- const int POLICY_SIGNATURE = 0x00000010;
- const int POLICY_ODM_PARTITION = 0x00000020;
- const int POLICY_OEM_PARTITION = 0x00000040;
-
@utf8InCpp String getIdmapPath(@utf8InCpp String overlayApkPath, int userId);
boolean removeIdmap(@utf8InCpp String overlayApkPath, int userId);
boolean verifyIdmap(@utf8InCpp String overlayApkPath, int fulfilledPolicies,
diff --git a/cmds/idmap2/idmap2d/aidl/android/os/OverlayablePolicy.aidl b/cmds/idmap2/idmap2d/aidl/android/os/OverlayablePolicy.aidl
new file mode 100644
index 0000000..02b27a8
--- /dev/null
+++ b/cmds/idmap2/idmap2d/aidl/android/os/OverlayablePolicy.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.os;
+
+/**
+ * @see ResourcesTypes.h ResTable_overlayable_policy_header::PolicyFlags
+ * @hide
+ */
+interface OverlayablePolicy {
+ const int PUBLIC = 0x00000001;
+ const int SYSTEM_PARTITION = 0x00000002;
+ const int VENDOR_PARTITION = 0x00000004;
+ const int PRODUCT_PARTITION = 0x00000008;
+ const int SIGNATURE = 0x00000010;
+ const int ODM_PARTITION = 0x00000020;
+ const int OEM_PARTITION = 0x00000040;
+ const int ACTOR_SIGNATURE = 0x00000080;
+}
diff --git a/cmds/idmap2/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h
index d4a0c322..2e4836e 100644
--- a/cmds/idmap2/include/idmap2/Idmap.h
+++ b/cmds/idmap2/include/idmap2/Idmap.h
@@ -73,7 +73,6 @@
#include "androidfw/ApkAssets.h"
#include "androidfw/ResourceTypes.h"
#include "androidfw/StringPiece.h"
-#include "idmap2/Policies.h"
#include "idmap2/ResourceMapping.h"
namespace android::idmap2 {
diff --git a/cmds/idmap2/include/idmap2/PolicyUtils.h b/cmds/idmap2/include/idmap2/PolicyUtils.h
new file mode 100644
index 0000000..b95b8b4
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/PolicyUtils.h
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+#ifndef IDMAP2_INCLUDE_IDMAP2_POLICYUTILS_H_
+#define IDMAP2_INCLUDE_IDMAP2_POLICYUTILS_H_
+
+#include <string>
+#include <vector>
+
+#include "androidfw/ResourceTypes.h"
+#include "idmap2/Policies.h"
+#include "idmap2/Result.h"
+
+using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
+
+namespace android::idmap2::utils {
+
+// Returns a Result object containing a policy flag bitmask built from a list of policy strings.
+// On error will contain a human readable message listing the invalid policies.
+Result<PolicyBitmask> PoliciesToBitmaskResult(const std::vector<std::string>& policies);
+
+// Converts a bitmask of policy flags into a list of their string representation as would be written
+// into XML
+std::vector<std::string> BitmaskToPolicies(const PolicyBitmask& bitmask);
+
+} // namespace android::idmap2::utils
+
+#endif // IDMAP2_INCLUDE_IDMAP2_POLICYUTILS_H_
diff --git a/cmds/idmap2/include/idmap2/ResourceMapping.h b/cmds/idmap2/include/idmap2/ResourceMapping.h
index 86dfab2..5869409 100644
--- a/cmds/idmap2/include/idmap2/ResourceMapping.h
+++ b/cmds/idmap2/include/idmap2/ResourceMapping.h
@@ -30,6 +30,8 @@
using android::idmap2::utils::OverlayManifestInfo;
+using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
+
namespace android::idmap2 {
struct TargetValue {
diff --git a/cmds/idmap2/libidmap2/Policies.cpp b/cmds/idmap2/libidmap2/Policies.cpp
deleted file mode 100644
index 495fe61..0000000
--- a/cmds/idmap2/libidmap2/Policies.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#include "idmap2/Policies.h"
-
-#include <iterator>
-#include <string>
-#include <unordered_map>
-#include <vector>
-
-#include "androidfw/ResourceTypes.h"
-#include "idmap2/Idmap.h"
-#include "idmap2/Result.h"
-
-namespace android::idmap2 {
-
-Result<PolicyBitmask> PoliciesToBitmask(const std::vector<std::string>& policies) {
- static const std::unordered_map<android::StringPiece, PolicyFlags> kStringToFlag = {
- {kPolicyOdm, PolicyFlags::POLICY_ODM_PARTITION},
- {kPolicyOem, PolicyFlags::POLICY_OEM_PARTITION},
- {kPolicyPublic, PolicyFlags::POLICY_PUBLIC},
- {kPolicyProduct, PolicyFlags::POLICY_PRODUCT_PARTITION},
- {kPolicySignature, PolicyFlags::POLICY_SIGNATURE},
- {kPolicySystem, PolicyFlags::POLICY_SYSTEM_PARTITION},
- {kPolicyVendor, PolicyFlags::POLICY_VENDOR_PARTITION},
- };
-
- PolicyBitmask bitmask = 0;
- for (const std::string& policy : policies) {
- const auto iter = kStringToFlag.find(policy);
- if (iter != kStringToFlag.end()) {
- bitmask |= iter->second;
- } else {
- return Error("unknown policy \"%s\"", policy.c_str());
- }
- }
-
- return Result<PolicyBitmask>(bitmask);
-}
-
-std::vector<std::string> BitmaskToPolicies(const PolicyBitmask& bitmask) {
- std::vector<std::string> policies;
-
- if ((bitmask & PolicyFlags::POLICY_ODM_PARTITION) != 0) {
- policies.emplace_back(kPolicyOdm);
- }
-
- if ((bitmask & PolicyFlags::POLICY_OEM_PARTITION) != 0) {
- policies.emplace_back(kPolicyOem);
- }
-
- if ((bitmask & PolicyFlags::POLICY_PUBLIC) != 0) {
- policies.emplace_back(kPolicyPublic);
- }
-
- if ((bitmask & PolicyFlags::POLICY_PRODUCT_PARTITION) != 0) {
- policies.emplace_back(kPolicyProduct);
- }
-
- if ((bitmask & PolicyFlags::POLICY_SIGNATURE) != 0) {
- policies.emplace_back(kPolicySignature);
- }
-
- if ((bitmask & PolicyFlags::POLICY_SYSTEM_PARTITION) != 0) {
- policies.emplace_back(kPolicySystem);
- }
-
- if ((bitmask & PolicyFlags::POLICY_VENDOR_PARTITION) != 0) {
- policies.emplace_back(kPolicyVendor);
- }
-
- return policies;
-}
-
-} // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/PolicyUtils.cpp b/cmds/idmap2/libidmap2/PolicyUtils.cpp
new file mode 100644
index 0000000..fc5182a
--- /dev/null
+++ b/cmds/idmap2/libidmap2/PolicyUtils.cpp
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+#include "include/idmap2/PolicyUtils.h"
+
+#include <sstream>
+
+#include "android-base/strings.h"
+#include "idmap2/Policies.h"
+
+using android::idmap2::policy::kPolicyStringToFlag;
+
+namespace android::idmap2::utils {
+
+Result<PolicyBitmask> PoliciesToBitmaskResult(const std::vector<std::string>& policies) {
+ std::vector<std::string> unknown_policies;
+ PolicyBitmask bitmask = 0;
+ for (const std::string& policy : policies) {
+ const auto result = std::find_if(kPolicyStringToFlag.begin(), kPolicyStringToFlag.end(),
+ [policy](const auto& it) { return policy == it.first; });
+ if (result != kPolicyStringToFlag.end()) {
+ bitmask |= result->second;
+ } else {
+ unknown_policies.emplace_back(policy.empty() ? "empty" : policy);
+ }
+ }
+
+ if (unknown_policies.empty()) {
+ return Result<PolicyBitmask>(bitmask);
+ }
+
+ auto prefix = unknown_policies.size() == 1 ? "policy" : "policies";
+ return Error("unknown %s: \"%s\"", prefix, android::base::Join(unknown_policies, ",").c_str());
+}
+
+std::vector<std::string> BitmaskToPolicies(const PolicyBitmask& bitmask) {
+ std::vector<std::string> policies;
+
+ for (const auto& policy : kPolicyStringToFlag) {
+ if ((bitmask & policy.second) != 0) {
+ policies.emplace_back(policy.first.to_string());
+ }
+ }
+
+ return policies;
+}
+
+} // namespace android::idmap2::utils
diff --git a/cmds/idmap2/libidmap2/ResourceMapping.cpp b/cmds/idmap2/libidmap2/ResourceMapping.cpp
index 43cfec3..f82c8f1 100644
--- a/cmds/idmap2/libidmap2/ResourceMapping.cpp
+++ b/cmds/idmap2/libidmap2/ResourceMapping.cpp
@@ -24,11 +24,16 @@
#include <vector>
#include "android-base/stringprintf.h"
+#include "androidfw/ResourceTypes.h"
+#include "idmap2/PolicyUtils.h"
#include "idmap2/ResourceUtils.h"
using android::base::StringPrintf;
+using android::idmap2::utils::BitmaskToPolicies;
using android::idmap2::utils::IsReference;
using android::idmap2::utils::ResToTypeEntryName;
+using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
namespace android::idmap2 {
@@ -55,9 +60,8 @@
const PolicyBitmask& fulfilled_policies,
const ResourceId& target_resource) {
static constexpr const PolicyBitmask sDefaultPolicies =
- PolicyFlags::POLICY_ODM_PARTITION | PolicyFlags::POLICY_OEM_PARTITION |
- PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION |
- PolicyFlags::POLICY_PRODUCT_PARTITION | PolicyFlags::POLICY_SIGNATURE;
+ PolicyFlags::ODM_PARTITION | PolicyFlags::OEM_PARTITION | PolicyFlags::SYSTEM_PARTITION |
+ PolicyFlags::VENDOR_PARTITION | PolicyFlags::PRODUCT_PARTITION | PolicyFlags::SIGNATURE;
// If the resource does not have an overlayable definition, allow the resource to be overlaid if
// the overlay is preinstalled or signed with the same signature as the target.
diff --git a/cmds/idmap2/include/idmap2/Policies.h b/cmds/idmap2/libidmap2_policies/include/idmap2/Policies.h
similarity index 60%
rename from cmds/idmap2/include/idmap2/Policies.h
rename to cmds/idmap2/libidmap2_policies/include/idmap2/Policies.h
index 87edd35..4973b76 100644
--- a/cmds/idmap2/include/idmap2/Policies.h
+++ b/cmds/idmap2/libidmap2_policies/include/idmap2/Policies.h
@@ -14,18 +14,22 @@
* limitations under the License.
*/
-#include <string>
-#include <vector>
-
-#include "Result.h"
-#include "androidfw/ResourceTypes.h"
-#include "androidfw/StringPiece.h"
-
#ifndef IDMAP2_INCLUDE_IDMAP2_POLICIES_H_
#define IDMAP2_INCLUDE_IDMAP2_POLICIES_H_
-namespace android::idmap2 {
+#include <array>
+#include <string>
+#include <vector>
+#include "androidfw/ResourceTypes.h"
+#include "androidfw/StringPiece.h"
+
+using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
+namespace android::idmap2::policy {
+
+constexpr const char* kPolicyActor = "actor";
constexpr const char* kPolicyOdm = "odm";
constexpr const char* kPolicyOem = "oem";
constexpr const char* kPolicyProduct = "product";
@@ -34,15 +38,16 @@
constexpr const char* kPolicySystem = "system";
constexpr const char* kPolicyVendor = "vendor";
-using PolicyFlags = ResTable_overlayable_policy_header::PolicyFlags;
-using PolicyBitmask = uint32_t;
-
-// Parses the string representations of policies into a bitmask.
-Result<PolicyBitmask> PoliciesToBitmask(const std::vector<std::string>& policies);
-
-// Retrieves the string representations of policies in the bitmask.
-std::vector<std::string> BitmaskToPolicies(const PolicyBitmask& bitmask);
-
-} // namespace android::idmap2
+inline static const std::array<std::pair<StringPiece, PolicyFlags>, 8> kPolicyStringToFlag = {
+ std::pair{kPolicyActor, PolicyFlags::ACTOR_SIGNATURE},
+ {kPolicyOdm, PolicyFlags::ODM_PARTITION},
+ {kPolicyOem, PolicyFlags::OEM_PARTITION},
+ {kPolicyProduct, PolicyFlags::PRODUCT_PARTITION},
+ {kPolicyPublic, PolicyFlags::PUBLIC},
+ {kPolicySignature, PolicyFlags::SIGNATURE},
+ {kPolicySystem, PolicyFlags::SYSTEM_PARTITION},
+ {kPolicyVendor, PolicyFlags::VENDOR_PARTITION},
+};
+} // namespace android::idmap2::policy
#endif // IDMAP2_INCLUDE_IDMAP2_POLICIES_H_
diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
index b535f30..d896cf9 100644
--- a/cmds/idmap2/tests/Idmap2BinaryTests.cpp
+++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
@@ -34,6 +34,7 @@
#include <string>
#include <vector>
+#include "R.h"
#include "TestHelpers.h"
#include "androidfw/PosixUtils.h"
#include "gmock/gmock.h"
@@ -127,10 +128,14 @@
// clang-format on
ASSERT_THAT(result, NotNull());
ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
- ASSERT_NE(result->stdout.find("0x7f010000 -> 0x7f010000 integer/int1"), std::string::npos);
- ASSERT_NE(result->stdout.find("0x7f02000c -> 0x7f020000 string/str1"), std::string::npos);
- ASSERT_NE(result->stdout.find("0x7f02000e -> 0x7f020001 string/str3"), std::string::npos);
- ASSERT_NE(result->stdout.find("0x7f02000f -> 0x7f020002 string/str4"), std::string::npos);
+ ASSERT_NE(result->stdout.find(R::target::integer::literal::int1 + " -> 0x7f010000 integer/int1"),
+ std::string::npos);
+ ASSERT_NE(result->stdout.find(R::target::string::literal::str1 + " -> 0x7f020000 string/str1"),
+ std::string::npos);
+ ASSERT_NE(result->stdout.find(R::target::string::literal::str3 + " -> 0x7f020001 string/str3"),
+ std::string::npos);
+ ASSERT_NE(result->stdout.find(R::target::string::literal::str4 + " -> 0x7f020002 string/str4"),
+ std::string::npos);
// clang-format off
result = ExecuteBinary({"idmap2",
@@ -297,7 +302,7 @@
"lookup",
"--idmap-path", GetIdmapPath(),
"--config", "",
- "--resid", "0x7f02000c"}); // string/str1
+ "--resid", R::target::string::literal::str1});
// clang-format on
ASSERT_THAT(result, NotNull());
ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index a2c1560..87da36c 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -22,6 +22,8 @@
#include <utility>
#include <vector>
+#include "R.h"
+#include "TestConstants.h"
#include "TestHelpers.h"
#include "android-base/macros.h"
#include "androidfw/ApkAssets.h"
@@ -36,6 +38,8 @@
using ::testing::IsNull;
using ::testing::NotNull;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace android::idmap2 {
#define ASSERT_TARGET_ENTRY(entry, target_resid, type, value) \
@@ -168,7 +172,7 @@
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
+ auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
auto& idmap = *idmap_result;
@@ -177,8 +181,8 @@
ASSERT_THAT(idmap->GetHeader(), NotNull());
ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x03U);
- ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x76a20829);
- ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0xc054fb26);
+ ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), android::idmap2::TestConstants::TARGET_CRC);
+ ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), android::idmap2::TestConstants::OVERLAY_CRC);
ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), target_apk_path);
ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path);
}
@@ -220,7 +224,7 @@
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
+ auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
auto& idmap = *idmap_result;
@@ -234,17 +238,21 @@
const auto& target_entries = data->GetTargetEntries();
ASSERT_EQ(target_entries.size(), 4U);
- ASSERT_TARGET_ENTRY(target_entries[0], 0x7f010000, Res_value::TYPE_DYNAMIC_REFERENCE, 0x7f010000);
- ASSERT_TARGET_ENTRY(target_entries[1], 0x7f02000c, Res_value::TYPE_DYNAMIC_REFERENCE, 0x7f020000);
- ASSERT_TARGET_ENTRY(target_entries[2], 0x7f02000e, Res_value::TYPE_DYNAMIC_REFERENCE, 0x7f020001);
- ASSERT_TARGET_ENTRY(target_entries[3], 0x7f02000f, Res_value::TYPE_DYNAMIC_REFERENCE, 0x7f020002);
+ ASSERT_TARGET_ENTRY(target_entries[0], R::target::integer::int1,
+ Res_value::TYPE_DYNAMIC_REFERENCE, R::overlay::integer::int1);
+ ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay::string::str1);
+ ASSERT_TARGET_ENTRY(target_entries[2], R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay::string::str3);
+ ASSERT_TARGET_ENTRY(target_entries[3], R::target::string::str4, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay::string::str4);
const auto& overlay_entries = data->GetOverlayEntries();
ASSERT_EQ(target_entries.size(), 4U);
- ASSERT_OVERLAY_ENTRY(overlay_entries[0], 0x7f010000, 0x7f010000);
- ASSERT_OVERLAY_ENTRY(overlay_entries[1], 0x7f020000, 0x7f02000c);
- ASSERT_OVERLAY_ENTRY(overlay_entries[2], 0x7f020001, 0x7f02000e);
- ASSERT_OVERLAY_ENTRY(overlay_entries[3], 0x7f020002, 0x7f02000f);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[0], R::overlay::integer::int1, R::target::integer::int1);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[1], R::overlay::string::str1, R::target::string::str1);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[2], R::overlay::string::str3, R::target::string::str3);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[3], R::overlay::string::str4, R::target::string::str4);
}
TEST(IdmapTests, CreateIdmapDataFromApkAssetsSharedLibOverlay) {
@@ -257,7 +265,7 @@
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
+ auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
auto& idmap = *idmap_result;
@@ -271,17 +279,25 @@
const auto& target_entries = data->GetTargetEntries();
ASSERT_EQ(target_entries.size(), 4U);
- ASSERT_TARGET_ENTRY(target_entries[0], 0x7f010000, Res_value::TYPE_DYNAMIC_REFERENCE, 0x00010000);
- ASSERT_TARGET_ENTRY(target_entries[1], 0x7f02000c, Res_value::TYPE_DYNAMIC_REFERENCE, 0x00020000);
- ASSERT_TARGET_ENTRY(target_entries[2], 0x7f02000e, Res_value::TYPE_DYNAMIC_REFERENCE, 0x00020001);
- ASSERT_TARGET_ENTRY(target_entries[3], 0x7f02000f, Res_value::TYPE_DYNAMIC_REFERENCE, 0x00020002);
+ ASSERT_TARGET_ENTRY(target_entries[0], R::target::integer::int1,
+ Res_value::TYPE_DYNAMIC_REFERENCE, R::overlay_shared::integer::int1);
+ ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay_shared::string::str1);
+ ASSERT_TARGET_ENTRY(target_entries[2], R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay_shared::string::str3);
+ ASSERT_TARGET_ENTRY(target_entries[3], R::target::string::str4, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay_shared::string::str4);
const auto& overlay_entries = data->GetOverlayEntries();
ASSERT_EQ(target_entries.size(), 4U);
- ASSERT_OVERLAY_ENTRY(overlay_entries[0], 0x00010000, 0x7f010000);
- ASSERT_OVERLAY_ENTRY(overlay_entries[1], 0x00020000, 0x7f02000c);
- ASSERT_OVERLAY_ENTRY(overlay_entries[2], 0x00020001, 0x7f02000e);
- ASSERT_OVERLAY_ENTRY(overlay_entries[3], 0x00020002, 0x7f02000f);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[0], R::overlay_shared::integer::int1,
+ R::target::integer::int1);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[1], R::overlay_shared::string::str1,
+ R::target::string::str1);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[2], R::overlay_shared::string::str3,
+ R::target::string::str3);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[3], R::overlay_shared::string::str4,
+ R::target::string::str4);
}
TEST(IdmapTests, CreateIdmapDataDoNotRewriteNonOverlayResourceId) {
@@ -290,7 +306,7 @@
info.target_name = "TestResources";
info.resource_mapping = 0x7f030001; // xml/overlays_different_packages
auto idmap_data = TestIdmapDataFromApkAssets("/target/target.apk", "/overlay/overlay.apk", info,
- PolicyFlags::POLICY_PUBLIC,
+ PolicyFlags::PUBLIC,
/* enforce_overlayable */ false);
ASSERT_TRUE(idmap_data) << idmap_data.GetErrorMessage();
@@ -298,14 +314,14 @@
const auto& target_entries = data->GetTargetEntries();
ASSERT_EQ(target_entries.size(), 2U);
- ASSERT_TARGET_ENTRY(target_entries[0], 0x7f02000c, Res_value::TYPE_REFERENCE,
- 0x0104000a); // string/str1 -> android:string/ok
- ASSERT_TARGET_ENTRY(target_entries[1], 0x7f02000e, Res_value::TYPE_DYNAMIC_REFERENCE,
- 0x7f020001); // string/str3 -> string/str4
+ ASSERT_TARGET_ENTRY(target_entries[0], R::target::string::str1, Res_value::TYPE_REFERENCE,
+ 0x0104000a); // -> android:string/ok
+ ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay::string::str3);
const auto& overlay_entries = data->GetOverlayEntries();
ASSERT_EQ(overlay_entries.size(), 1U);
- ASSERT_OVERLAY_ENTRY(overlay_entries[0], 0x7f020001, 0x7f02000e); // string/str3 <- string/str4
+ ASSERT_OVERLAY_ENTRY(overlay_entries[0], R::overlay::string::str3, R::target::string::str3);
}
TEST(IdmapTests, CreateIdmapDataInlineResources) {
@@ -314,7 +330,7 @@
info.target_name = "TestResources";
info.resource_mapping = 0x7f030002; // xml/overlays_inline
auto idmap_data = TestIdmapDataFromApkAssets("/target/target.apk", "/overlay/overlay.apk", info,
- PolicyFlags::POLICY_PUBLIC,
+ PolicyFlags::PUBLIC,
/* enforce_overlayable */ false);
ASSERT_TRUE(idmap_data) << idmap_data.GetErrorMessage();
@@ -323,10 +339,10 @@
constexpr size_t overlay_string_pool_size = 8U;
const auto& target_entries = data->GetTargetEntries();
ASSERT_EQ(target_entries.size(), 2U);
- ASSERT_TARGET_ENTRY(target_entries[0], 0x7f010000, Res_value::TYPE_INT_DEC,
- 73U); // integer/int1 -> 73
- ASSERT_TARGET_ENTRY(target_entries[1], 0x7f02000c, Res_value::TYPE_STRING,
- overlay_string_pool_size + 0U); // string/str1 -> "Hello World"
+ ASSERT_TARGET_ENTRY(target_entries[0], R::target::integer::int1, Res_value::TYPE_INT_DEC,
+ 73U); // -> 73
+ ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1, Res_value::TYPE_STRING,
+ overlay_string_pool_size + 0U); // -> "Hello World"
const auto& overlay_entries = data->GetOverlayEntries();
ASSERT_EQ(overlay_entries.size(), 0U);
@@ -346,7 +362,7 @@
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- const auto result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
+ const auto result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
ASSERT_FALSE(result);
}
@@ -362,7 +378,7 @@
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- auto result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
+ auto result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
ASSERT_TRUE(result);
const auto idmap = std::move(*result);
diff --git a/cmds/idmap2/tests/PoliciesTests.cpp b/cmds/idmap2/tests/PoliciesTests.cpp
index eca7404..1b27759 100644
--- a/cmds/idmap2/tests/PoliciesTests.cpp
+++ b/cmds/idmap2/tests/PoliciesTests.cpp
@@ -17,76 +17,96 @@
#include <string>
#include "TestHelpers.h"
+#include "androidfw/ResourceTypes.h"
#include "gtest/gtest.h"
-#include "idmap2/Policies.h"
+#include "idmap2/PolicyUtils.h"
-using android::idmap2::PolicyBitmask;
-using android::idmap2::PolicyFlags;
+using android::idmap2::utils::BitmaskToPolicies;
+using android::idmap2::utils::PoliciesToBitmaskResult;
+
+using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
namespace android::idmap2 {
-TEST(PoliciesTests, PoliciesToBitmasks) {
- const auto bitmask1 = PoliciesToBitmask({"system"});
+TEST(PoliciesTests, PoliciesToBitmaskResults) {
+ const auto bitmask1 = PoliciesToBitmaskResult({"system"});
ASSERT_TRUE(bitmask1);
- ASSERT_EQ(*bitmask1, PolicyFlags::POLICY_SYSTEM_PARTITION);
+ ASSERT_EQ(*bitmask1, PolicyFlags::SYSTEM_PARTITION);
- const auto bitmask2 = PoliciesToBitmask({"system", "vendor"});
+ const auto bitmask2 = PoliciesToBitmaskResult({"system", "vendor"});
ASSERT_TRUE(bitmask2);
- ASSERT_EQ(*bitmask2, PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION);
+ ASSERT_EQ(*bitmask2, PolicyFlags::SYSTEM_PARTITION | PolicyFlags::VENDOR_PARTITION);
- const auto bitmask3 = PoliciesToBitmask({"vendor", "system"});
+ const auto bitmask3 = PoliciesToBitmaskResult({"vendor", "system"});
ASSERT_TRUE(bitmask3);
- ASSERT_EQ(*bitmask3, PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION);
+ ASSERT_EQ(*bitmask3, PolicyFlags::SYSTEM_PARTITION | PolicyFlags::VENDOR_PARTITION);
- const auto bitmask4 = PoliciesToBitmask({"odm", "oem", "public", "product", "system", "vendor"});
+ const auto bitmask4 =
+ PoliciesToBitmaskResult({"odm", "oem", "public", "product", "system", "vendor"});
ASSERT_TRUE(bitmask4);
- ASSERT_EQ(*bitmask4, PolicyFlags::POLICY_ODM_PARTITION | PolicyFlags::POLICY_OEM_PARTITION |
- PolicyFlags::POLICY_PUBLIC | PolicyFlags::POLICY_PRODUCT_PARTITION |
- PolicyFlags::POLICY_SYSTEM_PARTITION |
- PolicyFlags::POLICY_VENDOR_PARTITION);
+ ASSERT_EQ(*bitmask4, PolicyFlags::ODM_PARTITION | PolicyFlags::OEM_PARTITION |
+ PolicyFlags::PUBLIC | PolicyFlags::PRODUCT_PARTITION |
+ PolicyFlags::SYSTEM_PARTITION | PolicyFlags::VENDOR_PARTITION);
- const auto bitmask5 = PoliciesToBitmask({"system", "system", "system"});
+ const auto bitmask5 = PoliciesToBitmaskResult({"system", "system", "system"});
ASSERT_TRUE(bitmask5);
- ASSERT_EQ(*bitmask5, PolicyFlags::POLICY_SYSTEM_PARTITION);
+ ASSERT_EQ(*bitmask5, PolicyFlags::SYSTEM_PARTITION);
- const auto bitmask6 = PoliciesToBitmask({""});
+ const auto bitmask6 = PoliciesToBitmaskResult({""});
ASSERT_FALSE(bitmask6);
- const auto bitmask7 = PoliciesToBitmask({"foo"});
+ const auto bitmask7 = PoliciesToBitmaskResult({"foo"});
ASSERT_FALSE(bitmask7);
- const auto bitmask8 = PoliciesToBitmask({"system", "foo"});
+ const auto bitmask8 = PoliciesToBitmaskResult({"system", "foo"});
ASSERT_FALSE(bitmask8);
- const auto bitmask9 = PoliciesToBitmask({"system", ""});
+ const auto bitmask9 = PoliciesToBitmaskResult({"system", ""});
ASSERT_FALSE(bitmask9);
- const auto bitmask10 = PoliciesToBitmask({"system "});
+ const auto bitmask10 = PoliciesToBitmaskResult({"system "});
ASSERT_FALSE(bitmask10);
+
+ const auto bitmask11 = PoliciesToBitmaskResult({"signature"});
+ ASSERT_TRUE(bitmask11);
+ ASSERT_EQ(*bitmask11, PolicyFlags::SIGNATURE);
+
+ const auto bitmask12 = PoliciesToBitmaskResult({"actor"});
+ ASSERT_TRUE(bitmask12);
+ ASSERT_EQ(*bitmask12, PolicyFlags::ACTOR_SIGNATURE);
}
TEST(PoliciesTests, BitmaskToPolicies) {
- const auto policies1 = BitmaskToPolicies(PolicyFlags::POLICY_PUBLIC);
+ const auto policies1 = BitmaskToPolicies(PolicyFlags::PUBLIC);
ASSERT_EQ(1, policies1.size());
ASSERT_EQ(policies1[0], "public");
- const auto policies2 = BitmaskToPolicies(PolicyFlags::POLICY_SYSTEM_PARTITION |
- PolicyFlags::POLICY_VENDOR_PARTITION);
+ const auto policies2 =
+ BitmaskToPolicies(PolicyFlags::SYSTEM_PARTITION | PolicyFlags::VENDOR_PARTITION);
ASSERT_EQ(2, policies2.size());
ASSERT_EQ(policies2[0], "system");
ASSERT_EQ(policies2[1], "vendor");
- const auto policies3 = BitmaskToPolicies(
- PolicyFlags::POLICY_ODM_PARTITION | PolicyFlags::POLICY_OEM_PARTITION |
- PolicyFlags::POLICY_PUBLIC | PolicyFlags::POLICY_PRODUCT_PARTITION |
- PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION);
+ const auto policies3 =
+ BitmaskToPolicies(PolicyFlags::ODM_PARTITION | PolicyFlags::OEM_PARTITION |
+ PolicyFlags::PUBLIC | PolicyFlags::PRODUCT_PARTITION |
+ PolicyFlags::SYSTEM_PARTITION | PolicyFlags::VENDOR_PARTITION);
ASSERT_EQ(2, policies2.size());
ASSERT_EQ(policies3[0], "odm");
ASSERT_EQ(policies3[1], "oem");
- ASSERT_EQ(policies3[2], "public");
- ASSERT_EQ(policies3[3], "product");
+ ASSERT_EQ(policies3[2], "product");
+ ASSERT_EQ(policies3[3], "public");
ASSERT_EQ(policies3[4], "system");
ASSERT_EQ(policies3[5], "vendor");
+
+ const auto policies4 = BitmaskToPolicies(PolicyFlags::SIGNATURE);
+ ASSERT_EQ(1, policies4.size());
+ ASSERT_EQ(policies4[0], "signature");
+
+ const auto policies5 = BitmaskToPolicies(PolicyFlags::ACTOR_SIGNATURE);
+ ASSERT_EQ(1, policies5.size());
+ ASSERT_EQ(policies5[0], "actor");
}
} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
index 1d34e42..9a10079 100644
--- a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
@@ -18,19 +18,22 @@
#include <sstream>
#include <string>
+#include "R.h"
#include "TestHelpers.h"
#include "androidfw/ApkAssets.h"
#include "androidfw/Idmap.h"
+#include "androidfw/ResourceTypes.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "idmap2/Idmap.h"
-#include "idmap2/Policies.h"
#include "idmap2/PrettyPrintVisitor.h"
using ::testing::NotNull;
using android::ApkAssets;
-using android::idmap2::PolicyBitmask;
+
+using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
namespace android::idmap2 {
@@ -43,7 +46,7 @@
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
+ const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
ASSERT_TRUE(idmap);
@@ -53,7 +56,8 @@
ASSERT_NE(stream.str().find("target apk path : "), std::string::npos);
ASSERT_NE(stream.str().find("overlay apk path : "), std::string::npos);
- ASSERT_NE(stream.str().find("0x7f010000 -> 0x7f010000 integer/int1\n"), std::string::npos);
+ ASSERT_NE(stream.str().find(R::target::integer::literal::int1 + " -> 0x7f010000 integer/int1\n"),
+ std::string::npos);
}
TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitorWithoutAccessToApks) {
diff --git a/cmds/idmap2/tests/R.h b/cmds/idmap2/tests/R.h
new file mode 100644
index 0000000..aed263a
--- /dev/null
+++ b/cmds/idmap2/tests/R.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef IDMAP2_TESTS_R_H
+#define IDMAP2_TESTS_R_H
+
+#include <idmap2/ResourceUtils.h>
+
+namespace android::idmap2 {
+
+static std::string hexify(ResourceId id) {
+ std::stringstream stream;
+ stream << std::hex << static_cast<uint32_t>(id);
+ return stream.str();
+}
+
+// clang-format off
+namespace R::target {
+ namespace integer {
+ constexpr ResourceId int1 = 0x7f010000;
+
+ namespace literal {
+ inline const std::string int1 = hexify(R::target::integer::int1);
+ }
+ }
+
+ namespace string {
+ constexpr ResourceId not_overlayable = 0x7f020003;
+ constexpr ResourceId other = 0x7f020004;
+ constexpr ResourceId policy_actor = 0x7f020005;
+ constexpr ResourceId policy_odm = 0x7f020006;
+ constexpr ResourceId policy_oem = 0x7f020007;
+ constexpr ResourceId policy_product = 0x7f020008;
+ constexpr ResourceId policy_public = 0x7f020009;
+ constexpr ResourceId policy_signature = 0x7f02000a;
+ constexpr ResourceId policy_system = 0x7f02000b;
+ constexpr ResourceId policy_system_vendor = 0x7f02000c;
+ constexpr ResourceId str1 = 0x7f02000d;
+ constexpr ResourceId str3 = 0x7f02000f;
+ constexpr ResourceId str4 = 0x7f020010;
+
+ namespace literal {
+ inline const std::string str1 = hexify(R::target::string::str1);
+ inline const std::string str3 = hexify(R::target::string::str3);
+ inline const std::string str4 = hexify(R::target::string::str4);
+ }
+ }
+}
+
+namespace R::overlay {
+ namespace integer {
+ constexpr ResourceId int1 = 0x7f010000;
+ }
+ namespace string {
+ constexpr ResourceId str1 = 0x7f020000;
+ constexpr ResourceId str3 = 0x7f020001;
+ constexpr ResourceId str4 = 0x7f020002;
+ }
+}
+
+namespace R::overlay_shared {
+ namespace integer {
+ constexpr ResourceId int1 = 0x00010000;
+ }
+ namespace string {
+ constexpr ResourceId str1 = 0x00020000;
+ constexpr ResourceId str3 = 0x00020001;
+ constexpr ResourceId str4 = 0x00020002;
+ }
+}
+
+namespace R::system_overlay::string {
+ constexpr ResourceId policy_public = 0x7f010000;
+ constexpr ResourceId policy_system = 0x7f010001;
+ constexpr ResourceId policy_system_vendor = 0x7f010002;
+}
+
+namespace R::system_overlay_invalid::string {
+ constexpr ResourceId not_overlayable = 0x7f010000;
+ constexpr ResourceId other = 0x7f010001;
+ constexpr ResourceId policy_actor = 0x7f010002;
+ constexpr ResourceId policy_odm = 0x7f010003;
+ constexpr ResourceId policy_oem = 0x7f010004;
+ constexpr ResourceId policy_product = 0x7f010005;
+ constexpr ResourceId policy_public = 0x7f010006;
+ constexpr ResourceId policy_signature = 0x7f010007;
+ constexpr ResourceId policy_system = 0x7f010008;
+ constexpr ResourceId policy_system_vendor = 0x7f010009;
+};
+// clang-format on
+
+} // namespace android::idmap2
+
+#endif // IDMAP2_TESTS_R_H
diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
index b22fdaf..5c5c81e 100644
--- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
@@ -20,14 +20,20 @@
#include <sstream>
#include <string>
+#include "TestConstants.h"
#include "TestHelpers.h"
+#include "android-base/stringprintf.h"
+#include "androidfw/ResourceTypes.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "idmap2/Idmap.h"
#include "idmap2/RawPrintVisitor.h"
+using android::base::StringPrintf;
using ::testing::NotNull;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace android::idmap2 {
#define ASSERT_CONTAINS_REGEX(pattern, str) \
@@ -48,7 +54,7 @@
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
+ const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
ASSERT_TRUE(idmap);
@@ -59,8 +65,12 @@
#define ADDRESS "[0-9a-f]{8}: "
ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000003 version\n", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS "76a20829 target crc\n", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS "c054fb26 overlay crc\n", stream.str());
+ ASSERT_CONTAINS_REGEX(
+ StringPrintf(ADDRESS "%s target crc\n", android::idmap2::TestConstants::TARGET_CRC_STRING),
+ stream.str());
+ ASSERT_CONTAINS_REGEX(
+ StringPrintf(ADDRESS "%s overlay crc\n", android::idmap2::TestConstants::OVERLAY_CRC_STRING),
+ stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS " 7f target package id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS " 7f overlay package id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000004 target entry count\n", stream.str());
diff --git a/cmds/idmap2/tests/ResourceMappingTests.cpp b/cmds/idmap2/tests/ResourceMappingTests.cpp
index 39c4937..de039f4 100644
--- a/cmds/idmap2/tests/ResourceMappingTests.cpp
+++ b/cmds/idmap2/tests/ResourceMappingTests.cpp
@@ -22,7 +22,9 @@
#include <utility>
#include <vector>
+#include "R.h"
#include "TestHelpers.h"
+#include "androidfw/ResourceTypes.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "idmap2/LogInfo.h"
@@ -31,6 +33,8 @@
using android::Res_value;
using android::idmap2::utils::ExtractOverlayManifestInfo;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace android::idmap2 {
#define ASSERT_RESULT(r) \
@@ -106,20 +110,20 @@
info.target_name = "TestResources";
info.resource_mapping = 0U; // no xml
auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", info,
- PolicyFlags::POLICY_PUBLIC,
+ PolicyFlags::PUBLIC,
/* enforce_overlayable */ false);
ASSERT_TRUE(resources) << resources.GetErrorMessage();
auto& res = *resources;
ASSERT_EQ(res.GetTargetToOverlayMap().size(), 4U);
- ASSERT_RESULT(MappingExists(res, 0x7f010000, Res_value::TYPE_REFERENCE, 0x7f010000,
- false /* rewrite */)); // integer/int1
- ASSERT_RESULT(MappingExists(res, 0x7f02000c, Res_value::TYPE_REFERENCE, 0x7f020000,
- false /* rewrite */)); // string/str1
- ASSERT_RESULT(MappingExists(res, 0x7f02000e, Res_value::TYPE_REFERENCE, 0x7f020001,
- false /* rewrite */)); // string/str3
- ASSERT_RESULT(MappingExists(res, 0x7f02000f, Res_value::TYPE_REFERENCE, 0x7f020002,
- false /* rewrite */)); // string/str4
+ ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_REFERENCE,
+ R::overlay::integer::int1, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_REFERENCE,
+ R::overlay::string::str1, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::str3, Res_value::TYPE_REFERENCE,
+ R::overlay::string::str3, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::str4, Res_value::TYPE_REFERENCE,
+ R::overlay::string::str4, false /* rewrite */));
}
TEST(ResourceMappingTests, ResourcesFromApkAssetsNonMatchingNames) {
@@ -128,18 +132,18 @@
info.target_name = "TestResources";
info.resource_mapping = 0x7f030003; // xml/overlays_swap
auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", info,
- PolicyFlags::POLICY_PUBLIC,
+ PolicyFlags::PUBLIC,
/* enforce_overlayable */ false);
ASSERT_TRUE(resources) << resources.GetErrorMessage();
auto& res = *resources;
ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
- ASSERT_RESULT(MappingExists(res, 0x7f02000c, Res_value::TYPE_DYNAMIC_REFERENCE, 0x7f020002,
- true /* rewrite */)); // string/str1 -> string/str4
- ASSERT_RESULT(MappingExists(res, 0x7f02000e, Res_value::TYPE_DYNAMIC_REFERENCE, 0x7f020000,
- true /* rewrite */)); // string/str3 -> string/str1
- ASSERT_RESULT(MappingExists(res, 0x7f02000f, Res_value::TYPE_DYNAMIC_REFERENCE, 0x7f020001,
- true /* rewrite */)); // string/str4 -> string/str3
+ ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay::string::str4, true /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay::string::str1, true /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::str4, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay::string::str3, true /* rewrite */));
}
TEST(ResourceMappingTests, DoNotRewriteNonOverlayResourceId) {
@@ -148,17 +152,17 @@
info.target_name = "TestResources";
info.resource_mapping = 0x7f030001; // xml/overlays_different_packages
auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", info,
- PolicyFlags::POLICY_PUBLIC,
+ PolicyFlags::PUBLIC,
/* enforce_overlayable */ false);
ASSERT_TRUE(resources) << resources.GetErrorMessage();
auto& res = *resources;
ASSERT_EQ(res.GetTargetToOverlayMap().size(), 2U);
ASSERT_EQ(res.GetOverlayToTargetMap().size(), 1U);
- ASSERT_RESULT(MappingExists(res, 0x7f02000c, Res_value::TYPE_REFERENCE, 0x0104000a,
- false /* rewrite */)); // string/str1 -> android:string/ok
- ASSERT_RESULT(MappingExists(res, 0x7f02000e, Res_value::TYPE_DYNAMIC_REFERENCE, 0x7f020001,
- true /* rewrite */)); // string/str3 -> string/str4
+ ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_REFERENCE, 0x0104000a,
+ false /* rewrite */)); // -> android:string/ok
+ ASSERT_RESULT(MappingExists(res, R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE,
+ 0x7f020001, true /* rewrite */));
}
TEST(ResourceMappingTests, InlineResources) {
@@ -167,7 +171,7 @@
info.target_name = "TestResources";
info.resource_mapping = 0x7f030002; // xml/overlays_inline
auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", info,
- PolicyFlags::POLICY_PUBLIC,
+ PolicyFlags::PUBLIC,
/* enforce_overlayable */ false);
constexpr size_t overlay_string_pool_size = 8U;
@@ -175,108 +179,120 @@
auto& res = *resources;
ASSERT_EQ(res.GetTargetToOverlayMap().size(), 2U);
ASSERT_EQ(res.GetOverlayToTargetMap().size(), 0U);
- ASSERT_RESULT(MappingExists(res, 0x7f02000c, Res_value::TYPE_STRING,
+ ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_STRING,
overlay_string_pool_size + 0U,
- false /* rewrite */)); // string/str1 -> "Hello World"
- ASSERT_RESULT(MappingExists(res, 0x7f010000, Res_value::TYPE_INT_DEC, 73U,
- false /* rewrite */)); // string/str1 -> "Hello World"
+ false /* rewrite */)); // -> "Hello World"
+ ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_INT_DEC, 73U,
+ false /* rewrite */)); // -> 73
}
TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublic) {
auto resources =
TestGetResourceMapping("/target/target.apk", "/system-overlay/system-overlay.apk",
- PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC,
+ PolicyFlags::SYSTEM_PARTITION | PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
ASSERT_TRUE(resources) << resources.GetErrorMessage();
auto& res = *resources;
ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
- ASSERT_RESULT(MappingExists(res, 0x7f020008, Res_value::TYPE_REFERENCE, 0x7f010000,
- false /* rewrite */)); // string/policy_public
- ASSERT_RESULT(MappingExists(res, 0x7f02000a, Res_value::TYPE_REFERENCE, 0x7f010001,
- false /* rewrite */)); // string/policy_system
- ASSERT_RESULT(MappingExists(res, 0x7f02000b, Res_value::TYPE_REFERENCE, 0x7f010002,
- false /* rewrite */)); // string/policy_system_vendor
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, Res_value::TYPE_REFERENCE,
+ R::system_overlay::string::policy_public, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, Res_value::TYPE_REFERENCE,
+ R::system_overlay::string::policy_system, false /* rewrite */));
+ ASSERT_RESULT(
+ MappingExists(res, R::target::string::policy_system_vendor, Res_value::TYPE_REFERENCE,
+ R::system_overlay::string::policy_system_vendor, false /* rewrite */));
}
// Resources that are not declared as overlayable and resources that a protected by policies the
// overlay does not fulfill must not map to overlay resources.
TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) {
- auto resources = TestGetResourceMapping(
- "/target/target.apk", "/system-overlay-invalid/system-overlay-invalid.apk",
- PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC,
- /* enforce_overlayable */ true);
+ auto resources = TestGetResourceMapping("/target/target.apk",
+ "/system-overlay-invalid/system-overlay-invalid.apk",
+ PolicyFlags::SYSTEM_PARTITION | PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ true);
ASSERT_TRUE(resources) << resources.GetErrorMessage();
auto& res = *resources;
ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
- ASSERT_RESULT(MappingExists(res, 0x7f020008, Res_value::TYPE_REFERENCE, 0x7f010005,
- false /* rewrite */)); // string/policy_public
- ASSERT_RESULT(MappingExists(res, 0x7f02000a, Res_value::TYPE_REFERENCE, 0x7f010007,
- false /* rewrite */)); // string/policy_system
- ASSERT_RESULT(MappingExists(res, 0x7f02000b, Res_value::TYPE_REFERENCE, 0x7f010008,
- false /* rewrite */)); // string/policy_system_vendor
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_public,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_system,
+ false /* rewrite */));
+ ASSERT_RESULT(
+ MappingExists(res, R::target::string::policy_system_vendor, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_system_vendor, false /* rewrite */));
}
// Resources that are not declared as overlayable and resources that a protected by policies the
// overlay does not fulfilled can map to overlay resources when overlayable enforcement is turned
// off.
TEST(ResourceMappingTests, ResourcesFromApkAssetsPolicySystemPublicInvalidIgnoreOverlayable) {
- auto resources = TestGetResourceMapping(
- "/target/target.apk", "/system-overlay-invalid/system-overlay-invalid.apk",
- PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC,
- /* enforce_overlayable */ false);
+ auto resources = TestGetResourceMapping("/target/target.apk",
+ "/system-overlay-invalid/system-overlay-invalid.apk",
+ PolicyFlags::SYSTEM_PARTITION | PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ false);
ASSERT_TRUE(resources) << resources.GetErrorMessage();
auto& res = *resources;
- ASSERT_EQ(res.GetTargetToOverlayMap().size(), 9U);
- ASSERT_RESULT(MappingExists(res, 0x7f020003, Res_value::TYPE_REFERENCE, 0x7f010000,
- false /* rewrite */)); // string/not_overlayable
- ASSERT_RESULT(MappingExists(res, 0x7f020004, Res_value::TYPE_REFERENCE, 0x7f010001,
- false /* rewrite */)); // string/other
- ASSERT_RESULT(MappingExists(res, 0x7f020005, Res_value::TYPE_REFERENCE, 0x7f010002,
- false /* rewrite */)); // string/policy_odm
- ASSERT_RESULT(MappingExists(res, 0x7f020006, Res_value::TYPE_REFERENCE, 0x7f010003,
- false /* rewrite */)); // string/policy_oem
- ASSERT_RESULT(MappingExists(res, 0x7f020007, Res_value::TYPE_REFERENCE, 0x7f010004,
- false /* rewrite */)); // string/policy_product
- ASSERT_RESULT(MappingExists(res, 0x7f020008, Res_value::TYPE_REFERENCE, 0x7f010005,
- false /* rewrite */)); // string/policy_public
- ASSERT_RESULT(MappingExists(res, 0x7f020009, Res_value::TYPE_REFERENCE, 0x7f010006,
- false /* rewrite */)); // string/policy_signature
- ASSERT_RESULT(MappingExists(res, 0x7f02000a, Res_value::TYPE_REFERENCE, 0x7f010007,
- false /* rewrite */)); // string/policy_system
- ASSERT_RESULT(MappingExists(res, 0x7f02000b, Res_value::TYPE_REFERENCE, 0x7f010008,
- false /* rewrite */)); // string/policy_system_vendor
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 10U);
+ ASSERT_RESULT(MappingExists(res, R::target::string::not_overlayable, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::not_overlayable,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::other, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::other, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_actor, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_actor,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_odm, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_odm, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_oem, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_oem, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_product, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_product,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_public,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_signature, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_signature,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_system,
+ false /* rewrite */));
+ ASSERT_RESULT(
+ MappingExists(res, R::target::string::policy_system_vendor, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_system_vendor, false /* rewrite */));
}
// Overlays that do not target an <overlayable> tag can overlay resources defined within any
// <overlayable> tag.
TEST(ResourceMappingTests, ResourcesFromApkAssetsNoDefinedOverlayableAndNoTargetName) {
auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay-no-name.apk",
- PolicyFlags::POLICY_PUBLIC,
+ PolicyFlags::PUBLIC,
/* enforce_overlayable */ false);
ASSERT_TRUE(resources) << resources.GetErrorMessage();
auto& res = *resources;
ASSERT_EQ(res.GetTargetToOverlayMap().size(), 4U);
- ASSERT_RESULT(MappingExists(res, 0x7f010000, Res_value::TYPE_REFERENCE, 0x7f010000,
- false /* rewrite */)); // integer/int1
- ASSERT_RESULT(MappingExists(res, 0x7f02000c, Res_value::TYPE_REFERENCE, 0x7f020000,
- false /* rewrite */)); // string/str1
- ASSERT_RESULT(MappingExists(res, 0x7f02000e, Res_value::TYPE_REFERENCE, 0x7f020001,
- false /* rewrite */)); // string/str3
- ASSERT_RESULT(MappingExists(res, 0x7f02000f, Res_value::TYPE_REFERENCE, 0x7f020002,
- false /* rewrite */)); // string/str4
+ ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_REFERENCE,
+ R::overlay::integer::int1, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_REFERENCE,
+ R::overlay::string::str1, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::str3, Res_value::TYPE_REFERENCE,
+ R::overlay::string::str3, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::str4, Res_value::TYPE_REFERENCE,
+ R::overlay::string::str4, false /* rewrite */));
}
// Overlays that are neither pre-installed nor signed with the same signature as the target cannot
// overlay packages that have not defined overlayable resources.
TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPoliciesPublicFail) {
- auto resources =
- TestGetResourceMapping("/target/target-no-overlayable.apk", "/overlay/overlay-no-name.apk",
- PolicyFlags::POLICY_PUBLIC,
- /* enforce_overlayable */ true);
+ auto resources = TestGetResourceMapping("/target/target-no-overlayable.apk",
+ "/overlay/overlay-no-name.apk", PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ true);
ASSERT_TRUE(resources) << resources.GetErrorMessage();
ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 0U);
@@ -293,33 +309,44 @@
ASSERT_TRUE(resources) << resources.GetErrorMessage();
auto& res = *resources;
- ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 9U);
- ASSERT_RESULT(MappingExists(res, 0x7f020003, Res_value::TYPE_REFERENCE, 0x7f010000,
- false /* rewrite */)); // string/not_overlayable
- ASSERT_RESULT(MappingExists(res, 0x7f020004, Res_value::TYPE_REFERENCE, 0x7f010001,
- false /* rewrite */)); // string/other
- ASSERT_RESULT(MappingExists(res, 0x7f020005, Res_value::TYPE_REFERENCE, 0x7f010002,
- false /* rewrite */)); // string/policy_odm
- ASSERT_RESULT(MappingExists(res, 0x7f020006, Res_value::TYPE_REFERENCE, 0x7f010003,
- false /* rewrite */)); // string/policy_oem
- ASSERT_RESULT(MappingExists(res, 0x7f020007, Res_value::TYPE_REFERENCE, 0x7f010004,
- false /* rewrite */)); // string/policy_product
- ASSERT_RESULT(MappingExists(res, 0x7f020008, Res_value::TYPE_REFERENCE, 0x7f010005,
- false /* rewrite */)); // string/policy_public
- ASSERT_RESULT(MappingExists(res, 0x7f020009, Res_value::TYPE_REFERENCE, 0x7f010006,
- false /* rewrite */)); // string/policy_signature
- ASSERT_RESULT(MappingExists(res, 0x7f02000a, Res_value::TYPE_REFERENCE, 0x7f010007,
- false /* rewrite */)); // string/policy_system
- ASSERT_RESULT(MappingExists(res, 0x7f02000b, Res_value::TYPE_REFERENCE, 0x7f010008,
- false /* rewrite */)); // string/policy_system_vendor
+ ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 10U);
+ ASSERT_RESULT(MappingExists(res, R::target::string::not_overlayable, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::not_overlayable,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::other, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::other, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_actor, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_actor,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_odm, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_odm,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_oem, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_oem,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_product, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_product,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_public,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_signature, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_signature,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_system,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(
+ res, R::target::string::policy_system_vendor, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_system_vendor, false /* rewrite */));
};
- CheckEntries(PolicyFlags::POLICY_SIGNATURE);
- CheckEntries(PolicyFlags::POLICY_PRODUCT_PARTITION);
- CheckEntries(PolicyFlags::POLICY_SYSTEM_PARTITION);
- CheckEntries(PolicyFlags::POLICY_VENDOR_PARTITION);
- CheckEntries(PolicyFlags::POLICY_ODM_PARTITION);
- CheckEntries(PolicyFlags::POLICY_OEM_PARTITION);
+ CheckEntries(PolicyFlags::SIGNATURE);
+ CheckEntries(PolicyFlags::PRODUCT_PARTITION);
+ CheckEntries(PolicyFlags::SYSTEM_PARTITION);
+ CheckEntries(PolicyFlags::VENDOR_PARTITION);
+ CheckEntries(PolicyFlags::ODM_PARTITION);
+ CheckEntries(PolicyFlags::OEM_PARTITION);
}
} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/TestConstants.h b/cmds/idmap2/tests/TestConstants.h
new file mode 100644
index 0000000..6bc924e
--- /dev/null
+++ b/cmds/idmap2/tests/TestConstants.h
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#ifndef IDMAP2_TESTS_TESTCONSTANTS_H
+#define IDMAP2_TESTS_TESTCONSTANTS_H
+
+namespace android::idmap2::TestConstants {
+
+constexpr const auto TARGET_CRC = 0x41c60c8c;
+constexpr const auto TARGET_CRC_STRING = "41c60c8c";
+
+constexpr const auto OVERLAY_CRC = 0xc054fb26;
+constexpr const auto OVERLAY_CRC_STRING = "c054fb26";
+
+} // namespace android::idmap2::TestConstants
+
+#endif // IDMAP2_TESTS_TESTCONSTANTS_H
diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml b/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml
index 9ebfae4..7119d82 100644
--- a/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml
+++ b/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml
@@ -25,6 +25,7 @@
<string name="policy_signature">policy_signature</string>
<string name="policy_odm">policy_odm</string>
<string name="policy_oem">policy_oem</string>
+ <string name="policy_actor">policy_actor</string>
<!-- Requests to overlay a resource that is not declared as overlayable. -->
<string name="not_overlayable">not_overlayable</string>
diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk b/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk
index 1456e74..bd99098 100644
--- a/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk
+++ b/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/target/res/values/overlayable.xml b/cmds/idmap2/tests/data/target/res/values/overlayable.xml
index 8389f56..ad4cd48 100644
--- a/cmds/idmap2/tests/data/target/res/values/overlayable.xml
+++ b/cmds/idmap2/tests/data/target/res/values/overlayable.xml
@@ -41,6 +41,10 @@
<item type="string" name="policy_oem" />
</policy>
+ <policy type="actor">
+ <item type="string" name="policy_actor" />
+ </policy>
+
<!-- Resources publicly overlayable -->
<policy type="public">
<item type="string" name="policy_public" />
@@ -63,4 +67,4 @@
<item type="string" name="other" />
</policy>
</overlayable>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/cmds/idmap2/tests/data/target/res/values/values.xml b/cmds/idmap2/tests/data/target/res/values/values.xml
index a892c98..5230e25 100644
--- a/cmds/idmap2/tests/data/target/res/values/values.xml
+++ b/cmds/idmap2/tests/data/target/res/values/values.xml
@@ -36,6 +36,7 @@
<string name="policy_signature">policy_signature</string>
<string name="policy_system">policy_system</string>
<string name="policy_system_vendor">policy_system_vendor</string>
+ <string name="policy_actor">policy_actor</string>
<string name="other">other</string>
</resources>
diff --git a/cmds/idmap2/tests/data/target/target-no-overlayable.apk b/cmds/idmap2/tests/data/target/target-no-overlayable.apk
index 2eb7c47..58504a7 100644
--- a/cmds/idmap2/tests/data/target/target-no-overlayable.apk
+++ b/cmds/idmap2/tests/data/target/target-no-overlayable.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/target/target.apk b/cmds/idmap2/tests/data/target/target.apk
index 251cf46..c80e5eb 100644
--- a/cmds/idmap2/tests/data/target/target.apk
+++ b/cmds/idmap2/tests/data/target/target.apk
Binary files differ
diff --git a/cmds/incident_helper/src/ih_util.cpp b/cmds/incident_helper/src/ih_util.cpp
index 77a56e5..9439e1d 100644
--- a/cmds/incident_helper/src/ih_util.cpp
+++ b/cmds/incident_helper/src/ih_util.cpp
@@ -237,33 +237,38 @@
Reader::Reader(const int fd)
{
mFile = fdopen(fd, "r");
+ mBuffer = new char[1024];
mStatus = mFile == nullptr ? "Invalid fd " + std::to_string(fd) : "";
}
Reader::~Reader()
{
if (mFile != nullptr) fclose(mFile);
+ free(mBuffer);
}
bool Reader::readLine(std::string* line) {
if (mFile == nullptr) return false;
- char* buf = nullptr;
size_t len = 0;
- ssize_t read = getline(&buf, &len, mFile);
+ ssize_t read = getline(&mBuffer, &len, mFile);
if (read != -1) {
- std::string s(buf);
+ std::string s(mBuffer);
line->assign(trim(s, DEFAULT_NEWLINE));
- } else if (errno == EINVAL) {
- mStatus = "Bad Argument";
+ return true;
}
- free(buf);
- return read != -1;
+ if (!feof(mFile)) {
+ mStatus = "Error reading file. Ferror: " + std::to_string(ferror(mFile));
+ }
+ return false;
}
bool Reader::ok(std::string* error) {
+ if (mStatus.empty()) {
+ return true;
+ }
error->assign(mStatus);
- return mStatus.empty();
+ return false;
}
// ==============================================================================
diff --git a/cmds/incident_helper/src/ih_util.h b/cmds/incident_helper/src/ih_util.h
index 09dc8e6..5812c60 100644
--- a/cmds/incident_helper/src/ih_util.h
+++ b/cmds/incident_helper/src/ih_util.h
@@ -117,6 +117,7 @@
private:
FILE* mFile;
+ char* mBuffer;
std::string mStatus;
};
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 73a8f66..0c3a49a 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -302,11 +302,7 @@
static_libs: [
"libgmock",
"libplatformprotos",
-
- // TODO(b/149842105): Make libstatssocket shared and remove libcutils once statsd_test is
- // moved to the apex.
- "libstatssocket",
- "libcutils",
+ "libstatssocket_private",
],
proto: {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index f36b855..e58e7bc 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -380,8 +380,8 @@
BootTimeEventErrorCode boot_time_event_error_code_reported = 242 [(module) = "framework"];
UserspaceRebootReported userspace_reboot_reported = 243 [(module) = "framework"];
NotificationReported notification_reported = 244 [(module) = "framework"];
- NotificationPanelReported notification_panel_reported = 245;
- NotificationChannelModified notification_channel_modified = 246;
+ NotificationPanelReported notification_panel_reported = 245 [(module) = "sysui"];
+ NotificationChannelModified notification_channel_modified = 246 [(module) = "framework"];
IntegrityCheckResultReported integrity_check_result_reported = 247 [(module) = "framework"];
IntegrityRulesPushed integrity_rules_pushed = 248 [(module) = "framework"];
CellBroadcastMessageReported cb_message_reported =
@@ -395,6 +395,8 @@
SnapshotMergeReported snapshot_merge_reported = 255;
ForegroundServiceAppOpSessionEnded foreground_service_app_op_session_ended =
256 [(module) = "framework"];
+ DisplayJankReported display_jank_reported = 257;
+ AppStandbyBucketChanged app_standby_bucket_changed = 258 [(module) = "framework"];
SdkExtensionStatus sdk_extension_status = 354;
}
@@ -980,6 +982,17 @@
// The job id (as assigned by the app).
optional int32 job_id = 6;
+
+ // One flag for each of the API constraints defined by Jobscheduler. Does not include implcit
+ // constraints as they are always assumed to be set.
+ optional bool has_charging_constraint = 7;
+ optional bool has_battery_not_low_constraint = 8;
+ optional bool has_storage_not_low_constraint = 9;
+ optional bool has_timing_delay_constraint = 10;
+ optional bool has_deadline_constraint = 11;
+ optional bool has_idle_constraint = 12;
+ optional bool has_connectivity_constraint = 13;
+ optional bool has_content_trigger_constraint = 14;
}
/**
@@ -1215,18 +1228,8 @@
// Name of source package (for historical reasons, since BatteryStats tracked it).
optional string package_name = 3;
- // These enum values match the STANDBY_BUCKET_XXX constants defined in UsageStatsManager.java.
- enum Bucket {
- UNKNOWN = 0;
- EXEMPTED = 5;
- ACTIVE = 10;
- WORKING_SET = 20;
- FREQUENT = 30;
- RARE = 40;
- NEVER = 50;
- }
// The App Standby bucket of the app that scheduled the alarm at the time the alarm fired.
- optional Bucket app_standby_bucket = 4;
+ optional AppStandbyBucketChanged.Bucket app_standby_bucket = 4;
}
/**
@@ -3634,8 +3637,8 @@
// The notifying app's uid and package.
optional int32 uid = 2 [(is_uid) = true];
optional string package_name = 3;
- // App-assigned notification channel ID or channel-group ID
- optional string channel_id = 4;
+ // Hash of app-assigned notification channel ID or channel-group ID
+ optional int32 channel_id_hash = 4;
// Previous importance setting, if applicable
optional android.stats.sysui.NotificationImportance old_importance = 5;
// New importance setting
@@ -8228,6 +8231,22 @@
}
/**
+ * Janky event as reported by SurfaceFlinger.
+ * This event is intended to be consumed by a Perfetto subscriber for
+ * automated trace collection.
+ *
+ * Logged from:
+ * frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
+ */
+message DisplayJankReported {
+ // Informational field for how long the janky event lasted in milliseconds
+ optional int64 event_duration_millis = 1;
+ // Number of frame deadlines missed, where SurfaceFlinger failed to update
+ // the display on time.
+ optional int32 present_deadlines_missed = 2;
+}
+
+/**
* Information about camera facing and API level usage.
* Logged from:
* frameworks/base/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -8696,3 +8715,45 @@
// Total number of L5 sv status messages reports, where sv is used in fix since boot
optional int64 l5_sv_status_reports_used_in_fix = 14;
}
+
+/**
+ * Logs when an app is moved to a different standby bucket.
+ *
+ * Logged from:
+ * frameworks/base/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
+ */
+message AppStandbyBucketChanged {
+ optional string package_name = 1;
+
+ // Should be 0, 10, 11, 12, etc. where 0 is the owner. See UserHandle for more documentation.
+ optional int32 user_id = 2;
+
+ // These enum values match the constants defined in UsageStatsManager.java.
+ enum Bucket {
+ BUCKET_UNKNOWN = 0;
+ BUCKET_EXEMPTED = 5;
+ BUCKET_ACTIVE = 10;
+ BUCKET_WORKING_SET = 20;
+ BUCKET_FREQUENT = 30;
+ BUCKET_RARE = 40;
+ BUCKET_RESTRICTED = 45;
+ BUCKET_NEVER = 50;
+ }
+ optional Bucket bucket = 3;
+
+ enum MainReason {
+ MAIN_UNKNOWN = 0;
+ MAIN_DEFAULT = 0x0100;
+ MAIN_TIMEOUT = 0x0200;
+ MAIN_USAGE = 0x0300;
+ MAIN_FORCED_BY_USER = 0x0400;
+ MAIN_PREDICTED = 0x0500;
+ MAIN_FORCED_BY_SYSTEM = 0x0600;
+ }
+ optional MainReason main_reason = 4;
+
+ // A more detailed reason for the standby bucket change. The sub reason name is dependent on
+ // the main reason. Values are one of the REASON_SUB_XXX constants defined in
+ // UsageStatsManager.java.
+ optional int32 sub_reason = 5;
+}
diff --git a/cmds/statsd/src/external/StatsCallbackPuller.cpp b/cmds/statsd/src/external/StatsCallbackPuller.cpp
index 0edf40b..933f48d 100644
--- a/cmds/statsd/src/external/StatsCallbackPuller.cpp
+++ b/cmds/statsd/src/external/StatsCallbackPuller.cpp
@@ -66,10 +66,8 @@
{
lock_guard<mutex> lk(*cv_mutex);
for (const StatsEventParcel& parcel: output) {
- uint8_t* buf = reinterpret_cast<uint8_t*>(
- const_cast<int8_t*>(parcel.buffer.data()));
- shared_ptr<LogEvent> event = make_shared<LogEvent>(
- buf, parcel.buffer.size(), /*uid=*/-1, /*pid=*/-1);
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(/*uid=*/-1, /*pid=*/-1);
+ event->parseBuffer((uint8_t*)parcel.buffer.data(), parcel.buffer.size());
sharedData->push_back(event);
}
*pullSuccess = success;
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 3e46d13..974e203 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -65,18 +65,6 @@
#define ATTRIBUTION_CHAIN_TYPE 0x09
#define ERROR_TYPE 0x0F
-// Msg is expected to begin at the start of the serialized atom -- it should not
-// include the android_log_header_t or the StatsEventTag.
-LogEvent::LogEvent(uint8_t* msg, uint32_t len, int32_t uid, int32_t pid)
- : mBuf(msg),
- mRemainingLen(len),
- mLogdTimestampNs(time(nullptr)),
- mLogUid(uid),
- mLogPid(pid)
-{
- initNew();
-}
-
LogEvent::LogEvent(const LogEvent& event) {
mTagId = event.mTagId;
mLogUid = event.mLogUid;
@@ -86,6 +74,12 @@
mValues = event.mValues;
}
+LogEvent::LogEvent(int32_t uid, int32_t pid)
+ : mLogdTimestampNs(time(nullptr)),
+ mLogUid(uid),
+ mLogPid(pid) {
+}
+
LogEvent::LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedTimestampNs) {
mLogdTimestampNs = wallClockTimestampNs;
mElapsedTimestampNs = elapsedTimestampNs;
@@ -189,9 +183,6 @@
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(4)), Value(trainInfo.status)));
}
-LogEvent::LogEvent(int32_t tagId, int64_t timestampNs) : LogEvent(tagId, timestampNs, timestampNs) {
-}
-
LogEvent::LogEvent(int32_t tagId, int64_t timestampNs, int32_t uid) {
mLogdTimestampNs = timestampNs;
mTagId = tagId;
@@ -467,7 +458,10 @@
// This parsing logic is tied to the encoding scheme used in StatsEvent.java and
// stats_event.c
-void LogEvent::initNew() {
+bool LogEvent::parseBuffer(uint8_t* buf, size_t len) {
+ mBuf = buf;
+ mRemainingLen = (uint32_t)len;
+
int32_t pos[] = {1, 1, 1};
bool last[] = {false, false, false};
@@ -529,6 +523,7 @@
if (mRemainingLen != 0) mValid = false;
mBuf = nullptr;
+ return mValid;
}
uint8_t LogEvent::getTypeId(uint8_t typeInfo) {
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index e167e67..3940aa8 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -20,7 +20,6 @@
#include <android/util/ProtoOutputStream.h>
#include <private/android_logger.h>
-#include <stats_event.h>
#include <string>
#include <vector>
@@ -61,14 +60,32 @@
};
/**
- * Wrapper for the log_msg structure.
+ * This class decodes the structured, serialized encoding of an atom into a
+ * vector of FieldValues.
*/
class LogEvent {
public:
/**
- * Read a LogEvent from the socket
+ * \param uid user id of the logging caller
+ * \param pid process id of the logging caller
*/
- explicit LogEvent(uint8_t* msg, uint32_t len, int32_t uid, int32_t pid);
+ explicit LogEvent(int32_t uid, int32_t pid);
+
+ /**
+ * Parses the atomId, timestamp, and vector of values from a buffer
+ * containing the StatsEvent/AStatsEvent encoding of an atom.
+ *
+ * \param buf a buffer that begins at the start of the serialized atom (it
+ * should not include the android_log_header_t or the StatsEventTag)
+ * \param len size of the buffer
+ *
+ * \return success of the initialization
+ */
+ bool parseBuffer(uint8_t* buf, size_t len);
+
+ // TODO(b/149590301): delete unused functions below once LogEvent uses the
+ // new socket schema within test code. Really we would like the only entry
+ // points into LogEvent to be the above constructor and parseBuffer functions.
/**
* Constructs a LogEvent with synthetic data for testing. Must call init() before reading.
@@ -76,9 +93,6 @@
explicit LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedTimestampNs);
// For testing. The timestamp is used as both elapsed real time and logd timestamp.
- explicit LogEvent(int32_t tagId, int64_t timestampNs);
-
- // For testing. The timestamp is used as both elapsed real time and logd timestamp.
explicit LogEvent(int32_t tagId, int64_t timestampNs, int32_t uid);
/**
@@ -192,10 +206,6 @@
return &mValues;
}
- bool isValid() {
- return mValid;
- }
-
inline LogEvent makeCopy() {
return LogEvent(*this);
}
@@ -222,12 +232,6 @@
*/
LogEvent(const LogEvent&);
-
- /**
- * Parsing function for new encoding scheme.
- */
- void initNew();
-
void parseInt32(int32_t* pos, int32_t depth, bool* last);
void parseInt64(int32_t* pos, int32_t depth, bool* last);
void parseString(int32_t* pos, int32_t depth, bool* last);
@@ -238,13 +242,14 @@
void parseAttributionChain(int32_t* pos, int32_t depth, bool* last);
/**
- * mBuf is a pointer to the current location in the buffer being parsed.
- * Because the buffer lives on the StatsSocketListener stack, this pointer
- * is only valid during the LogEvent constructor. It will be set to null at
- * the end of initNew.
+ * The below three variables are only valid during the execution of
+ * parseBuffer. There are no guarantees about the state of these variables
+ * before/after.
+ *
+ * TODO (b/150312423): These shouldn't be member variables. We should pass
+ * them around as parameters.
*/
uint8_t* mBuf;
-
uint32_t mRemainingLen; // number of valid bytes left in the buffer being parsed
bool mValid = true; // stores whether the event we received from the socket is valid
diff --git a/cmds/statsd/src/socket/StatsSocketListener.cpp b/cmds/statsd/src/socket/StatsSocketListener.cpp
index 8f0f480..b877cc9 100755
--- a/cmds/statsd/src/socket/StatsSocketListener.cpp
+++ b/cmds/statsd/src/socket/StatsSocketListener.cpp
@@ -126,7 +126,10 @@
uint32_t pid = cred->pid;
int64_t oldestTimestamp;
- if (!mQueue->push(std::make_unique<LogEvent>(msg, len, uid, pid), &oldestTimestamp)) {
+ std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(uid, pid);
+ logEvent->parseBuffer(msg, len);
+
+ if (!mQueue->push(std::move(logEvent), &oldestTimestamp)) {
StatsdStats::getInstance().noteEventQueueOverflow(oldestTimestamp);
}
diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
index 7542faf..7458cbf 100644
--- a/cmds/statsd/tests/LogEvent_test.cpp
+++ b/cmds/statsd/tests/LogEvent_test.cpp
@@ -54,8 +54,9 @@
size_t size;
uint8_t* buf = AStatsEvent_getBuffer(event, &size);
- LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
- EXPECT_TRUE(logEvent.isValid());
+ LogEvent logEvent(/*uid=*/1000, /*pid=*/1001);
+ EXPECT_TRUE(logEvent.parseBuffer(buf, size));
+
EXPECT_EQ(100, logEvent.GetTagId());
EXPECT_EQ(1000, logEvent.GetUid());
EXPECT_EQ(1001, logEvent.GetPid());
@@ -102,8 +103,9 @@
size_t size;
uint8_t* buf = AStatsEvent_getBuffer(event, &size);
- LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
- EXPECT_TRUE(logEvent.isValid());
+ LogEvent logEvent(/*uid=*/ 1000, /*pid=*/ 1001);
+ EXPECT_TRUE(logEvent.parseBuffer(buf, size));
+
EXPECT_EQ(100, logEvent.GetTagId());
EXPECT_EQ(1000, logEvent.GetUid());
EXPECT_EQ(1001, logEvent.GetPid());
@@ -137,8 +139,9 @@
size_t size;
uint8_t* buf = AStatsEvent_getBuffer(event, &size);
- LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
- EXPECT_TRUE(logEvent.isValid());
+ LogEvent logEvent(/*uid=*/ 1000, /*pid=*/ 1001);
+ EXPECT_TRUE(logEvent.parseBuffer(buf, size));
+
EXPECT_EQ(100, logEvent.GetTagId());
EXPECT_EQ(1000, logEvent.GetUid());
EXPECT_EQ(1001, logEvent.GetPid());
@@ -165,8 +168,9 @@
size_t size;
uint8_t* buf = AStatsEvent_getBuffer(event, &size);
- LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
- EXPECT_TRUE(logEvent.isValid());
+ LogEvent logEvent(/*uid=*/ 1000, /*pid=*/ 1001);
+ EXPECT_TRUE(logEvent.parseBuffer(buf, size));
+
EXPECT_EQ(100, logEvent.GetTagId());
EXPECT_EQ(1000, logEvent.GetUid());
EXPECT_EQ(1001, logEvent.GetPid());
@@ -200,8 +204,9 @@
size_t size;
uint8_t* buf = AStatsEvent_getBuffer(event, &size);
- LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
- EXPECT_TRUE(logEvent.isValid());
+ LogEvent logEvent(/*uid=*/ 1000, /*pid=*/ 1001);
+ EXPECT_TRUE(logEvent.parseBuffer(buf, size));
+
EXPECT_EQ(100, logEvent.GetTagId());
EXPECT_EQ(1000, logEvent.GetUid());
EXPECT_EQ(1001, logEvent.GetPid());
diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
index 36094b2..4b78e30 100644
--- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
+++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
@@ -13,6 +13,7 @@
// limitations under the License.
#include "src/condition/SimpleConditionTracker.h"
+#include "stats_event.h"
#include "tests/statsd_test_util.h"
#include <gmock/gmock.h>
@@ -31,6 +32,8 @@
namespace os {
namespace statsd {
+namespace {
+
const ConfigKey kConfigKey(0, 12345);
const int ATTRIBUTION_NODE_FIELD_ID = 1;
@@ -57,24 +60,33 @@
return simplePredicate;
}
-void writeAttributionNodesToEvent(LogEvent* event, const std::vector<int> &uids) {
- std::vector<AttributionNodeInternal> nodes;
- for (size_t i = 0; i < uids.size(); ++i) {
- AttributionNodeInternal node;
- node.set_uid(uids[i]);
- nodes.push_back(node);
+void makeWakeLockEvent(LogEvent* logEvent, uint32_t atomId, uint64_t timestamp,
+ const vector<int>& uids, const string& wl, int acquire) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
+
+ vector<std::string> tags(uids.size()); // vector of empty strings
+ vector<const char*> cTags(uids.size());
+ for (int i = 0; i < cTags.size(); i++) {
+ cTags[i] = tags[i].c_str();
}
- event->write(nodes); // attribution chain.
+ AStatsEvent_writeAttributionChain(statsEvent, reinterpret_cast<const uint32_t*>(uids.data()),
+ cTags.data(), uids.size());
+
+ AStatsEvent_writeString(statsEvent, wl.c_str());
+ AStatsEvent_writeInt32(statsEvent, acquire);
+ AStatsEvent_build(statsEvent);
+
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+ logEvent->parseBuffer(buf, size);
+
+ AStatsEvent_release(statsEvent);
}
-// TODO(b/149590301): Update this helper to use new socket schema.
-//void makeWakeLockEvent(
-// LogEvent* event, const std::vector<int> &uids, const string& wl, int acquire) {
-// writeAttributionNodesToEvent(event, uids);
-// event->write(wl);
-// event->write(acquire);
-// event->init();
-//}
+} // anonymous namespace
+
std::map<int64_t, HashableDimensionKey> getWakeLockQueryKey(
const Position position,
@@ -265,138 +277,128 @@
EXPECT_TRUE(changedCache[0]);
}
-// TODO(b/149590301): Update these tests to use new socket schema.
-//TEST(SimpleConditionTrackerTest, TestSlicedCondition) {
-// std::vector<sp<ConditionTracker>> allConditions;
-// for (Position position :
-// { Position::FIRST, Position::LAST}) {
-//
-// SimplePredicate simplePredicate = getWakeLockHeldCondition(
-// true /*nesting*/, true /*default to false*/, true /*output slice by uid*/,
-// position);
-// string conditionName = "WL_HELD_BY_UID2";
-//
-// unordered_map<int64_t, int> trackerNameIndexMap;
-// trackerNameIndexMap[StringToId("WAKE_LOCK_ACQUIRE")] = 0;
-// trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1;
-// trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2;
-//
-// SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName),
-// 0 /*condition tracker index*/, simplePredicate,
-// trackerNameIndexMap);
-//
-// std::vector<int> uids = {111, 222, 333};
-//
-// LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
-// makeWakeLockEvent(&event, uids, "wl1", 1);
-//
-// // one matched start
-// vector<MatchingState> matcherState;
-// matcherState.push_back(MatchingState::kMatched);
-// matcherState.push_back(MatchingState::kNotMatched);
-// matcherState.push_back(MatchingState::kNotMatched);
-// vector<sp<ConditionTracker>> allPredicates;
-// vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
-// vector<bool> changedCache(1, false);
-//
-// conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
-// changedCache);
-//
-// if (position == Position::FIRST ||
-// position == Position::LAST) {
-// EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
-// } else {
-// EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
-// }
-// EXPECT_TRUE(changedCache[0]);
-// if (position == Position::FIRST ||
-// position == Position::LAST) {
-// EXPECT_EQ(conditionTracker.getChangedToTrueDimensions(allConditions)->size(), 1u);
-// EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
-// } else {
-// EXPECT_EQ(conditionTracker.getChangedToTrueDimensions(allConditions)->size(), uids.size());
-// }
-//
-// // Now test query
-// const auto queryKey = getWakeLockQueryKey(position, uids, conditionName);
-// conditionCache[0] = ConditionState::kNotEvaluated;
-//
-// conditionTracker.isConditionMet(queryKey, allPredicates,
-// false,
-// conditionCache);
-// EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
-//
-// // another wake lock acquired by this uid
-// LogEvent event2(1 /*tagId*/, 0 /*timestamp*/);
-// makeWakeLockEvent(&event2, uids, "wl2", 1);
-// matcherState.clear();
-// matcherState.push_back(MatchingState::kMatched);
-// matcherState.push_back(MatchingState::kNotMatched);
-// conditionCache[0] = ConditionState::kNotEvaluated;
-// changedCache[0] = false;
-// conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache,
-// changedCache);
-// EXPECT_FALSE(changedCache[0]);
-// if (position == Position::FIRST ||
-// position == Position::LAST) {
-// EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
-// } else {
-// EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
-// }
-// EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
-// EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
-//
-//
-// // wake lock 1 release
-// LogEvent event3(1 /*tagId*/, 0 /*timestamp*/);
-// makeWakeLockEvent(&event3, uids, "wl1", 0); // now release it.
-// matcherState.clear();
-// matcherState.push_back(MatchingState::kNotMatched);
-// matcherState.push_back(MatchingState::kMatched);
-// conditionCache[0] = ConditionState::kNotEvaluated;
-// changedCache[0] = false;
-// conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache,
-// changedCache);
-// // nothing changes, because wake lock 2 is still held for this uid
-// EXPECT_FALSE(changedCache[0]);
-// if (position == Position::FIRST ||
-// position == Position::LAST) {
-// EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
-// } else {
-// EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
-// }
-// EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
-// EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
-//
-// LogEvent event4(1 /*tagId*/, 0 /*timestamp*/);
-// makeWakeLockEvent(&event4, uids, "wl2", 0); // now release it.
-// matcherState.clear();
-// matcherState.push_back(MatchingState::kNotMatched);
-// matcherState.push_back(MatchingState::kMatched);
-// conditionCache[0] = ConditionState::kNotEvaluated;
-// changedCache[0] = false;
-// conditionTracker.evaluateCondition(event4, matcherState, allPredicates, conditionCache,
-// changedCache);
-// EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
-// EXPECT_TRUE(changedCache[0]);
-// if (position == Position::FIRST ||
-// position == Position::LAST) {
-// EXPECT_EQ(conditionTracker.getChangedToFalseDimensions(allConditions)->size(), 1u);
-// EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
-// } else {
-// EXPECT_EQ(conditionTracker.getChangedToFalseDimensions(allConditions)->size(), uids.size());
-// }
-//
-// // query again
-// conditionCache[0] = ConditionState::kNotEvaluated;
-// conditionTracker.isConditionMet(queryKey, allPredicates,
-// false,
-// conditionCache);
-// EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
-// }
-//
-//}
-//
+TEST(SimpleConditionTrackerTest, TestSlicedCondition) {
+ std::vector<sp<ConditionTracker>> allConditions;
+ for (Position position : {Position::FIRST, Position::LAST}) {
+ SimplePredicate simplePredicate = getWakeLockHeldCondition(
+ true /*nesting*/, true /*default to false*/, true /*output slice by uid*/,
+ position);
+ string conditionName = "WL_HELD_BY_UID2";
+
+ unordered_map<int64_t, int> trackerNameIndexMap;
+ trackerNameIndexMap[StringToId("WAKE_LOCK_ACQUIRE")] = 0;
+ trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1;
+ trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2;
+
+ SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName),
+ 0 /*condition tracker index*/, simplePredicate,
+ trackerNameIndexMap);
+
+ std::vector<int> uids = {111, 222, 333};
+
+ LogEvent event(/*uid=*/-1, /*pid=*/-1);
+ makeWakeLockEvent(&event, /*atomId=*/1, /*timestamp=*/0, uids, "wl1", /*acquire=*/1);
+
+ // one matched start
+ vector<MatchingState> matcherState;
+ matcherState.push_back(MatchingState::kMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ vector<sp<ConditionTracker>> allPredicates;
+ vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
+ vector<bool> changedCache(1, false);
+
+ conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
+ changedCache);
+
+ if (position == Position::FIRST || position == Position::LAST) {
+ EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+ } else {
+ EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
+ }
+ EXPECT_TRUE(changedCache[0]);
+ if (position == Position::FIRST || position == Position::LAST) {
+ EXPECT_EQ(conditionTracker.getChangedToTrueDimensions(allConditions)->size(), 1u);
+ EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
+ } else {
+ EXPECT_EQ(conditionTracker.getChangedToTrueDimensions(allConditions)->size(),
+ uids.size());
+ }
+
+ // Now test query
+ const auto queryKey = getWakeLockQueryKey(position, uids, conditionName);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+
+ conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache);
+ EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+
+ // another wake lock acquired by this uid
+ LogEvent event2(/*uid=*/-1, /*pid=*/-1);
+ makeWakeLockEvent(&event2, /*atomId=*/1, /*timestamp=*/0, uids, "wl2", /*acquire=*/1);
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+ conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache,
+ changedCache);
+ EXPECT_FALSE(changedCache[0]);
+ if (position == Position::FIRST || position == Position::LAST) {
+ EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+ } else {
+ EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
+ }
+ EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
+ EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
+
+
+ // wake lock 1 release
+ LogEvent event3(/*uid=*/-1, /*pid=*/-1);
+ makeWakeLockEvent(&event3, /*atomId=*/1, /*timestamp=*/0, uids, "wl1", /*acquire=*/0);
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+ conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache,
+ changedCache);
+ // nothing changes, because wake lock 2 is still held for this uid
+ EXPECT_FALSE(changedCache[0]);
+ if (position == Position::FIRST || position == Position::LAST) {
+ EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+ } else {
+ EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
+ }
+ EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
+ EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
+
+ LogEvent event4(/*uid=*/-1, /*pid=*/-1);
+ makeWakeLockEvent(&event4, /*atomId=*/1, /*timestamp=*/0, uids, "wl2", /*acquire=*/0);
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+ conditionTracker.evaluateCondition(event4, matcherState, allPredicates, conditionCache,
+ changedCache);
+ EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
+ EXPECT_TRUE(changedCache[0]);
+ if (position == Position::FIRST || position == Position::LAST) {
+ EXPECT_EQ(conditionTracker.getChangedToFalseDimensions(allConditions)->size(), 1u);
+ EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
+ } else {
+ EXPECT_EQ(conditionTracker.getChangedToFalseDimensions(allConditions)->size(),
+ uids.size());
+ }
+
+ // query again
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache);
+ EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+ }
+
+}
+
//TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) {
// std::vector<sp<ConditionTracker>> allConditions;
//
diff --git a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
index e416b4c..1ff6621 100644
--- a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
+++ b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
@@ -28,6 +28,7 @@
#include "../metrics/metrics_test_helper.h"
#include "src/stats_log_util.h"
+#include "stats_event.h"
#include "tests/statsd_test_util.h"
#ifdef __ANDROID__
diff --git a/cmds/statsd/tests/log_event/LogEventQueue_test.cpp b/cmds/statsd/tests/log_event/LogEventQueue_test.cpp
index f27d129..c4407f4 100644
--- a/cmds/statsd/tests/log_event/LogEventQueue_test.cpp
+++ b/cmds/statsd/tests/log_event/LogEventQueue_test.cpp
@@ -16,9 +16,11 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <stdio.h>
+
#include <thread>
-#include <stdio.h>
+#include "stats_event.h"
namespace android {
namespace os {
@@ -29,6 +31,25 @@
using std::unique_ptr;
+namespace {
+
+std::unique_ptr<LogEvent> makeLogEvent(uint64_t timestampNs) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, 10);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+ AStatsEvent_build(statsEvent);
+
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+
+ std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/-1, /*pid=*/-1);
+ logEvent->parseBuffer(buf, size);
+ AStatsEvent_release(statsEvent);
+ return logEvent;
+}
+
+} // anonymous namespace
+
#ifdef __ANDROID__
TEST(LogEventQueue_test, TestGoodConsumer) {
LogEventQueue queue(50);
@@ -36,8 +57,7 @@
std::thread writer([&queue, timeBaseNs] {
for (int i = 0; i < 100; i++) {
int64_t oldestEventNs;
- bool success = queue.push(std::make_unique<LogEvent>(10, timeBaseNs + i * 1000),
- &oldestEventNs);
+ bool success = queue.push(makeLogEvent(timeBaseNs + i * 1000), &oldestEventNs);
EXPECT_TRUE(success);
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
@@ -63,8 +83,7 @@
int failure_count = 0;
int64_t oldestEventNs;
for (int i = 0; i < 100; i++) {
- bool success = queue.push(std::make_unique<LogEvent>(10, timeBaseNs + i * 1000),
- &oldestEventNs);
+ bool success = queue.push(makeLogEvent(timeBaseNs + i * 1000), &oldestEventNs);
if (!success) failure_count++;
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 2bfce9b..d416f13 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -15,6 +15,7 @@
#include "statsd_test_util.h"
#include <aidl/android/util/StatsEventParcel.h>
+#include "stats_event.h"
using aidl::android::util::StatsEventParcel;
using std::shared_ptr;
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 3b0667d..ca22bf4 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -16,6 +16,8 @@
package android.accessibilityservice;
+import static android.accessibilityservice.util.AccessibilityUtils.getFilteredHtmlText;
+import static android.accessibilityservice.util.AccessibilityUtils.loadSafeAnimatedImage;
import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
import android.annotation.IntDef;
@@ -27,7 +29,6 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
@@ -782,12 +783,10 @@
}
/**
- * The animated image resource id.
- * <p>
- * <strong>Statically set from
- * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
- * </p>
+ * Gets the animated image resource id.
+ *
* @return The animated image resource id.
+ *
* @hide
*/
public int getAnimatedImageRes() {
@@ -797,10 +796,14 @@
/**
* The animated image drawable.
* <p>
+ * Image can not exceed the screen size.
* <strong>Statically set from
* {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
* </p>
- * @return The animated image drawable.
+ * @return The animated image drawable, or null if the resource is invalid or the image
+ * exceed the screen size.
+ *
+ * @hide
*/
@Nullable
public Drawable loadAnimatedImage(@NonNull Context context) {
@@ -808,11 +811,8 @@
return null;
}
- final PackageManager packageManager = context.getPackageManager();
- final String packageName = mComponentName.getPackageName();
- final ApplicationInfo applicationInfo = mResolveInfo.serviceInfo.applicationInfo;
-
- return packageManager.getDrawable(packageName, mAnimatedImageRes, applicationInfo);
+ return loadSafeAnimatedImage(context, mResolveInfo.serviceInfo.applicationInfo,
+ mAnimatedImageRes);
}
/**
@@ -924,16 +924,19 @@
}
/**
- * The localized html description of the accessibility service.
+ * The localized and restricted html description of the accessibility service.
* <p>
+ * Filters the <img> tag which do not meet the custom specification and the <a> tag.
* <strong>Statically set from
* {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
* </p>
- * @return The localized html description.
+ * @return The localized and restricted html description.
+ *
+ * @hide
*/
@Nullable
public String loadHtmlDescription(@NonNull PackageManager packageManager) {
- if (mHtmlDescriptionRes == 0) {
+ if (mHtmlDescriptionRes == /* invalid */ 0) {
return null;
}
@@ -941,7 +944,7 @@
final CharSequence htmlDescription = packageManager.getText(serviceInfo.packageName,
mHtmlDescriptionRes, serviceInfo.applicationInfo);
if (htmlDescription != null) {
- return htmlDescription.toString().trim();
+ return getFilteredHtmlText(htmlDescription.toString().trim());
}
return null;
}
diff --git a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
index 6209679..d2bdf80 100644
--- a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
@@ -16,18 +16,21 @@
package android.accessibilityservice;
+import static android.accessibilityservice.util.AccessibilityUtils.getFilteredHtmlText;
+import static android.accessibilityservice.util.AccessibilityUtils.loadSafeAnimatedImage;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Xml;
@@ -134,7 +137,7 @@
// Gets animated image
mAnimatedImageRes = asAttributes.getResourceId(
com.android.internal.R.styleable
- .AccessibilityShortcutTarget_animatedImageDrawable, 0);
+ .AccessibilityShortcutTarget_animatedImageDrawable, /* defValue= */ 0);
// Gets html description
mHtmlDescriptionRes = asAttributes.getResourceId(
com.android.internal.R.styleable.AccessibilityShortcutTarget_htmlDescription,
@@ -192,7 +195,7 @@
}
/**
- * The animated image resource id of the accessibility shortcut target.
+ * Gets the animated image resource id.
*
* @return The animated image resource id.
*
@@ -205,7 +208,10 @@
/**
* The animated image drawable of the accessibility shortcut target.
*
- * @return The animated image drawable.
+ * @return The animated image drawable, or null if the resource is invalid or the image
+ * exceed the screen size.
+ *
+ * @hide
*/
@Nullable
public Drawable loadAnimatedImage(@NonNull Context context) {
@@ -213,21 +219,22 @@
return null;
}
- final PackageManager packageManager = context.getPackageManager();
- final String packageName = mComponentName.getPackageName();
- final ApplicationInfo applicationInfo = mActivityInfo.applicationInfo;
-
- return packageManager.getDrawable(packageName, mAnimatedImageRes, applicationInfo);
+ return loadSafeAnimatedImage(context, mActivityInfo.applicationInfo, mAnimatedImageRes);
}
/**
- * The localized html description of the accessibility shortcut target.
+ * The localized and restricted html description of the accessibility shortcut target.
+ * It filters the <img> tag which do not meet the custom specification and the <a> tag.
*
- * @return The localized html description.
+ * @return The localized and restricted html description.
+ *
+ * @hide
*/
@Nullable
public String loadHtmlDescription(@NonNull PackageManager packageManager) {
- return loadResourceString(packageManager, mActivityInfo, mHtmlDescriptionRes);
+ final String htmlDescription = loadResourceString(packageManager, mActivityInfo,
+ mHtmlDescriptionRes);
+ return TextUtils.isEmpty(htmlDescription) ? null : getFilteredHtmlText(htmlDescription);
}
/**
diff --git a/core/java/android/accessibilityservice/util/AccessibilityUtils.java b/core/java/android/accessibilityservice/util/AccessibilityUtils.java
new file mode 100644
index 0000000..fa32bb2
--- /dev/null
+++ b/core/java/android/accessibilityservice/util/AccessibilityUtils.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accessibilityservice.util;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringRes;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.util.TypedValue;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Pattern;
+
+/**
+ * Collection of utilities for accessibility service.
+ *
+ * @hide
+ */
+public final class AccessibilityUtils {
+ private AccessibilityUtils() {}
+
+ // Used for html description of accessibility service. The <img> src tag must follow the
+ // prefix rule. e.g. <img src="R.drawable.fileName"/>
+ private static final String IMG_PREFIX = "R.drawable.";
+ private static final String ANCHOR_TAG = "a";
+ private static final List<String> UNSUPPORTED_TAG_LIST = new ArrayList<>(
+ Collections.singletonList(ANCHOR_TAG));
+
+ /**
+ * Gets the filtered html string for
+ * {@link android.accessibilityservice.AccessibilityServiceInfo} and
+ * {@link android.accessibilityservice.AccessibilityShortcutInfo}. It filters
+ * the <img> tag which do not meet the custom specification and the <a> tag.
+ *
+ * @param text the target text is html format.
+ * @return the filtered html string.
+ */
+ public static @NonNull String getFilteredHtmlText(@NonNull String text) {
+ final String replacementStart = "<invalidtag ";
+ final String replacementEnd = "</invalidtag>";
+
+ for (String tag : UNSUPPORTED_TAG_LIST) {
+ final String regexStart = "(?i)<" + tag + "(\\s+|>)";
+ final String regexEnd = "(?i)</" + tag + "\\s*>";
+ text = Pattern.compile(regexStart).matcher(text).replaceAll(replacementStart);
+ text = Pattern.compile(regexEnd).matcher(text).replaceAll(replacementEnd);
+ }
+
+ final String regexInvalidImgTag = "(?i)<img\\s+(?!src\\s*=\\s*\"(?-i)" + IMG_PREFIX + ")";
+ text = Pattern.compile(regexInvalidImgTag).matcher(text).replaceAll(
+ replacementStart);
+
+ return text;
+ }
+
+ /**
+ * Loads the animated image for
+ * {@link android.accessibilityservice.AccessibilityServiceInfo} and
+ * {@link android.accessibilityservice.AccessibilityShortcutInfo}. It checks the resource
+ * whether to exceed the screen size.
+ *
+ * @param context the current context.
+ * @param applicationInfo the current application.
+ * @param resId the animated image resource id.
+ * @return the animated image which is safe.
+ */
+ @Nullable
+ public static Drawable loadSafeAnimatedImage(@NonNull Context context,
+ @NonNull ApplicationInfo applicationInfo, @StringRes int resId) {
+ if (resId == /* invalid */ 0) {
+ return null;
+ }
+
+ final PackageManager packageManager = context.getPackageManager();
+ final String packageName = applicationInfo.packageName;
+ final Drawable bannerDrawable = packageManager.getDrawable(packageName, resId,
+ applicationInfo);
+ if (bannerDrawable == null) {
+ return null;
+ }
+
+ final boolean isImageWidthOverScreenLength =
+ bannerDrawable.getIntrinsicWidth() > getScreenWidthPixels(context);
+ final boolean isImageHeightOverScreenLength =
+ bannerDrawable.getIntrinsicHeight() > getScreenHeightPixels(context);
+
+ return (isImageWidthOverScreenLength || isImageHeightOverScreenLength)
+ ? null
+ : bannerDrawable;
+ }
+
+ /**
+ * Gets the width of the screen.
+ *
+ * @param context the current context.
+ * @return the width of the screen in term of pixels.
+ */
+ private static int getScreenWidthPixels(@NonNull Context context) {
+ final Resources resources = context.getResources();
+ final int screenWidthDp = resources.getConfiguration().screenWidthDp;
+
+ return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, screenWidthDp,
+ resources.getDisplayMetrics()));
+ }
+
+ /**
+ * Gets the height of the screen.
+ *
+ * @param context the current context.
+ * @return the height of the screen in term of pixels.
+ */
+ private static int getScreenHeightPixels(@NonNull Context context) {
+ final Resources resources = context.getResources();
+ final int screenHeightDp = resources.getConfiguration().screenHeightDp;
+
+ return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, screenHeightDp,
+ resources.getDisplayMetrics()));
+ }
+}
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index d48b35b..fb315ad 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -215,8 +215,7 @@
public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, boolean toTop,
boolean animate, Rect initialBounds, boolean showRecents) throws SecurityException {
try {
- return getService().setTaskWindowingModeSplitScreenPrimary(taskId, createMode, toTop,
- animate, initialBounds, showRecents);
+ return getService().setTaskWindowingModeSplitScreenPrimary(taskId, toTop);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 4f41f8b..969ea70 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -675,7 +675,7 @@
@Override
public int checkPermission(String permName, String pkgName) {
return PermissionManager
- .checkPackageNamePermission(permName, pkgName);
+ .checkPackageNamePermission(permName, pkgName, getUserId());
}
@Override
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 96edca1..2873b10 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1397,9 +1397,9 @@
}
@Override
- public void sendOrderedBroadcast(Intent intent, String receiverPermission, String receiverAppOp,
- Bundle options, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
- String initialData, @Nullable Bundle initialExtras) {
+ public void sendOrderedBroadcast(Intent intent, int initialCode, String receiverPermission,
+ String receiverAppOp, BroadcastReceiver resultReceiver, Handler scheduler,
+ String initialData, @Nullable Bundle initialExtras, Bundle options) {
int intAppOp = AppOpsManager.OP_NONE;
if (!TextUtils.isEmpty(receiverAppOp)) {
intAppOp = AppOpsManager.strOpToOp(receiverAppOp);
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 463c8c9..6f0611e 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -528,29 +528,6 @@
boolean unlockUser(int userid, in byte[] token, in byte[] secret,
in IProgressListener listener);
void killPackageDependents(in String packageName, int userId);
- /**
- * Resizes the docked stack, and all other stacks as the result of the dock stack bounds change.
- *
- * @param dockedBounds The bounds for the docked stack.
- * @param tempDockedTaskBounds The temporary bounds for the tasks in the docked stack, which
- * might be different from the stack bounds to allow more
- * flexibility while resizing, or {@code null} if they should be the
- * same as the stack bounds.
- * @param tempDockedTaskInsetBounds The temporary bounds for the tasks to calculate the insets.
- * When resizing, we usually "freeze" the layout of a task. To
- * achieve that, we also need to "freeze" the insets, which
- * gets achieved by changing task bounds but not bounds used
- * to calculate the insets in this transient state
- * @param tempOtherTaskBounds The temporary bounds for the tasks in all other stacks, or
- * {@code null} if they should be the same as the stack bounds.
- * @param tempOtherTaskInsetBounds Like {@code tempDockedTaskInsetBounds}, but for the other
- * stacks.
- * @throws RemoteException
- */
- @UnsupportedAppUsage
- void resizeDockedStack(in Rect dockedBounds, in Rect tempDockedTaskBounds,
- in Rect tempDockedTaskInsetBounds,
- in Rect tempOtherTaskBounds, in Rect tempOtherTaskInsetBounds);
@UnsupportedAppUsage
void removeStack(int stackId);
void makePackageIdle(String packageName, int userId);
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 266a06a..7c89263 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -244,8 +244,7 @@
*/
boolean setTaskWindowingMode(int taskId, int windowingMode, boolean toTop);
void moveTaskToStack(int taskId, int stackId, boolean toTop);
- boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, boolean toTop,
- boolean animate, in Rect initialBounds, boolean showRecents);
+ boolean setTaskWindowingModeSplitScreenPrimary(int taskId, boolean toTop);
/**
* Removes stacks in the input windowing modes from the system if they are of activity type
* ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
diff --git a/core/java/android/app/compat/ChangeIdStateCache.java b/core/java/android/app/compat/ChangeIdStateCache.java
new file mode 100644
index 0000000..9ef63f6
--- /dev/null
+++ b/core/java/android/app/compat/ChangeIdStateCache.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.compat;
+
+import android.app.PropertyInvalidatedCache;
+import android.content.Context;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import com.android.internal.compat.IPlatformCompat;
+
+/**
+ * Handles caching of calls to {@link com.android.internal.compat.IPlatformCompat}
+ * @hide
+ */
+public final class ChangeIdStateCache
+ extends PropertyInvalidatedCache<ChangeIdStateQuery, Boolean> {
+ private static final String CACHE_KEY = "cache_key.is_compat_change_enabled";
+ private static final int MAX_ENTRIES = 20;
+ private static boolean sDisabled = false;
+
+ /** @hide */
+ public ChangeIdStateCache() {
+ super(MAX_ENTRIES, CACHE_KEY);
+ }
+
+ /**
+ * Disable cache.
+ *
+ * <p>Should only be used in unit tests.
+ * @hide
+ */
+ public static void disable() {
+ sDisabled = true;
+ }
+
+ /**
+ * Invalidate the cache.
+ *
+ * <p>Can only be called by the system server process.
+ * @hide
+ */
+ public static void invalidate() {
+ if (!sDisabled) {
+ PropertyInvalidatedCache.invalidateCache(CACHE_KEY);
+ }
+ }
+
+ @Override
+ protected Boolean recompute(ChangeIdStateQuery query) {
+ IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
+ ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (query.type == ChangeIdStateQuery.QUERY_BY_PACKAGE_NAME) {
+ return platformCompat.isChangeEnabledByPackageName(query.changeId,
+ query.packageName,
+ query.userId);
+ } else if (query.type == ChangeIdStateQuery.QUERY_BY_UID) {
+ return platformCompat.isChangeEnabledByUid(query.changeId, query.uid);
+ } else {
+ throw new IllegalArgumentException("Invalid query type: " + query.type);
+ }
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ throw new IllegalStateException("Could not recompute value!");
+ }
+}
diff --git a/core/java/android/app/compat/ChangeIdStateQuery.java b/core/java/android/app/compat/ChangeIdStateQuery.java
new file mode 100644
index 0000000..3c245b1
--- /dev/null
+++ b/core/java/android/app/compat/ChangeIdStateQuery.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.compat;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+
+import com.android.internal.annotations.Immutable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+
+/**
+ * A key type for caching calls to {@link com.android.internal.compat.IPlatformCompat}
+ *
+ * <p>For {@link com.android.internal.compat.IPlatformCompat#isChangeEnabledByPackageName}
+ * and {@link com.android.internal.compat.IPlatformCompat#isChangeEnabledByUid}
+ *
+ * @hide
+ */
+@Immutable
+final class ChangeIdStateQuery {
+
+ static final int QUERY_BY_PACKAGE_NAME = 0;
+ static final int QUERY_BY_UID = 1;
+ @IntDef({QUERY_BY_PACKAGE_NAME, QUERY_BY_UID})
+ @Retention(RetentionPolicy.SOURCE)
+ @interface QueryType {}
+
+ public @QueryType int type;
+ public long changeId;
+ public String packageName;
+ public int uid;
+ public int userId;
+
+ private ChangeIdStateQuery(@QueryType int type, long changeId, String packageName,
+ int uid, int userId) {
+ this.type = type;
+ this.changeId = changeId;
+ this.packageName = packageName;
+ this.uid = uid;
+ this.userId = userId;
+ }
+
+ static ChangeIdStateQuery byPackageName(long changeId, @NonNull String packageName,
+ int userId) {
+ return new ChangeIdStateQuery(QUERY_BY_PACKAGE_NAME, changeId, packageName, 0, userId);
+ }
+
+ static ChangeIdStateQuery byUid(long changeId, int uid) {
+ return new ChangeIdStateQuery(QUERY_BY_UID, changeId, null, uid, 0);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if ((other == null) || !(other instanceof ChangeIdStateQuery)) {
+ return false;
+ }
+ final ChangeIdStateQuery that = (ChangeIdStateQuery) other;
+ return this.type == that.type
+ && this.changeId == that.changeId
+ && Objects.equals(this.packageName, that.packageName)
+ && this.uid == that.uid
+ && this.userId == that.userId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(type, changeId, packageName, uid, userId);
+ }
+}
diff --git a/core/java/android/app/compat/CompatChanges.java b/core/java/android/app/compat/CompatChanges.java
index e289a27..0d5e45f 100644
--- a/core/java/android/app/compat/CompatChanges.java
+++ b/core/java/android/app/compat/CompatChanges.java
@@ -19,14 +19,8 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.compat.Compatibility;
-import android.content.Context;
-import android.os.Binder;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.UserHandle;
-import com.android.internal.compat.IPlatformCompat;
-
/**
* CompatChanges APIs - to be used by platform code only (including mainline
* modules).
@@ -35,6 +29,7 @@
*/
@SystemApi
public final class CompatChanges {
+ private static final ChangeIdStateCache QUERY_CACHE = new ChangeIdStateCache();
private CompatChanges() {}
/**
@@ -69,17 +64,8 @@
*/
public static boolean isChangeEnabled(long changeId, @NonNull String packageName,
@NonNull UserHandle user) {
- IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
- ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
- final long token = Binder.clearCallingIdentity();
- try {
- return platformCompat.isChangeEnabledByPackageName(changeId, packageName,
- user.getIdentifier());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ return QUERY_CACHE.query(ChangeIdStateQuery.byPackageName(changeId, packageName,
+ user.getIdentifier()));
}
/**
@@ -101,15 +87,7 @@
* @return {@code true} if the change is enabled for the current app.
*/
public static boolean isChangeEnabled(long changeId, int uid) {
- IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
- ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
- final long token = Binder.clearCallingIdentity();
- try {
- return platformCompat.isChangeEnabledByUid(changeId, uid);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ return QUERY_CACHE.query(ChangeIdStateQuery.byUid(changeId, uid));
}
+
}
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index 1f89ddf..277a5a8 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -278,16 +278,19 @@
}
/**
- * Pbap does not store connection policy, so this function only disconnects pbap if
- * connectionPolicy is {@link #CONNECTION_POLICY_FORBIDDEN}.
+ * Set connection policy of the profile and tries to disconnect it if connectionPolicy is
+ * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}
*
* <p> The device should already be paired.
- * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
- * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
+ * Connection policy can be one of:
+ * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED},
+ * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN},
+ * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN}
*
* @param device Paired bluetooth device
- * @param connectionPolicy determines whether to disconnect the device
- * @return true if pbap is successfully disconnected, false otherwise
+ * @param connectionPolicy is the connection policy to set to for this profile
+ * @return true if connectionPolicy is set, false on error
+ *
* @hide
*/
@SystemApi
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index b748cfa..c7f42cb 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -3827,6 +3827,18 @@
return queryArgs;
}
+ /** @hide */
+ public static @NonNull Bundle includeSqlSelectionArgs(@NonNull Bundle queryArgs,
+ @Nullable String selection, @Nullable String[] selectionArgs) {
+ if (selection != null) {
+ queryArgs.putString(QUERY_ARG_SQL_SELECTION, selection);
+ }
+ if (selectionArgs != null) {
+ queryArgs.putStringArray(QUERY_ARG_SQL_SELECTION_ARGS, selectionArgs);
+ }
+ return queryArgs;
+ }
+
/**
* Returns structured sort args formatted as an SQL sort clause.
*
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 536b6c3..a8f7610 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2543,12 +2543,13 @@
*
* @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
* @see android.app.BroadcastOptions
+ * @hide
*/
- public void sendOrderedBroadcast(@RequiresPermission @NonNull Intent intent,
+ public void sendOrderedBroadcast(@RequiresPermission @NonNull Intent intent, int initialCode,
@Nullable String receiverPermission, @Nullable String receiverAppOp,
- @Nullable Bundle options, @Nullable BroadcastReceiver resultReceiver,
- @Nullable Handler scheduler, int initialCode, @Nullable String initialData,
- @Nullable Bundle initialExtras) {
+ @Nullable BroadcastReceiver resultReceiver, @Nullable Handler scheduler,
+ @Nullable String initialData, @Nullable Bundle initialExtras,
+ @Nullable Bundle options) {
throw new RuntimeException("Not implemented. Must override in a subclass.");
}
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index e5381ea..91d214b 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -600,13 +600,13 @@
}
@Override
- public void sendOrderedBroadcast(@RequiresPermission @NonNull Intent intent,
+ public void sendOrderedBroadcast(@RequiresPermission @NonNull Intent intent, int initialCode,
@Nullable String receiverPermission, @Nullable String receiverAppOp,
- @Nullable Bundle options, @Nullable BroadcastReceiver resultReceiver,
- @Nullable Handler scheduler, int initialCode, @Nullable String initialData,
- @Nullable Bundle initialExtras) {
- mBase.sendOrderedBroadcast(intent, receiverPermission, receiverAppOp, options,
- resultReceiver, scheduler, initialCode, initialData, initialExtras);
+ @Nullable BroadcastReceiver resultReceiver, @Nullable Handler scheduler,
+ @Nullable String initialData, @Nullable Bundle initialExtras,
+ @Nullable Bundle options) {
+ mBase.sendOrderedBroadcast(intent, initialCode, receiverPermission, receiverAppOp,
+ resultReceiver, scheduler, initialData, initialExtras, options);
}
@Override
diff --git a/core/java/android/content/om/OverlayInfo.java b/core/java/android/content/om/OverlayInfo.java
index 55a6cab..62815dd 100644
--- a/core/java/android/content/om/OverlayInfo.java
+++ b/core/java/android/content/om/OverlayInfo.java
@@ -138,6 +138,7 @@
*
* @hide
*/
+ @NonNull
public final String packageName;
/**
@@ -145,6 +146,7 @@
*
* @hide
*/
+ @NonNull
public final String targetPackageName;
/**
@@ -165,6 +167,7 @@
* Full path to the base APK for this overlay package
* @hide
*/
+ @NonNull
public final String baseCodePath;
/**
@@ -292,6 +295,7 @@
return targetOverlayableName;
}
+ @SuppressWarnings("ConstantConditions")
private void ensureValidState() {
if (packageName == null) {
throw new IllegalArgumentException("packageName must not be null");
diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
index e7d91c2..115d6c4 100644
--- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
@@ -40,11 +40,6 @@
import android.content.pm.ServiceInfo;
import android.content.pm.Signature;
import android.content.pm.SigningInfo;
-import android.content.pm.parsing.ParsingPackage;
-import android.os.Environment;
-import android.os.UserHandle;
-
-import com.android.internal.util.ArrayUtils;
import android.content.pm.parsing.component.ComponentParseUtils;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedComponent;
@@ -54,6 +49,10 @@
import android.content.pm.parsing.component.ParsedPermissionGroup;
import android.content.pm.parsing.component.ParsedProvider;
import android.content.pm.parsing.component.ParsedService;
+import android.os.Environment;
+import android.os.UserHandle;
+
+import com.android.internal.util.ArrayUtils;
import libcore.util.EmptyArray;
@@ -186,6 +185,22 @@
return null;
}
+ return generateWithoutComponentsUnchecked(pkg, gids, flags, firstInstallTime,
+ lastUpdateTime, grantedPermissions, state, userId, apexInfo, applicationInfo);
+ }
+
+ /**
+ * This bypasses critical checks that are necessary for usage with data passed outside of
+ * system server.
+ *
+ * Prefer {@link #generateWithoutComponents(ParsingPackageRead, int[], int, long, long, Set,
+ * PackageUserState, int, ApexInfo, ApplicationInfo)}.
+ */
+ @NonNull
+ public static PackageInfo generateWithoutComponentsUnchecked(ParsingPackageRead pkg, int[] gids,
+ @PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime,
+ Set<String> grantedPermissions, PackageUserState state, int userId,
+ @Nullable ApexInfo apexInfo, @NonNull ApplicationInfo applicationInfo) {
PackageInfo pi = new PackageInfo();
pi.packageName = pkg.getPackageName();
pi.splitNames = pkg.getSplitNames();
@@ -317,6 +332,18 @@
return null;
}
+ return generateApplicationInfoUnchecked(pkg, flags, state, userId);
+ }
+
+ /**
+ * This bypasses critical checks that are necessary for usage with data passed outside of
+ * system server.
+ *
+ * Prefer {@link #generateApplicationInfo(ParsingPackageRead, int, PackageUserState, int)}.
+ */
+ @NonNull
+ public static ApplicationInfo generateApplicationInfoUnchecked(@NonNull ParsingPackageRead pkg,
+ @PackageManager.ApplicationInfoFlags int flags, PackageUserState state, int userId) {
// Make shallow copy so we can store the metadata/libraries safely
ApplicationInfo ai = pkg.toAppInfoWithoutState();
// Init handles data directories
@@ -378,6 +405,23 @@
if (applicationInfo == null) {
applicationInfo = generateApplicationInfo(pkg, flags, state, userId);
}
+ if (applicationInfo == null) {
+ return null;
+ }
+
+ return generateActivityInfoUnchecked(a, applicationInfo);
+ }
+
+ /**
+ * This bypasses critical checks that are necessary for usage with data passed outside of
+ * system server.
+ *
+ * Prefer {@link #generateActivityInfo(ParsingPackageRead, ParsedActivity, int,
+ * PackageUserState, ApplicationInfo, int)}.
+ */
+ @NonNull
+ public static ActivityInfo generateActivityInfoUnchecked(@NonNull ParsedActivity a,
+ @NonNull ApplicationInfo applicationInfo) {
// Make shallow copies so we can store the metadata safely
ActivityInfo ai = new ActivityInfo();
assignSharedFieldsForComponentInfo(ai, a);
@@ -431,6 +475,23 @@
if (applicationInfo == null) {
applicationInfo = generateApplicationInfo(pkg, flags, state, userId);
}
+ if (applicationInfo == null) {
+ return null;
+ }
+
+ return generateServiceInfoUnchecked(s, applicationInfo);
+ }
+
+ /**
+ * This bypasses critical checks that are necessary for usage with data passed outside of
+ * system server.
+ *
+ * Prefer {@link #generateServiceInfo(ParsingPackageRead, ParsedService, int, PackageUserState,
+ * ApplicationInfo, int)}.
+ */
+ @NonNull
+ public static ServiceInfo generateServiceInfoUnchecked(@NonNull ParsedService s,
+ @NonNull ApplicationInfo applicationInfo) {
// Make shallow copies so we can store the metadata safely
ServiceInfo si = new ServiceInfo();
assignSharedFieldsForComponentInfo(si, s);
@@ -461,6 +522,24 @@
if (applicationInfo == null) {
applicationInfo = generateApplicationInfo(pkg, flags, state, userId);
}
+ if (applicationInfo == null) {
+ return null;
+ }
+
+ return generateProviderInfoUnchecked(p, flags, applicationInfo);
+ }
+
+ /**
+ * This bypasses critical checks that are necessary for usage with data passed outside of
+ * system server.
+ *
+ * Prefer {@link #generateProviderInfo(ParsingPackageRead, ParsedProvider, int,
+ * PackageUserState, ApplicationInfo, int)}.
+ */
+ @NonNull
+ public static ProviderInfo generateProviderInfoUnchecked(@NonNull ParsedProvider p,
+ @PackageManager.ComponentInfoFlags int flags,
+ @NonNull ApplicationInfo applicationInfo) {
// Make shallow copies so we can store the metadata safely
ProviderInfo pi = new ProviderInfo();
assignSharedFieldsForComponentInfo(pi, p);
diff --git a/core/java/android/content/res/ResourcesKey.java b/core/java/android/content/res/ResourcesKey.java
index 9e40f46..9da0f20 100644
--- a/core/java/android/content/res/ResourcesKey.java
+++ b/core/java/android/content/res/ResourcesKey.java
@@ -54,7 +54,6 @@
private final int mHash;
- @UnsupportedAppUsage
public ResourcesKey(@Nullable String resDir,
@Nullable String[] splitResDirs,
@Nullable String[] overlayDirs,
@@ -85,6 +84,18 @@
mHash = hash;
}
+ @UnsupportedAppUsage
+ public ResourcesKey(@Nullable String resDir,
+ @Nullable String[] splitResDirs,
+ @Nullable String[] overlayDirs,
+ @Nullable String[] libDirs,
+ int displayId,
+ @Nullable Configuration overrideConfig,
+ @Nullable CompatibilityInfo compatInfo) {
+ this(resDir, splitResDirs, overlayDirs, libDirs, displayId, overrideConfig, compatInfo,
+ null);
+ }
+
public boolean hasOverrideConfiguration() {
return !Configuration.EMPTY.equals(mOverrideConfiguration);
}
diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java
index 4246b84..34cc856 100644
--- a/core/java/android/database/DatabaseUtils.java
+++ b/core/java/android/database/DatabaseUtils.java
@@ -16,6 +16,7 @@
package android.database;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentValues;
@@ -1548,4 +1549,24 @@
}
return -1;
}
+
+ /**
+ * Escape the given argument for use in a {@code LIKE} statement.
+ * @hide
+ */
+ public static String escapeForLike(@NonNull String arg) {
+ // Shamelessly borrowed from com.android.providers.media.util.DatabaseUtils
+ final StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < arg.length(); i++) {
+ final char c = arg.charAt(i);
+ switch (c) {
+ case '%': sb.append('\\');
+ break;
+ case '_': sb.append('\\');
+ break;
+ }
+ sb.append(c);
+ }
+ return sb.toString();
+ }
}
diff --git a/core/java/android/debug/AdbManagerInternal.java b/core/java/android/debug/AdbManagerInternal.java
index 0bd9f19..d730129 100644
--- a/core/java/android/debug/AdbManagerInternal.java
+++ b/core/java/android/debug/AdbManagerInternal.java
@@ -53,4 +53,14 @@
* Returns the file that contains all of the ADB keys and their last used time.
*/
public abstract File getAdbTempKeysFile();
+
+ /**
+ * Starts adbd for a transport.
+ */
+ public abstract void startAdbdForTransport(byte transportType);
+
+ /**
+ * Stops adbd for a transport.
+ */
+ public abstract void stopAdbdForTransport(byte transportType);
}
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 743ce7b..85ef4a3 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -131,9 +131,17 @@
}
/**
- * Return the list of combinations of currently connected camera devices identifiers, which
+ * Return the set of combinations of currently connected camera device identifiers, which
* support configuring camera device sessions concurrently.
*
+ * <p>The devices in these combinations can be concurrently configured by the same
+ * client camera application. Using these camera devices concurrently by two different
+ * applications is not guaranteed to be supported, however.</p>
+ *
+ * <p>Each device in a combination, is guaranteed to support stream combinations which may be
+ * obtained by querying {@link #getCameraCharacteristics} for the key
+ * {@link android.hardware.camera2.CameraCharacteristics#SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS}.</p>
+ *
* <p>The set of combinations may include camera devices that may be in use by other camera API
* clients.</p>
*
@@ -174,7 +182,7 @@
* to be used for exploring the entire space of supported concurrent stream combinations. The
* available mandatory concurrent stream combinations may be obtained by querying
* {@link #getCameraCharacteristics} for the key
- * SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS. </p>
+ * {@link android.hardware.camera2.CameraCharacteristics#SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS}. </p>
*
* <p>Note that session parameters will be ignored and calls to
* {@link SessionConfiguration#setSessionParameters} are not required.</p>
diff --git a/core/java/android/hardware/soundtrigger/ConversionUtil.java b/core/java/android/hardware/soundtrigger/ConversionUtil.java
index dbf33ca..3d763e6 100644
--- a/core/java/android/hardware/soundtrigger/ConversionUtil.java
+++ b/core/java/android/hardware/soundtrigger/ConversionUtil.java
@@ -32,7 +32,11 @@
import android.media.soundtrigger_middleware.SoundModel;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
+import android.os.SharedMemory;
+import android.system.ErrnoException;
+import java.io.FileDescriptor;
+import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.UUID;
@@ -105,7 +109,8 @@
aidlModel.type = apiModel.type;
aidlModel.uuid = api2aidlUuid(apiModel.uuid);
aidlModel.vendorUuid = api2aidlUuid(apiModel.vendorUuid);
- aidlModel.data = Arrays.copyOf(apiModel.data, apiModel.data.length);
+ aidlModel.data = byteArrayToSharedMemory(apiModel.data, "SoundTrigger SoundModel");
+ aidlModel.dataSize = apiModel.data.length;
return aidlModel;
}
@@ -352,4 +357,20 @@
}
return result;
}
+
+ private static @Nullable FileDescriptor byteArrayToSharedMemory(byte[] data, String name) {
+ if (data.length == 0) {
+ return null;
+ }
+
+ try {
+ SharedMemory shmem = SharedMemory.create(name != null ? name : "", data.length);
+ ByteBuffer buffer = shmem.mapReadWrite();
+ buffer.put(data);
+ shmem.unmap(buffer);
+ return shmem.getFileDescriptor();
+ } catch (ErrnoException e) {
+ throw new RuntimeException(e);
+ }
+ }
}
diff --git a/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java b/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
index ef76c62..bf641d7 100644
--- a/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
+++ b/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
@@ -28,6 +28,7 @@
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.AttributeSet;
+import android.util.Log;
import android.util.Slog;
import android.util.Xml;
@@ -43,6 +44,7 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Set;
/**
* Enrollment information about the different available keyphrases.
@@ -116,7 +118,12 @@
/**
* List of available keyphrases.
*/
- final private KeyphraseMetadata[] mKeyphrases;
+ private final KeyphraseMetadata[] mKeyphrases;
+
+ /**
+ * Set of UIDs associated with the detected enrollment applications.
+ */
+ private final Set<Integer> mEnrollmentApplicationUids;
/**
* Map between KeyphraseMetadata and the package name of the enrollment app that provides it.
@@ -136,11 +143,13 @@
mParseError = "No enrollment applications found";
mKeyphrasePackageMap = Collections.<KeyphraseMetadata, String>emptyMap();
mKeyphrases = null;
+ mEnrollmentApplicationUids = Collections.emptySet();
return;
}
List<String> parseErrors = new LinkedList<String>();
mKeyphrasePackageMap = new HashMap<KeyphraseMetadata, String>();
+ mEnrollmentApplicationUids = new ArraySet<>();
for (ResolveInfo ri : ris) {
try {
ApplicationInfo ai = pm.getApplicationInfo(
@@ -162,6 +171,7 @@
getKeyphraseMetadataFromApplicationInfo(pm, ai, parseErrors);
if (metadata != null) {
mKeyphrasePackageMap.put(metadata, ai.packageName);
+ mEnrollmentApplicationUids.add(ai.uid);
}
} catch (PackageManager.NameNotFoundException e) {
String error = "error parsing voice enrollment meta-data for "
@@ -372,9 +382,22 @@
return null;
}
+ /**
+ * Tests if the input UID matches a supported enrollment application.
+ *
+ * @param uid UID of the caller to test against.
+ * @return Returns true if input uid matches the uid of a supported enrollment application.
+ * False if not.
+ */
+ public boolean isUidSupportedEnrollmentApplication(int uid) {
+ Log.d(TAG, "isUidSupportedEnrollmentApplication: " + toString());
+ return mEnrollmentApplicationUids.contains(uid);
+ }
+
@Override
public String toString() {
- return "KeyphraseEnrollmentInfo [Keyphrases=" + mKeyphrasePackageMap.toString()
+ return "KeyphraseEnrollmentInfo [KeyphrasePackageMap=" + mKeyphrasePackageMap.toString()
+ + ", enrollmentApplicationUids=" + mEnrollmentApplicationUids.toString()
+ ", ParseError=" + mParseError + "]";
}
}
diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
index 9327b24..e9de274 100644
--- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
@@ -53,6 +53,7 @@
private static final int DO_FINISH_SESSION = 110;
private static final int DO_VIEW_CLICKED = 115;
private static final int DO_NOTIFY_IME_HIDDEN = 120;
+ private static final int DO_REMOVE_IME_SURFACE = 130;
@UnsupportedAppUsage
HandlerCaller mCaller;
@@ -136,6 +137,10 @@
mInputMethodSession.notifyImeHidden();
return;
}
+ case DO_REMOVE_IME_SURFACE: {
+ mInputMethodSession.removeImeSurface();
+ return;
+ }
}
Log.w(TAG, "Unhandled message code: " + msg.what);
}
@@ -184,6 +189,11 @@
}
@Override
+ public void removeImeSurface() {
+ mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_REMOVE_IME_SURFACE));
+ }
+
+ @Override
public void updateCursor(Rect newCursor) {
mCaller.executeOrSendMessage(
mCaller.obtainMessageO(DO_UPDATE_CURSOR, newCursor));
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index f0b1eaa..b52b437 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -219,22 +219,29 @@
case DO_REVOKE_SESSION:
inputMethod.revokeSession((InputMethodSession)msg.obj);
return;
- case DO_SHOW_SOFT_INPUT:
- SomeArgs args = (SomeArgs)msg.obj;
+ case DO_SHOW_SOFT_INPUT: {
+ final SomeArgs args = (SomeArgs)msg.obj;
inputMethod.showSoftInputWithToken(
msg.arg1, (ResultReceiver) args.arg2, (IBinder) args.arg1);
+ args.recycle();
return;
- case DO_HIDE_SOFT_INPUT:
- inputMethod.hideSoftInput(msg.arg1, (ResultReceiver)msg.obj);
+ }
+ case DO_HIDE_SOFT_INPUT: {
+ final SomeArgs args = (SomeArgs) msg.obj;
+ inputMethod.hideSoftInputWithToken(msg.arg1, (ResultReceiver) args.arg2,
+ (IBinder) args.arg1);
+ args.recycle();
return;
+ }
case DO_CHANGE_INPUTMETHOD_SUBTYPE:
inputMethod.changeInputMethodSubtype((InputMethodSubtype)msg.obj);
return;
case DO_CREATE_INLINE_SUGGESTIONS_REQUEST:
- args = (SomeArgs) msg.obj;
+ final SomeArgs args = (SomeArgs) msg.obj;
inputMethod.onCreateInlineSuggestionsRequest(
(InlineSuggestionsRequestInfo) args.arg1,
(IInlineSuggestionsRequestCallback) args.arg2);
+ args.recycle();
return;
}
@@ -380,9 +387,9 @@
@BinderThread
@Override
- public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
- mCaller.executeOrSendMessage(mCaller.obtainMessageIO(DO_HIDE_SOFT_INPUT,
- flags, resultReceiver));
+ public void hideSoftInput(IBinder hideInputToken, int flags, ResultReceiver resultReceiver) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(DO_HIDE_SOFT_INPUT,
+ flags, hideInputToken, resultReceiver));
}
@BinderThread
diff --git a/core/java/android/inputmethodservice/InlineSuggestionSession.java b/core/java/android/inputmethodservice/InlineSuggestionSession.java
index edae06a..1f12d2a 100644
--- a/core/java/android/inputmethodservice/InlineSuggestionSession.java
+++ b/core/java/android/inputmethodservice/InlineSuggestionSession.java
@@ -27,6 +27,7 @@
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
+import android.view.autofill.AutofillId;
import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.inputmethod.InlineSuggestionsResponse;
@@ -145,8 +146,8 @@
}
@Override
- public void onInlineSuggestionsResponse(InlineSuggestionsResponse response)
- throws RemoteException {
+ public void onInlineSuggestionsResponse(AutofillId fieldId,
+ InlineSuggestionsResponse response) {
final InlineSuggestionSession session = mInlineSuggestionSession.get();
if (session != null) {
session.mHandler.sendMessage(obtainMessage(
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 20a4ab3..27839e7 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -459,6 +459,16 @@
*/
private IBinder mCurShowInputToken;
+ /**
+ * An opaque {@link Binder} token of window requesting {@link InputMethodImpl#hideSoftInput}
+ * The original app window token is passed from client app window.
+ * {@link com.android.server.inputmethod.InputMethodManagerService} creates a unique dummy
+ * token to identify this window.
+ * This dummy token is only valid for a single call to {@link InputMethodImpl#hideSoftInput},
+ * after which it is set {@code null} until next call.
+ */
+ private IBinder mCurHideInputToken;
+
final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = info -> {
onComputeInsets(mTmpInsets);
if (isExtractViewShown()) {
@@ -500,6 +510,7 @@
public class InputMethodImpl extends AbstractInputMethodImpl {
private boolean mSystemCallingShowSoftInput;
+ private boolean mSystemCallingHideSoftInput;
/**
* {@inheritDoc}
@@ -636,11 +647,32 @@
/**
* {@inheritDoc}
+ * @hide
+ */
+ @MainThread
+ @Override
+ public void hideSoftInputWithToken(int flags, ResultReceiver resultReceiver,
+ IBinder hideInputToken) {
+ mSystemCallingHideSoftInput = true;
+ mCurHideInputToken = hideInputToken;
+ hideSoftInput(flags, resultReceiver);
+ mCurHideInputToken = null;
+ mSystemCallingHideSoftInput = false;
+ }
+
+ /**
+ * {@inheritDoc}
*/
@MainThread
@Override
public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
if (DEBUG) Log.v(TAG, "hideSoftInput()");
+ if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R
+ && !mSystemCallingHideSoftInput) {
+ Log.e(TAG, "IME shouldn't call hideSoftInput on itself."
+ + " Use requestHideSelf(int) itself");
+ return;
+ }
final boolean wasVisible = mIsPreRendered
? mDecorViewVisible && mWindowVisible : isInputViewShown();
applyVisibilityInInsetsConsumerIfNecessary(false /* setVisible */);
@@ -738,6 +770,15 @@
public void setCurrentShowInputToken(IBinder showInputToken) {
mCurShowInputToken = showInputToken;
}
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @Override
+ public void setCurrentHideInputToken(IBinder hideInputToken) {
+ mCurHideInputToken = hideInputToken;
+ }
}
// TODO(b/137800469): Add detailed docs explaining the inline suggestions process.
@@ -814,6 +855,13 @@
onPreRenderedWindowVisibilityChanged(false /* setVisible */);
}
+ private void removeImeSurface() {
+ if (!mShowInputRequested && !mWindowVisible) {
+ // hiding a window removes its surface.
+ mWindow.hide();
+ }
+ }
+
private void setImeWindowStatus(int visibilityFlags, int backDisposition) {
mPrivOps.setImeWindowStatus(visibilityFlags, backDisposition);
}
@@ -932,6 +980,14 @@
public final void notifyImeHidden() {
InputMethodService.this.notifyImeHidden();
}
+
+ /**
+ * Notify IME that surface can be now removed.
+ * @hide
+ */
+ public final void removeImeSurface() {
+ InputMethodService.this.removeImeSurface();
+ }
}
/**
@@ -2157,7 +2213,8 @@
if (!isVisibilityAppliedUsingInsetsConsumer()) {
return;
}
- mPrivOps.applyImeVisibility(mCurShowInputToken, setVisible);
+ mPrivOps.applyImeVisibility(setVisible
+ ? mCurShowInputToken : mCurHideInputToken, setVisible);
}
private boolean isVisibilityAppliedUsingInsetsConsumer() {
diff --git a/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java b/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java
index 31c948a..ef138a0 100644
--- a/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java
+++ b/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java
@@ -296,6 +296,12 @@
// no-op for multi-session since IME is responsible controlling navigation bar buttons.
reportNotSupported();
}
+
+ @Override
+ public void removeImeSurface() {
+ // no-op for multi-session
+ reportNotSupported();
+ }
}
private static final class MultiClientInputMethodSessionImpl
diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java
index a3c2cd8..9ca1c33 100644
--- a/core/java/android/os/BugreportManager.java
+++ b/core/java/android/os/BugreportManager.java
@@ -27,8 +27,10 @@
import android.annotation.TestApi;
import android.app.ActivityManager;
import android.content.Context;
+import android.os.Handler;
import android.util.Log;
-
+import android.widget.Toast;
+import com.android.internal.R;
import com.android.internal.util.Preconditions;
import libcore.io.IoUtils;
@@ -155,12 +157,14 @@
Preconditions.checkNotNull(executor);
Preconditions.checkNotNull(callback);
+ boolean validScreenshotFd = screenshotFd != null;
if (screenshotFd == null) {
// Binder needs a valid File Descriptor to be passed
screenshotFd = ParcelFileDescriptor.open(new File("/dev/null"),
ParcelFileDescriptor.MODE_READ_ONLY);
}
- DumpstateListener dsListener = new DumpstateListener(executor, callback);
+ DumpstateListener dsListener = new DumpstateListener(executor, callback,
+ validScreenshotFd);
// Note: mBinder can get callingUid from the binder transaction.
mBinder.startBugreport(-1 /* callingUid */,
mContext.getOpPackageName(),
@@ -221,10 +225,13 @@
private final class DumpstateListener extends IDumpstateListener.Stub {
private final Executor mExecutor;
private final BugreportCallback mCallback;
+ private final boolean mValidScreenshotFd;
- DumpstateListener(Executor executor, BugreportCallback callback) {
+ DumpstateListener(Executor executor, BugreportCallback callback,
+ boolean validScreenshotFd) {
mExecutor = executor;
mCallback = callback;
+ mValidScreenshotFd = validScreenshotFd;
}
@Override
@@ -262,5 +269,20 @@
Binder.restoreCallingIdentity(identity);
}
}
+
+ @Override
+ public void onScreenshotTaken(boolean success) throws RemoteException {
+ if (!mValidScreenshotFd) {
+ return;
+ }
+
+ Handler mainThreadHandler = new Handler(Looper.getMainLooper());
+ mainThreadHandler.post(
+ () -> {
+ int message = success ? R.string.bugreport_screenshot_success_toast
+ : R.string.bugreport_screenshot_failure_toast;
+ Toast.makeText(mContext, message, Toast.LENGTH_LONG).show();
+ });
+ }
}
}
diff --git a/core/java/android/os/HidlMemoryUtil.java b/core/java/android/os/HidlMemoryUtil.java
index b08822d..4252fe3 100644
--- a/core/java/android/os/HidlMemoryUtil.java
+++ b/core/java/android/os/HidlMemoryUtil.java
@@ -21,14 +21,13 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.annotation.TestApi;
import android.system.ErrnoException;
import android.system.Os;
import android.util.Log;
import com.android.internal.util.Preconditions;
+import java.io.FileDescriptor;
import java.nio.ByteBuffer;
import java.nio.DirectByteBuffer;
import java.util.ArrayList;
@@ -82,8 +81,7 @@
ByteBuffer buffer = shmem.mapReadWrite();
buffer.put(input);
shmem.unmap(buffer);
- NativeHandle handle = new NativeHandle(shmem.getFileDescriptor(), true);
- return new HidlMemory("ashmem", input.length, handle);
+ return sharedMemoryToHidlMemory(shmem);
} catch (ErrnoException e) {
throw new RuntimeException(e);
}
@@ -128,8 +126,7 @@
buffer.put(b);
}
shmem.unmap(buffer);
- NativeHandle handle = new NativeHandle(shmem.getFileDescriptor(), true);
- return new HidlMemory("ashmem", input.size(), handle);
+ return sharedMemoryToHidlMemory(shmem);
} catch (ErrnoException e) {
throw new RuntimeException(e);
}
@@ -189,6 +186,38 @@
return result;
}
+ /**
+ * Converts a SharedMemory to a HidlMemory without copying.
+ *
+ * @param shmem The shared memory object. Null means "empty" and will still result in a non-null
+ * return value.
+ * @return The HidlMemory instance.
+ */
+ @NonNull public static HidlMemory sharedMemoryToHidlMemory(@Nullable SharedMemory shmem) {
+ if (shmem == null) {
+ return new HidlMemory("ashmem", 0, null);
+ }
+ return fileDescriptorToHidlMemory(shmem.getFileDescriptor(), shmem.getSize());
+ }
+
+ /**
+ * Converts a FileDescriptor to a HidlMemory without copying.
+ *
+ * @param fd The FileDescriptor object. Null is allowed if size is 0 and will still result in
+ * a non-null return value.
+ * @param size The size of the memory buffer.
+ * @return The HidlMemory instance.
+ */
+ @NonNull public static HidlMemory fileDescriptorToHidlMemory(@Nullable FileDescriptor fd,
+ int size) {
+ Preconditions.checkArgument(fd != null || size == 0);
+ if (fd == null) {
+ return new HidlMemory("ashmem", 0, null);
+ }
+ NativeHandle handle = new NativeHandle(fd, true);
+ return new HidlMemory("ashmem", size, handle);
+ }
+
private static ByteBuffer getBuffer(@NonNull HidlMemory mem) {
try {
final int size = (int) mem.getSize();
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index dbe3b7b..d7af1b9 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -529,6 +529,40 @@
private static int sPidFdSupported = PIDFD_UNKNOWN;
/**
+ * Value used to indicate that there is no special information about an application launch. App
+ * launches with this policy will occur through the primary or secondary Zygote with no special
+ * treatment.
+ *
+ * @hide
+ */
+ public static final int ZYGOTE_POLICY_FLAG_EMPTY = 0;
+
+ /**
+ * Flag used to indicate that an application launch is user-visible and latency sensitive. Any
+ * launch with this policy will use a Unspecialized App Process Pool if the target Zygote
+ * supports it.
+ *
+ * @hide
+ */
+ public static final int ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE = 1 << 0;
+
+ /**
+ * Flag used to indicate that the launch is one in a series of app launches that will be
+ * performed in quick succession. For future use.
+ *
+ * @hide
+ */
+ public static final int ZYGOTE_POLICY_FLAG_BATCH_LAUNCH = 1 << 1;
+
+ /**
+ * Flag used to indicate that the current launch event is for a system process. All system
+ * processes are equally important, so none of them should be prioritized over the others.
+ *
+ * @hide
+ */
+ public static final int ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS = 1 << 2;
+
+ /**
* State associated with the zygote process.
* @hide
*/
@@ -567,6 +601,7 @@
* @param appDataDir null-ok the data directory of the app.
* @param invokeWith null-ok the command to invoke with.
* @param packageName null-ok the name of the package this process belongs to.
+ * @param zygotePolicyFlags Flags used to determine how to launch the application
* @param isTopApp whether the process starts for high priority application.
* @param disabledCompatChanges null-ok list of disabled compat changes for the process being
* started.
@@ -590,6 +625,7 @@
@Nullable String appDataDir,
@Nullable String invokeWith,
@Nullable String packageName,
+ int zygotePolicyFlags,
boolean isTopApp,
@Nullable long[] disabledCompatChanges,
@Nullable Map<String, Pair<String, Long>>
@@ -598,7 +634,7 @@
return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, packageName,
- /*useUsapPool=*/ true, isTopApp, disabledCompatChanges,
+ zygotePolicyFlags, isTopApp, disabledCompatChanges,
pkgDataInfoMap, zygoteArgs);
}
@@ -622,8 +658,8 @@
return WebViewZygote.getProcess().start(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, packageName,
- /*useUsapPool=*/ false, /*isTopApp=*/ false, disabledCompatChanges,
- /* pkgDataInfoMap */ null, zygoteArgs);
+ /*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, /*isTopApp=*/ false,
+ disabledCompatChanges, /* pkgDataInfoMap */ null, zygoteArgs);
}
/**
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 3846f89..34cec06 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -16,6 +16,9 @@
package android.os;
+import static android.os.Process.ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE;
+import static android.os.Process.ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
@@ -119,6 +122,10 @@
mUsapPoolSecondarySocketAddress =
new LocalSocketAddress(Zygote.USAP_POOL_SECONDARY_SOCKET_NAME,
LocalSocketAddress.Namespace.RESERVED);
+
+ // This constructor is used to create the primary and secondary Zygotes, which can support
+ // Unspecialized App Process Pools.
+ mUsapPoolSupported = true;
}
public ZygoteProcess(LocalSocketAddress primarySocketAddress,
@@ -128,6 +135,10 @@
mUsapPoolSocketAddress = null;
mUsapPoolSecondarySocketAddress = null;
+
+ // This constructor is used to create the primary and secondary Zygotes, which CAN NOT
+ // support Unspecialized App Process Pools.
+ mUsapPoolSupported = false;
}
public LocalSocketAddress getPrimarySocketAddress() {
@@ -267,6 +278,14 @@
private ZygoteState secondaryZygoteState;
/**
+ * If this Zygote supports the creation and maintenance of a USAP pool.
+ *
+ * Currently only the primary and secondary Zygotes support USAP pools. Any
+ * child Zygotes will be unable to create or use a USAP pool.
+ */
+ private final boolean mUsapPoolSupported;
+
+ /**
* If the USAP pool should be created and used to start applications.
*
* Setting this value to false will disable the creation, maintenance, and use of the USAP
@@ -308,13 +327,14 @@
* @param appDataDir null-ok the data directory of the app.
* @param invokeWith null-ok the command to invoke with.
* @param packageName null-ok the name of the package this process belongs to.
+ * @param zygotePolicyFlags Flags used to determine how to launch the application.
+ * @param isTopApp Whether the process starts for high priority application.
* @param disabledCompatChanges null-ok list of disabled compat changes for the process being
* started.
- * @param zygoteArgs Additional arguments to supply to the zygote process.
- * @param isTopApp Whether the process starts for high priority application.
* @param pkgDataInfoMap Map from related package names to private data directory
* volume UUID and inode number.
*
+ * @param zygoteArgs Additional arguments to supply to the Zygote process.
* @return An object that describes the result of the attempt to start the process.
* @throws RuntimeException on fatal start failure
*/
@@ -329,7 +349,7 @@
@Nullable String appDataDir,
@Nullable String invokeWith,
@Nullable String packageName,
- boolean useUsapPool,
+ int zygotePolicyFlags,
boolean isTopApp,
@Nullable long[] disabledCompatChanges,
@Nullable Map<String, Pair<String, Long>>
@@ -344,7 +364,7 @@
return startViaZygote(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/ false,
- packageName, useUsapPool, isTopApp, disabledCompatChanges,
+ packageName, zygotePolicyFlags, isTopApp, disabledCompatChanges,
pkgDataInfoMap, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
@@ -391,7 +411,7 @@
*/
@GuardedBy("mLock")
private Process.ProcessStartResult zygoteSendArgsAndGetResult(
- ZygoteState zygoteState, boolean useUsapPool, @NonNull ArrayList<String> args)
+ ZygoteState zygoteState, int zygotePolicyFlags, @NonNull ArrayList<String> args)
throws ZygoteStartFailedEx {
// Throw early if any of the arguments are malformed. This means we can
// avoid writing a partial response to the zygote.
@@ -417,7 +437,7 @@
*/
String msgStr = args.size() + "\n" + String.join("\n", args) + "\n";
- if (useUsapPool && mUsapPoolEnabled && canAttemptUsap(args)) {
+ if (shouldAttemptUsapLaunch(zygotePolicyFlags, args)) {
try {
return attemptUsapSendArgsAndGetResult(zygoteState, msgStr);
} catch (IOException ex) {
@@ -488,7 +508,43 @@
}
/**
- * Flags that may not be passed to a USAP.
+ * Test various member properties and parameters to determine if a launch event should be
+ * handled using an Unspecialized App Process Pool or not.
+ *
+ * @param zygotePolicyFlags Policy flags indicating special behavioral observations about the
+ * Zygote command
+ * @param args Arguments that will be passed to the Zygote
+ * @return If the command should be sent to a USAP Pool member or an actual Zygote
+ */
+ private boolean shouldAttemptUsapLaunch(int zygotePolicyFlags, ArrayList<String> args) {
+ return mUsapPoolSupported
+ && mUsapPoolEnabled
+ && policySpecifiesUsapPoolLaunch(zygotePolicyFlags)
+ && commandSupportedByUsap(args);
+ }
+
+ /**
+ * Tests a Zygote policy flag set for various properties that determine if it is eligible for
+ * being handled by an Unspecialized App Process Pool.
+ *
+ * @param zygotePolicyFlags Policy flags indicating special behavioral observations about the
+ * Zygote command
+ * @return If the policy allows for use of a USAP pool
+ */
+ private static boolean policySpecifiesUsapPoolLaunch(int zygotePolicyFlags) {
+ /*
+ * Zygote USAP Pool Policy: Launch the new process from the USAP Pool iff the launch event
+ * is latency sensitive but *NOT* a system process. All system processes are equally
+ * important so we don't want to prioritize one over another.
+ */
+ return (zygotePolicyFlags
+ & (ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS | ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE))
+ == ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE;
+ }
+
+ /**
+ * Flags that may not be passed to a USAP. These may appear as prefixes to individual Zygote
+ * arguments.
*/
private static final String[] INVALID_USAP_FLAGS = {
"--query-abi-list",
@@ -505,10 +561,11 @@
/**
* Tests a command list to see if it is valid to send to a USAP.
+ *
* @param args Zygote/USAP command arguments
* @return True if the command can be passed to a USAP; false otherwise
*/
- private static boolean canAttemptUsap(ArrayList<String> args) {
+ private static boolean commandSupportedByUsap(ArrayList<String> args) {
for (String flag : args) {
for (String badFlag : INVALID_USAP_FLAGS) {
if (flag.startsWith(badFlag)) {
@@ -544,6 +601,7 @@
* @param startChildZygote Start a sub-zygote. This creates a new zygote process
* that has its state cloned from this zygote process.
* @param packageName null-ok the name of the package this process belongs to.
+ * @param zygotePolicyFlags Flags used to determine how to launch the application.
* @param isTopApp Whether the process starts for high priority application.
* @param disabledCompatChanges a list of disabled compat changes for the process being started.
* @param pkgDataInfoMap Map from related package names to private data directory volume UUID
@@ -565,7 +623,7 @@
@Nullable String invokeWith,
boolean startChildZygote,
@Nullable String packageName,
- boolean useUsapPool,
+ int zygotePolicyFlags,
boolean isTopApp,
@Nullable long[] disabledCompatChanges,
@Nullable Map<String, Pair<String, Long>>
@@ -692,7 +750,7 @@
// The USAP pool can not be used if the application will not use the systems graphics
// driver. If that driver is requested use the Zygote application start path.
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi),
- useUsapPool,
+ zygotePolicyFlags,
argsForZygote);
}
}
@@ -722,6 +780,10 @@
private long mLastPropCheckTimestamp = 0;
private boolean fetchUsapPoolEnabledPropWithMinInterval() {
+ // If this Zygote doesn't support USAPs there is no need to fetch any
+ // properties.
+ if (!mUsapPoolSupported) return false;
+
final long currentTimestamp = SystemClock.elapsedRealtime();
if (mIsFirstPropCheck
@@ -1219,7 +1281,7 @@
gids, runtimeFlags, 0 /* mountExternal */, 0 /* targetSdkVersion */, seInfo,
abi, instructionSet, null /* appDataDir */, null /* invokeWith */,
true /* startChildZygote */, null /* packageName */,
- false /* useUsapPool */, false /* isTopApp */,
+ ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS /* zygotePolicyFlags */, false /* isTopApp */,
null /* disabledCompatChanges */, null /* pkgDataInfoMap */, extraArgs);
} catch (ZygoteStartFailedEx ex) {
throw new RuntimeException("Starting child-zygote through Zygote failed", ex);
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 5d6dc7b..0bd211d 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -561,21 +561,24 @@
private static final class PackageNamePermissionQuery {
final String permName;
final String pkgName;
+ final int uid;
- PackageNamePermissionQuery(@Nullable String permName, @Nullable String pkgName) {
+ PackageNamePermissionQuery(@Nullable String permName, @Nullable String pkgName, int uid) {
this.permName = permName;
this.pkgName = pkgName;
+ this.uid = uid;
}
@Override
public String toString() {
- return String.format("PackageNamePermissionQuery(pkgName=\"%s\", permName=\"%s\")",
- pkgName, permName);
+ return String.format(
+ "PackageNamePermissionQuery(pkgName=\"%s\", permName=\"%s, uid=%s\")",
+ pkgName, permName, uid);
}
@Override
public int hashCode() {
- return Objects.hashCode(permName) * 13 + Objects.hashCode(pkgName);
+ return Objects.hash(permName, pkgName, uid);
}
@Override
@@ -590,15 +593,17 @@
return false;
}
return Objects.equals(permName, other.permName)
- && Objects.equals(pkgName, other.pkgName);
+ && Objects.equals(pkgName, other.pkgName)
+ && uid == other.uid;
}
}
/* @hide */
- private static int checkPackageNamePermissionUncached(String permName, String pkgName) {
+ private static int checkPackageNamePermissionUncached(
+ String permName, String pkgName, int uid) {
try {
return ActivityThread.getPermissionManager().checkPermission(
- permName, pkgName, UserHandle.myUserId());
+ permName, pkgName, uid);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -611,7 +616,8 @@
16, CACHE_KEY_PACKAGE_INFO) {
@Override
protected Integer recompute(PackageNamePermissionQuery query) {
- return checkPackageNamePermissionUncached(query.permName, query.pkgName);
+ return checkPackageNamePermissionUncached(
+ query.permName, query.pkgName, query.uid);
}
};
@@ -620,9 +626,9 @@
*
* @hide
*/
- public static int checkPackageNamePermission(String permName, String pkgName) {
+ public static int checkPackageNamePermission(String permName, String pkgName, int uid) {
return sPackageNamePermissionCache.query(
- new PackageNamePermissionQuery(permName, pkgName));
+ new PackageNamePermissionQuery(permName, pkgName, uid));
}
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f2d415a..fa2b014 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -53,7 +53,9 @@
import android.net.ConnectivityManager;
import android.net.NetworkScoreManager;
import android.net.Uri;
+import android.net.wifi.SoftApConfiguration;
import android.net.wifi.WifiManager;
+import android.net.wifi.p2p.WifiP2pManager;
import android.os.BatteryManager;
import android.os.Binder;
import android.os.Build.VERSION_CODES;
@@ -10272,7 +10274,8 @@
/**
* Setting to allow scans to be enabled even wifi is turned off for connectivity.
* @hide
- * @deprecated To be removed.
+ * @deprecated To be removed. Use {@link WifiManager#setScanAlwaysAvailable(boolean)} for
+ * setting the value and {@link WifiManager#isScanAlwaysAvailable()} for query.
*/
public static final String WIFI_SCAN_ALWAYS_AVAILABLE =
"wifi_scan_always_enabled";
@@ -10292,7 +10295,9 @@
*
* Type: int (0 for false, 1 for true)
* @hide
- * @deprecated To be removed.
+ * @deprecated To be removed. Use {@link SoftApConfiguration.Builder#
+ * setAutoShutdownEnabled(boolean)} for setting the value and {@link SoftApConfiguration#
+ * isAutoShutdownEnabled()} for query.
*/
public static final String SOFT_AP_TIMEOUT_ENABLED = "soft_ap_timeout_enabled";
@@ -10301,7 +10306,8 @@
*
* Type: int (0 for false, 1 for true)
* @hide
- * @deprecated Use {@link WifiManager#isAutoWakeupEnabled()} instead.
+ * @deprecated Use {@link WifiManager#setAutoWakeupEnabled(boolean)} for setting the value
+ * and {@link WifiManager#isAutoWakeupEnabled()} for query.
*/
@Deprecated
@SystemApi
@@ -10381,7 +10387,8 @@
*
* Type: int (0 for false, 1 for true)
* @hide
- * @deprecated To be removed.
+ * @deprecated Use {@link WifiManager#setScanThrottleEnabled(boolean)} for setting the value
+ * and {@link WifiManager#isScanThrottleEnabled()} for query.
*/
public static final String WIFI_SCAN_THROTTLE_ENABLED = "wifi_scan_throttle_enabled";
@@ -10484,7 +10491,8 @@
* Setting to enable verbose logging in Wi-Fi; disabled by default, and setting to 1
* will enable it. In the future, additional values may be supported.
* @hide
- * @deprecated To be removed.
+ * @deprecated Use {@link WifiManager#setVerboseLoggingEnabled(boolean)} for setting the
+ * value and {@link WifiManager#isVerboseLoggingEnabled()} for query.
*/
public static final String WIFI_VERBOSE_LOGGING_ENABLED =
"wifi_verbose_logging_enabled";
@@ -10553,7 +10561,9 @@
/**
* The Wi-Fi peer-to-peer device name
* @hide
- * @deprecated To be removed.
+ * @deprecated Use {@link WifiP2pManager#setDeviceName(WifiP2pManager.Channel, String,
+ * WifiP2pManager.ActionListener)} for setting the value and
+ * {@link android.net.wifi.p2p.WifiP2pDevice#deviceName} for query.
*/
public static final String WIFI_P2P_DEVICE_NAME = "wifi_p2p_device_name";
diff --git a/core/java/android/service/autofill/InlinePresentation.java b/core/java/android/service/autofill/InlinePresentation.java
index fb8406e..a9addba 100644
--- a/core/java/android/service/autofill/InlinePresentation.java
+++ b/core/java/android/service/autofill/InlinePresentation.java
@@ -17,6 +17,7 @@
package android.service.autofill;
import android.annotation.NonNull;
+import android.annotation.Size;
import android.app.slice.Slice;
import android.os.Parcel;
import android.os.Parcelable;
@@ -24,6 +25,8 @@
import com.android.internal.util.DataClass;
+import java.util.List;
+
/**
* Wrapper class holding a {@link Slice} and an {@link InlinePresentationSpec} for rendering UI
* for an Inline Suggestion.
@@ -50,6 +53,18 @@
*/
private final boolean mPinned;
+ /**
+ * Returns the autofill hints set in the slice.
+ *
+ * @hide
+ */
+ @NonNull
+ @Size(min = 0)
+ public String[] getAutofillHints() {
+ List<String> hints = mSlice.getHints();
+ return hints.toArray(new String[hints.size()]);
+ }
+
// Code below generated by codegen v1.0.14.
@@ -214,10 +229,10 @@
};
@DataClass.Generated(
- time = 1579726472535L,
+ time = 1582753782651L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/service/autofill/InlinePresentation.java",
- inputSignatures = "private final @android.annotation.NonNull android.app.slice.Slice mSlice\nprivate final @android.annotation.NonNull android.view.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final boolean mPinned\nclass InlinePresentation extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genEqualsHashCode=true)")
+ inputSignatures = "private final @android.annotation.NonNull android.app.slice.Slice mSlice\nprivate final @android.annotation.NonNull android.view.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final boolean mPinned\npublic @android.annotation.NonNull @android.annotation.Size(min=0L) java.lang.String[] getAutofillHints()\nclass InlinePresentation extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/service/autofill/InlineSuggestionRenderService.java b/core/java/android/service/autofill/InlineSuggestionRenderService.java
index 17e0456..fcdefac 100644
--- a/core/java/android/service/autofill/InlineSuggestionRenderService.java
+++ b/core/java/android/service/autofill/InlineSuggestionRenderService.java
@@ -23,16 +23,13 @@
import android.annotation.TestApi;
import android.app.Service;
import android.app.slice.Slice;
-import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
-import android.hardware.display.DisplayManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
-import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.SurfaceControl;
@@ -76,35 +73,41 @@
return;
}
- final DisplayManager displayManager = getSystemService(DisplayManager.class);
- final Display targetDisplay = displayManager.getDisplay(displayId);
- if (targetDisplay == null) {
- sendResult(callback, /*surface*/ null);
- return;
- }
- final Context displayContext = createDisplayContext(targetDisplay);
-
- final SurfaceControlViewHost host = new SurfaceControlViewHost(displayContext,
- displayContext.getDisplay(), hostInputToken);
- final SurfaceControl surface = host.getSurfacePackage().getSurfaceControl();
-
- final View suggestionView = onRenderSuggestion(presentation, width, height);
-
- final InlineSuggestionRoot suggestionRoot = new InlineSuggestionRoot(this, callback);
- suggestionRoot.addView(suggestionView);
- suggestionRoot.setOnClickListener((v) -> {
- try {
- callback.onAutofill();
- } catch (RemoteException e) {
- Log.w(TAG, "RemoteException calling onAutofill()");
+ // When we create the UI it should be for the IME display
+ updateDisplay(displayId);
+ try {
+ final View suggestionView = onRenderSuggestion(presentation, width, height);
+ if (suggestionView == null) {
+ try {
+ callback.onError();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Null suggestion view returned by renderer");
+ }
+ return;
}
- });
- WindowManager.LayoutParams lp =
- new WindowManager.LayoutParams(width, height,
- WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSPARENT);
- host.addView(suggestionRoot, lp);
- sendResult(callback, surface);
+ final InlineSuggestionRoot suggestionRoot = new InlineSuggestionRoot(this, callback);
+ suggestionRoot.addView(suggestionView);
+ WindowManager.LayoutParams lp =
+ new WindowManager.LayoutParams(width, height,
+ WindowManager.LayoutParams.TYPE_APPLICATION, 0,
+ PixelFormat.TRANSPARENT);
+
+ final SurfaceControlViewHost host = new SurfaceControlViewHost(this, getDisplay(),
+ hostInputToken);
+ host.addView(suggestionRoot, lp);
+ suggestionRoot.setOnClickListener((v) -> {
+ try {
+ callback.onAutofill();
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException calling onAutofill()");
+ }
+ });
+
+ sendResult(callback, host.getSurfacePackage().getSurfaceControl());
+ } finally {
+ updateDisplay(Display.DEFAULT_DISPLAY);
+ }
}
private void sendResult(@NonNull IInlineSuggestionUiCallback callback,
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index 672b501..fe792b1 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -40,6 +40,7 @@
import android.os.SystemClock;
import android.service.autofill.Dataset;
import android.service.autofill.FillEventHistory;
+import android.service.autofill.InlinePresentation;
import android.service.autofill.augmented.PresentationParams.SystemPopupPresentationParams;
import android.util.Log;
import android.util.Pair;
@@ -557,12 +558,10 @@
}
}
- void reportResult(@Nullable List<Dataset> inlineSuggestionsData) {
+ void reportResult(@Nullable List<Dataset> inlineSuggestionsData,
+ @Nullable List<InlinePresentation> inlineActions) {
try {
- final Dataset[] inlineSuggestions = (inlineSuggestionsData != null)
- ? inlineSuggestionsData.toArray(new Dataset[inlineSuggestionsData.size()])
- : null;
- mCallback.onSuccess(inlineSuggestions);
+ mCallback.onSuccess(inlineSuggestionsData, inlineActions);
} catch (RemoteException e) {
Log.e(TAG, "Error calling back with the inline suggestions data: " + e);
}
diff --git a/core/java/android/service/autofill/augmented/FillCallback.java b/core/java/android/service/autofill/augmented/FillCallback.java
index 19eff57..6b4e118 100644
--- a/core/java/android/service/autofill/augmented/FillCallback.java
+++ b/core/java/android/service/autofill/augmented/FillCallback.java
@@ -55,14 +55,14 @@
if (response == null) {
mProxy.logEvent(AutofillProxy.REPORT_EVENT_NO_RESPONSE);
- mProxy.reportResult(null /*inlineSuggestions*/);
+ mProxy.reportResult(/* inlineSuggestionsData */ null, /* inlineActions */null);
return;
}
List<Dataset> inlineSuggestions = response.getInlineSuggestions();
if (inlineSuggestions != null && !inlineSuggestions.isEmpty()) {
mProxy.logEvent(AutofillProxy.REPORT_EVENT_INLINE_RESPONSE);
- mProxy.reportResult(inlineSuggestions);
+ mProxy.reportResult(inlineSuggestions, response.getInlineActions());
return;
}
diff --git a/core/java/android/service/autofill/augmented/FillResponse.java b/core/java/android/service/autofill/augmented/FillResponse.java
index 68ba63a..b7fdf5a 100644
--- a/core/java/android/service/autofill/augmented/FillResponse.java
+++ b/core/java/android/service/autofill/augmented/FillResponse.java
@@ -21,6 +21,7 @@
import android.annotation.TestApi;
import android.os.Bundle;
import android.service.autofill.Dataset;
+import android.service.autofill.InlinePresentation;
import com.android.internal.util.DataClass;
@@ -52,6 +53,13 @@
private @Nullable List<Dataset> mInlineSuggestions;
/**
+ * The {@link InlinePresentation}s representing the inline actions. Defaults to null if no
+ * inline actions are provided.
+ */
+ @DataClass.PluralOf("inlineAction")
+ private @Nullable List<InlinePresentation> mInlineActions;
+
+ /**
* The client state that {@link AugmentedAutofillService} implementation can put anything in to
* identify the request and the response when calling
* {@link AugmentedAutofillService#getFillEventHistory()}.
@@ -66,6 +74,10 @@
return null;
}
+ private static List<InlinePresentation> defaultInlineActions() {
+ return null;
+ }
+
private static Bundle defaultClientState() {
return null;
}
@@ -74,6 +86,7 @@
/** @hide */
abstract static class BaseBuilder {
abstract FillResponse.Builder addInlineSuggestion(@NonNull Dataset value);
+ abstract FillResponse.Builder addInlineAction(@NonNull InlinePresentation value);
}
@@ -95,9 +108,11 @@
/* package-private */ FillResponse(
@Nullable FillWindow fillWindow,
@Nullable List<Dataset> inlineSuggestions,
+ @Nullable List<InlinePresentation> inlineActions,
@Nullable Bundle clientState) {
this.mFillWindow = fillWindow;
this.mInlineSuggestions = inlineSuggestions;
+ this.mInlineActions = inlineActions;
this.mClientState = clientState;
// onConstructed(); // You can define this method to get a callback
@@ -125,6 +140,17 @@
}
/**
+ * The {@link InlinePresentation}s representing the inline actions. Defaults to null if no
+ * inline actions are provided.
+ *
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public @Nullable List<InlinePresentation> getInlineActions() {
+ return mInlineActions;
+ }
+
+ /**
* The client state that {@link AugmentedAutofillService} implementation can put anything in to
* identify the request and the response when calling
* {@link AugmentedAutofillService#getFillEventHistory()}.
@@ -145,6 +171,7 @@
private @Nullable FillWindow mFillWindow;
private @Nullable List<Dataset> mInlineSuggestions;
+ private @Nullable List<InlinePresentation> mInlineActions;
private @Nullable Bundle mClientState;
private long mBuilderFieldsSet = 0L;
@@ -185,6 +212,27 @@
}
/**
+ * The {@link InlinePresentation}s representing the inline actions. Defaults to null if no
+ * inline actions are provided.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setInlineActions(@Nullable List<InlinePresentation> value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4;
+ mInlineActions = value;
+ return this;
+ }
+
+ /** @see #setInlineActions */
+ @DataClass.Generated.Member
+ @Override
+ @NonNull FillResponse.Builder addInlineAction(@NonNull InlinePresentation value) {
+ if (mInlineActions == null) setInlineActions(new ArrayList<>());
+ mInlineActions.add(value);
+ return this;
+ }
+
+ /**
* The client state that {@link AugmentedAutofillService} implementation can put anything in to
* identify the request and the response when calling
* {@link AugmentedAutofillService#getFillEventHistory()}.
@@ -192,7 +240,7 @@
@DataClass.Generated.Member
public @NonNull Builder setClientState(@Nullable Bundle value) {
checkNotUsed();
- mBuilderFieldsSet |= 0x4;
+ mBuilderFieldsSet |= 0x8;
mClientState = value;
return this;
}
@@ -200,7 +248,7 @@
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull FillResponse build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x8; // Mark builder used
+ mBuilderFieldsSet |= 0x10; // Mark builder used
if ((mBuilderFieldsSet & 0x1) == 0) {
mFillWindow = defaultFillWindow();
@@ -209,17 +257,21 @@
mInlineSuggestions = defaultInlineSuggestions();
}
if ((mBuilderFieldsSet & 0x4) == 0) {
+ mInlineActions = defaultInlineActions();
+ }
+ if ((mBuilderFieldsSet & 0x8) == 0) {
mClientState = defaultClientState();
}
FillResponse o = new FillResponse(
mFillWindow,
mInlineSuggestions,
+ mInlineActions,
mClientState);
return o;
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x8) != 0) {
+ if ((mBuilderFieldsSet & 0x10) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -227,10 +279,10 @@
}
@DataClass.Generated(
- time = 1580335256422L,
+ time = 1582682935951L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/service/autofill/augmented/FillResponse.java",
- inputSignatures = "private @android.annotation.Nullable android.service.autofill.augmented.FillWindow mFillWindow\nprivate @com.android.internal.util.DataClass.PluralOf(\"inlineSuggestion\") @android.annotation.Nullable java.util.List<android.service.autofill.Dataset> mInlineSuggestions\nprivate @android.annotation.Nullable android.os.Bundle mClientState\nprivate static android.service.autofill.augmented.FillWindow defaultFillWindow()\nprivate static java.util.List<android.service.autofill.Dataset> defaultInlineSuggestions()\nprivate static android.os.Bundle defaultClientState()\nclass FillResponse extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true, genHiddenGetters=true)\nabstract android.service.autofill.augmented.FillResponse.Builder addInlineSuggestion(android.service.autofill.Dataset)\nclass BaseBuilder extends java.lang.Object implements []")
+ inputSignatures = "private @android.annotation.Nullable android.service.autofill.augmented.FillWindow mFillWindow\nprivate @com.android.internal.util.DataClass.PluralOf(\"inlineSuggestion\") @android.annotation.Nullable java.util.List<android.service.autofill.Dataset> mInlineSuggestions\nprivate @com.android.internal.util.DataClass.PluralOf(\"inlineAction\") @android.annotation.Nullable java.util.List<android.service.autofill.InlinePresentation> mInlineActions\nprivate @android.annotation.Nullable android.os.Bundle mClientState\nprivate static android.service.autofill.augmented.FillWindow defaultFillWindow()\nprivate static java.util.List<android.service.autofill.Dataset> defaultInlineSuggestions()\nprivate static java.util.List<android.service.autofill.InlinePresentation> defaultInlineActions()\nprivate static android.os.Bundle defaultClientState()\nclass FillResponse extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true, genHiddenGetters=true)\nabstract android.service.autofill.augmented.FillResponse.Builder addInlineSuggestion(android.service.autofill.Dataset)\nabstract android.service.autofill.augmented.FillResponse.Builder addInlineAction(android.service.autofill.InlinePresentation)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/service/autofill/augmented/IFillCallback.aidl b/core/java/android/service/autofill/augmented/IFillCallback.aidl
index d983721..bf0adcd 100644
--- a/core/java/android/service/autofill/augmented/IFillCallback.aidl
+++ b/core/java/android/service/autofill/augmented/IFillCallback.aidl
@@ -20,6 +20,9 @@
import android.os.ICancellationSignal;
import android.service.autofill.Dataset;
+import android.service.autofill.InlinePresentation;
+
+import java.util.List;
/**
* Interface to receive the result of an autofill request.
@@ -28,7 +31,8 @@
*/
interface IFillCallback {
void onCancellable(in ICancellationSignal cancellation);
- void onSuccess(in @nullable Dataset[] inlineSuggestionsData);
+ void onSuccess(in @nullable List<Dataset> inlineSuggestionsData,
+ in @nullable List<InlinePresentation> inlineActions);
boolean isCompleted();
void cancel();
}
diff --git a/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java b/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java
index de90b94..2a809b1 100644
--- a/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java
+++ b/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -47,7 +46,6 @@
* CarrierMessagingService.
* @hide
*/
-@SystemApi
public abstract class CarrierMessagingServiceWrapper {
// Populated by bindToCarrierMessagingService. bindToCarrierMessagingService must complete
// prior to calling disposeConnection so that mCarrierMessagingServiceConnection is initialized.
@@ -64,7 +62,6 @@
* @return true upon successfully binding to a carrier messaging service, false otherwise
* @hide
*/
- @SystemApi
public boolean bindToCarrierMessagingService(@NonNull Context context,
@NonNull String carrierPackageName) {
Preconditions.checkState(mCarrierMessagingServiceConnection == null);
@@ -82,7 +79,6 @@
* @param context the context
* @hide
*/
- @SystemApi
public void disposeConnection(@NonNull Context context) {
Preconditions.checkNotNull(mCarrierMessagingServiceConnection);
context.unbindService(mCarrierMessagingServiceConnection);
@@ -93,7 +89,6 @@
* Implemented by subclasses to use the carrier messaging service once it is ready.
* @hide
*/
- @SystemApi
public abstract void onServiceReady();
/**
@@ -117,7 +112,6 @@
* @param callback the callback to notify upon completion
* @hide
*/
- @SystemApi
public void filterSms(@NonNull MessagePdu pdu, @NonNull String format, int destPort,
int subId, @NonNull final CarrierMessagingCallbackWrapper callback) {
if (mICarrierMessagingService != null) {
@@ -142,7 +136,6 @@
* @param callback the callback to notify upon completion
* @hide
*/
- @SystemApi
public void sendTextSms(@NonNull String text, int subId, @NonNull String destAddress,
int sendSmsFlag, @NonNull final CarrierMessagingCallbackWrapper callback) {
if (mICarrierMessagingService != null) {
@@ -168,7 +161,6 @@
* @param callback the callback to notify upon completion
* @hide
*/
- @SystemApi
public void sendDataSms(@NonNull byte[] data, int subId, @NonNull String destAddress,
int destPort, int sendSmsFlag,
@NonNull final CarrierMessagingCallbackWrapper callback) {
@@ -194,7 +186,6 @@
* @param callback the callback to notify upon completion
* @hide
*/
- @SystemApi
public void sendMultipartTextSms(@NonNull List<String> parts, int subId,
@NonNull String destAddress, int sendSmsFlag,
@NonNull final CarrierMessagingCallbackWrapper callback) {
@@ -220,7 +211,6 @@
* @param callback the callback to notify upon completion
* @hide
*/
- @SystemApi
public void sendMms(@NonNull Uri pduUri, int subId, @NonNull Uri location,
@NonNull final CarrierMessagingCallbackWrapper callback) {
if (mICarrierMessagingService != null) {
@@ -244,7 +234,6 @@
* @param callback the callback to notify upon completion
* @hide
*/
- @SystemApi
public void downloadMms(@NonNull Uri pduUri, int subId, @NonNull Uri location,
@NonNull final CarrierMessagingCallbackWrapper callback) {
if (mICarrierMessagingService != null) {
@@ -276,7 +265,6 @@
* {@link CarrierMessagingServiceWrapper}.
* @hide
*/
- @SystemApi
public abstract static class CarrierMessagingCallbackWrapper {
/**
@@ -289,7 +277,6 @@
* {@see CarrierMessagingService#onReceiveTextSms}.
* @hide
*/
- @SystemApi
public void onFilterComplete(int result) {
}
@@ -304,7 +291,6 @@
* only if result is {@link CarrierMessagingService#SEND_STATUS_OK}.
* @hide
*/
- @SystemApi
public void onSendSmsComplete(int result, int messageRef) {
}
@@ -319,7 +305,6 @@
* {@link CarrierMessagingService#SEND_STATUS_OK}.
* @hide
*/
- @SystemApi
public void onSendMultipartSmsComplete(int result, @Nullable int[] messageRefs) {
}
@@ -334,7 +319,6 @@
* {@link CarrierMessagingService#SEND_STATUS_OK}.
* @hide
*/
- @SystemApi
public void onSendMmsComplete(int result, @Nullable byte[] sendConfPdu) {
}
@@ -346,7 +330,6 @@
* and {@link CarrierMessagingService#SEND_STATUS_ERROR}.
* @hide
*/
- @SystemApi
public void onDownloadMmsComplete(int result) {
}
diff --git a/core/java/android/service/controls/Control.java b/core/java/android/service/controls/Control.java
index 2d1d0ed..dabd977 100644
--- a/core/java/android/service/controls/Control.java
+++ b/core/java/android/service/controls/Control.java
@@ -209,61 +209,116 @@
mStatusText = in.readCharSequence();
}
+ /**
+ * @return the identifier for the {@link Control}
+ */
@NonNull
public String getControlId() {
return mControlId;
}
+
+ /**
+ * @return type of device represented by this {@link Control}, used to determine the default
+ * icon and color
+ */
@DeviceTypes.DeviceType
public int getDeviceType() {
return mDeviceType;
}
+ /**
+ * @return the user facing name of the {@link Control}
+ */
@NonNull
public CharSequence getTitle() {
return mTitle;
}
+ /**
+ * @return additional information about the {@link Control}, to appear underneath the title
+ */
@NonNull
public CharSequence getSubtitle() {
return mSubtitle;
}
+ /**
+ * Optional top-level group to help define the {@link Control}'s location, visible to the user.
+ * If not present, the application name will be used as the top-level group. A structure
+ * contains zones which contains controls.
+ *
+ * @return name of the structure containing the control
+ */
@Nullable
public CharSequence getStructure() {
return mStructure;
}
+ /**
+ * Optional group name to help define the {@link Control}'s location within a structure,
+ * visible to the user. A structure contains zones which contains controls.
+ *
+ * @return name of the zone containing the control
+ */
@Nullable
public CharSequence getZone() {
return mZone;
}
+ /**
+ * @return a {@link PendingIntent} linking to an Activity for the {@link Control}
+ */
@NonNull
public PendingIntent getAppIntent() {
return mAppIntent;
}
+ /**
+ * Optional icon to be shown with the {@link Control}. It is highly recommended
+ * to let the system default the icon unless the default icon is not suitable.
+ *
+ * @return icon to show
+ */
@Nullable
public Icon getCustomIcon() {
return mCustomIcon;
}
+ /**
+ * Optional color to be shown with the {@link Control}. It is highly recommended
+ * to let the system default the color unless the default is not suitable for the
+ * application.
+ *
+ * @return background color to use
+ */
@Nullable
public ColorStateList getCustomColor() {
return mCustomColor;
}
+ /**
+ * @return status of the {@link Control}, used to convey information about the attempt to
+ * fetch the current state
+ */
@Status
public int getStatus() {
return mStatus;
}
+ /**
+ * @return instance of {@link ControlTemplate}, that defines how the {@link Control} will
+ * behave and what interactions are available to the user
+ */
@NonNull
public ControlTemplate getControlTemplate() {
return mControlTemplate;
}
+ /**
+ * @return user-facing text description of the {@link Control}'s status, describing its current
+ * state
+ */
@NonNull
public CharSequence getStatusText() {
return mStatusText;
@@ -326,7 +381,10 @@
/**
* Builder class for {@link Control}.
*
- * This class facilitates the creation of {@link Control} with no state.
+ * This class facilitates the creation of {@link Control} with no state. Must be used to
+ * provide controls for {@link ControlsProviderService#createPublisherForAllAvailable} and
+ * {@link ControlsProviderService#createPublisherForSuggested}.
+ *
* It provides the following defaults for non-optional parameters:
* <ul>
* <li> Device type: {@link DeviceTypes#TYPE_UNKNOWN}
@@ -334,7 +392,7 @@
* <li> Subtitle: {@code ""}
* </ul>
* This fixes the values relating to state of the {@link Control} as required by
- * {@link ControlsProviderService#loadAvailableControls}:
+ * {@link ControlsProviderService#createPublisherForAllAvailable}:
* <ul>
* <li> Status: {@link Status#STATUS_UNKNOWN}
* <li> Control template: {@link ControlTemplate#NO_TEMPLATE}
@@ -355,8 +413,8 @@
private @Nullable ColorStateList mCustomColor;
/**
- * @param controlId the identifier for the {@link Control}.
- * @param appIntent the pending intent linking to the device Activity.
+ * @param controlId the identifier for the {@link Control}
+ * @param appIntent the pending intent linking to the device Activity
*/
public StatelessBuilder(@NonNull String controlId,
@NonNull PendingIntent appIntent) {
@@ -368,6 +426,7 @@
/**
* Creates a {@link StatelessBuilder} using an existing {@link Control} as a base.
+ *
* @param control base for the builder.
*/
public StatelessBuilder(@NonNull Control control) {
@@ -384,7 +443,7 @@
}
/**
- * @param controlId the identifier for the {@link Control}.
+ * @param controlId the identifier for the {@link Control}
* @return {@code this}
*/
@NonNull
@@ -394,6 +453,11 @@
return this;
}
+ /**
+ * @param deviceType type of device represented by this {@link Control}, used to
+ * determine the default icon and color
+ * @return {@code this}
+ */
@NonNull
public StatelessBuilder setDeviceType(@DeviceTypes.DeviceType int deviceType) {
if (!DeviceTypes.validDeviceType(deviceType)) {
@@ -416,6 +480,11 @@
return this;
}
+ /**
+ * @param subtitle additional information about the {@link Control}, to appear underneath
+ * the title
+ * @return {@code this}
+ */
@NonNull
public StatelessBuilder setSubtitle(@NonNull CharSequence subtitle) {
Preconditions.checkNotNull(subtitle);
@@ -423,12 +492,27 @@
return this;
}
+ /**
+ * Optional top-level group to help define the {@link Control}'s location, visible to the
+ * user. If not present, the application name will be used as the top-level group. A
+ * structure contains zones which contains controls.
+ *
+ * @param structure name of the structure containing the control
+ * @return {@code this}
+ */
@NonNull
public StatelessBuilder setStructure(@Nullable CharSequence structure) {
mStructure = structure;
return this;
}
+ /**
+ * Optional group name to help define the {@link Control}'s location within a structure,
+ * visible to the user. A structure contains zones which contains controls.
+ *
+ * @param zone name of the zone containing the control
+ * @return {@code this}
+ */
@NonNull
public StatelessBuilder setZone(@Nullable CharSequence zone) {
mZone = zone;
@@ -436,7 +520,7 @@
}
/**
- * @param appIntent an {@link Intent} linking to an Activity for the {@link Control}
+ * @param appIntent a {@link PendingIntent} linking to an Activity for the {@link Control}
* @return {@code this}
*/
@NonNull
@@ -446,12 +530,27 @@
return this;
}
+ /**
+ * Optional icon to be shown with the {@link Control}. It is highly recommended
+ * to let the system default the icon unless the default icon is not suitable.
+ *
+ * @param customIcon icon to show
+ * @return {@code this}
+ */
@NonNull
public StatelessBuilder setCustomIcon(@Nullable Icon customIcon) {
mCustomIcon = customIcon;
return this;
}
+ /**
+ * Optional color to be shown with the {@link Control}. It is highly recommended
+ * to let the system default the color unless the default is not suitable for the
+ * application.
+ *
+ * @param customColor background color to use
+ * @return {@code this}
+ */
@NonNull
public StatelessBuilder setCustomColor(@Nullable ColorStateList customColor) {
mCustomColor = customColor;
@@ -459,7 +558,6 @@
}
/**
- * Build a {@link Control}
* @return a valid {@link Control}
*/
@NonNull
@@ -480,9 +578,15 @@
}
/**
- * Builder class for {@link Control}.
+ * Builder class for {@link Control} that contains state information.
*
- * This class facilitates the creation of {@link Control}.
+ * State information is passed through an instance of a {@link ControlTemplate} and will
+ * determine how the user can interact with the {@link Control}. User interactions will
+ * be sent through the method call {@link ControlsProviderService#performControlAction}
+ * with an instance of {@link ControlAction} to convey any potential new value.
+ *
+ * Must be used to provide controls for {@link ControlsProviderService#createPublisherFor}.
+ *
* It provides the following defaults for non-optional parameters:
* <ul>
* <li> Device type: {@link DeviceTypes#TYPE_UNKNOWN}
@@ -522,6 +626,7 @@
/**
* Creates a {@link StatelessBuilder} using an existing {@link Control} as a base.
+ *
* @param control base for the builder.
*/
public StatefulBuilder(@NonNull Control control) {
@@ -551,6 +656,11 @@
return this;
}
+ /**
+ * @param deviceType type of device represented by this {@link Control}, used to
+ * determine the default icon and color
+ * @return {@code this}
+ */
@NonNull
public StatefulBuilder setDeviceType(@DeviceTypes.DeviceType int deviceType) {
if (!DeviceTypes.validDeviceType(deviceType)) {
@@ -573,6 +683,11 @@
return this;
}
+ /**
+ * @param subtitle additional information about the {@link Control}, to appear underneath
+ * the title
+ * @return {@code this}
+ */
@NonNull
public StatefulBuilder setSubtitle(@NonNull CharSequence subtitle) {
Preconditions.checkNotNull(subtitle);
@@ -580,12 +695,27 @@
return this;
}
+ /**
+ * Optional top-level group to help define the {@link Control}'s location, visible to the
+ * user. If not present, the application name will be used as the top-level group. A
+ * structure contains zones which contains controls.
+ *
+ * @param structure name of the structure containing the control
+ * @return {@code this}
+ */
@NonNull
public StatefulBuilder setStructure(@Nullable CharSequence structure) {
mStructure = structure;
return this;
}
+ /**
+ * Optional group name to help define the {@link Control}'s location within a structure,
+ * visible to the user. A structure contains zones which contains controls.
+ *
+ * @param zone name of the zone containing the control
+ * @return {@code this}
+ */
@NonNull
public StatefulBuilder setZone(@Nullable CharSequence zone) {
mZone = zone;
@@ -593,7 +723,7 @@
}
/**
- * @param appIntent an {@link Intent} linking to an Activity for the {@link Control}
+ * @param appIntent a {@link PendingIntent} linking to an Activity for the {@link Control}
* @return {@code this}
*/
@NonNull
@@ -603,18 +733,38 @@
return this;
}
+ /**
+ * Optional icon to be shown with the {@link Control}. It is highly recommended
+ * to let the system default the icon unless the default icon is not suitable.
+ *
+ * @param customIcon icon to show
+ * @return {@code this}
+ */
@NonNull
public StatefulBuilder setCustomIcon(@Nullable Icon customIcon) {
mCustomIcon = customIcon;
return this;
}
+ /**
+ * Optional color to be shown with the {@link Control}. It is highly recommended
+ * to let the system default the color unless the default is not suitable for the
+ * application.
+ *
+ * @param customColor background color to use
+ * @return {@code this}
+ */
@NonNull
public StatefulBuilder setCustomColor(@Nullable ColorStateList customColor) {
mCustomColor = customColor;
return this;
}
+ /**
+ * @param status status of the {@link Control}, used to convey information about the
+ * attempt to fetch the current state
+ * @return {@code this}
+ */
@NonNull
public StatefulBuilder setStatus(@Status int status) {
if (status < 0 || status >= NUM_STATUS) {
@@ -626,6 +776,12 @@
return this;
}
+ /**
+ * @param controlTemplate instance of {@link ControlTemplate}, that defines how the
+ * {@link Control} will behave and what interactions are
+ * available to the user
+ * @return {@code this}
+ */
@NonNull
public StatefulBuilder setControlTemplate(@NonNull ControlTemplate controlTemplate) {
Preconditions.checkNotNull(controlTemplate);
@@ -633,6 +789,11 @@
return this;
}
+ /**
+ * @param statusText user-facing text description of the {@link Control}'s status,
+ * describing its current state
+ * @return {@code this}
+ */
@NonNull
public StatefulBuilder setStatusText(@NonNull CharSequence statusText) {
Preconditions.checkNotNull(statusText);
@@ -640,6 +801,9 @@
return this;
}
+ /**
+ * @return a valid {@link Control}
+ */
@NonNull
public Control build() {
return new Control(mControlId,
diff --git a/core/java/android/service/controls/ControlsProviderService.java b/core/java/android/service/controls/ControlsProviderService.java
index b23d0cd..9debb37 100644
--- a/core/java/android/service/controls/ControlsProviderService.java
+++ b/core/java/android/service/controls/ControlsProviderService.java
@@ -38,7 +38,6 @@
import com.android.internal.util.Preconditions;
-import java.util.Collections;
import java.util.List;
import java.util.concurrent.Flow.Publisher;
import java.util.concurrent.Flow.Subscriber;
@@ -84,19 +83,6 @@
private RequestHandler mHandler;
/**
- * Retrieve all available controls, using the stateless builder
- * {@link Control.StatelessBuilder} to build each Control, then use the
- * provided consumer to callback to the call originator.
- *
- * @deprecated Removing consumer-based load apis. Use publisherForAllAvailable() instead
- */
- @Deprecated
- public void loadAvailableControls(@NonNull Consumer<List<Control>> consumer) {
- // pending removal
- consumer.accept(Collections.emptyList());
- }
-
- /**
* Publisher for all available controls
*
* Retrieve all available controls. Use the stateless builder {@link Control.StatelessBuilder}
@@ -104,11 +90,8 @@
* controls, or {@link Subscriber#onError} for error scenarios. Duplicate Controls will
* replace the original.
*/
- @Nullable
- public Publisher<Control> publisherForAllAvailable() {
- // will be abstract and @nonnull when consumers are removed
- return null;
- }
+ @NonNull
+ public abstract Publisher<Control> createPublisherForAllAvailable();
/**
* (Optional) Publisher for suggested controls
@@ -120,7 +103,7 @@
* when done, or {@link Subscriber#onError} for error scenarios.
*/
@Nullable
- public Publisher<Control> publisherForSuggested() {
+ public Publisher<Control> createPublisherForSuggested() {
return null;
}
@@ -128,10 +111,11 @@
* Return a valid Publisher for the given controlIds. This publisher will be asked to provide
* updates for the given list of controlIds as long as the {@link Subscription} is valid.
* Calls to {@link Subscriber#onComplete} will not be expected. Instead, wait for the call from
- * {@link Subscription#cancel} to indicate that updates are no longer required.
+ * {@link Subscription#cancel} to indicate that updates are no longer required. It is expected
+ * that controls provided by this publisher were created using {@link Control.StatefulBuilder}.
*/
@NonNull
- public abstract Publisher<Control> publisherFor(@NonNull List<String> controlIds);
+ public abstract Publisher<Control> createPublisherFor(@NonNull List<String> controlIds);
/**
* The user has interacted with a Control. The action is dictated by the type of
@@ -177,7 +161,7 @@
}
@Override
- public boolean onUnbind(@NonNull Intent intent) {
+ public final boolean onUnbind(@NonNull Intent intent) {
mHandler = null;
return true;
}
@@ -198,13 +182,7 @@
final IControlsSubscriber cs = (IControlsSubscriber) msg.obj;
final SubscriberProxy proxy = new SubscriberProxy(true, mToken, cs);
- Publisher<Control> publisher =
- ControlsProviderService.this.publisherForAllAvailable();
- if (publisher == null) {
- ControlsProviderService.this.loadAvailableControls(consumerFor(proxy));
- } else {
- publisher.subscribe(proxy);
- }
+ ControlsProviderService.this.createPublisherForAllAvailable().subscribe(proxy);
break;
}
@@ -213,7 +191,7 @@
final SubscriberProxy proxy = new SubscriberProxy(true, mToken, cs);
Publisher<Control> publisher =
- ControlsProviderService.this.publisherForSuggested();
+ ControlsProviderService.this.createPublisherForSuggested();
if (publisher == null) {
Log.i(TAG, "No publisher provided for suggested controls");
proxy.onComplete();
@@ -228,7 +206,8 @@
final SubscriberProxy proxy = new SubscriberProxy(false, mToken,
sMsg.mSubscriber);
- ControlsProviderService.this.publisherFor(sMsg.mControlIds).subscribe(proxy);
+ ControlsProviderService.this.createPublisherFor(sMsg.mControlIds)
+ .subscribe(proxy);
break;
}
@@ -256,37 +235,6 @@
}
};
}
-
- /**
- * Method will be removed during migration to publisher
- */
- private Consumer<List<Control>> consumerFor(final Subscriber<Control> subscriber) {
- return (@NonNull final List<Control> controls) -> {
- Preconditions.checkNotNull(controls);
-
- subscriber.onSubscribe(new Subscription() {
- public void request(long n) {
- for (Control control: controls) {
- Control c;
- if (control == null) {
- Log.e(TAG, "onLoad: null control.");
- }
- if (isStatelessControl(control)) {
- c = control;
- } else {
- Log.w(TAG, "onLoad: control is not stateless.");
- c = new Control.StatelessBuilder(control).build();
- }
-
- subscriber.onNext(c);
- }
- subscriber.onComplete();
- }
-
- public void cancel() {}
- });
- };
- }
}
private static boolean isStatelessControl(Control control) {
diff --git a/core/java/android/service/controls/DeviceTypes.java b/core/java/android/service/controls/DeviceTypes.java
index 8dbb9cf..6594d2c 100644
--- a/core/java/android/service/controls/DeviceTypes.java
+++ b/core/java/android/service/controls/DeviceTypes.java
@@ -21,6 +21,9 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+/**
+ * Device types for {@link Control}.
+ */
public class DeviceTypes {
// Update this when adding new concrete types. Does not count TYPE_UNKNOWN
diff --git a/core/java/android/service/controls/actions/BooleanAction.java b/core/java/android/service/controls/actions/BooleanAction.java
index 0259335..b794ead 100644
--- a/core/java/android/service/controls/actions/BooleanAction.java
+++ b/core/java/android/service/controls/actions/BooleanAction.java
@@ -19,10 +19,15 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
+import android.service.controls.Control;
+import android.service.controls.templates.ToggleRangeTemplate;
import android.service.controls.templates.ToggleTemplate;
/**
- * Action sent by a {@link ToggleTemplate}
+ * Action sent by user toggling a {@link Control} between checked/unchecked.
+ *
+ * This action is available when the {@link Control} was constructed with either a
+ * {@link ToggleTemplate} or a {@link ToggleRangeTemplate}.
*/
public final class BooleanAction extends ControlAction {
@@ -40,8 +45,8 @@
}
/**
- * @param templateId the identifier of the {@link ToggleTemplate} that originated this action.
- * @param newState new value for the state displayed by the {@link ToggleTemplate}.
+ * @param templateId the identifier of the template that originated this action.
+ * @param newState new value for the state displayed by the template.
* @param challengeValue a value sent by the user along with the action to authenticate. {@code}
* null is sent when no authentication is needed or has not been
* requested.
@@ -64,8 +69,7 @@
/**
* The new state set for the button in the corresponding {@link ToggleTemplate}.
*
- * @return {@code true} if the button was toggled from an {@code off} state to an {@code on}
- * state.
+ * @return {@code true} if the button was toggled from unchecked to checked.
*/
public boolean getNewState() {
return mNewState;
diff --git a/core/java/android/service/controls/actions/CommandAction.java b/core/java/android/service/controls/actions/CommandAction.java
index 84d6080..a560fa4 100644
--- a/core/java/android/service/controls/actions/CommandAction.java
+++ b/core/java/android/service/controls/actions/CommandAction.java
@@ -19,15 +19,32 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
+import android.service.controls.Control;
+import android.service.controls.templates.StatelessTemplate;
+/**
+ * A simple {@link ControlAction} indicating that the user has interacted with a {@link Control}
+ * created using a {@link StatelessTemplate}.
+ */
public final class CommandAction extends ControlAction {
private static final @ActionType int TYPE = TYPE_COMMAND;
+ /**
+ * @param templateId the identifier of the {@link StatelessTemplate} that originated this
+ * action.
+ * @param challengeValue a value sent by the user along with the action to authenticate. {@code}
+ * null is sent when no authentication is needed or has not been
+ * requested.
+ */
public CommandAction(@NonNull String templateId, @Nullable String challengeValue) {
super(templateId, challengeValue);
}
+ /**
+ * @param templateId the identifier of the {@link StatelessTemplate} that originated this
+ * action.
+ */
public CommandAction(@NonNull String templateId) {
this(templateId, null);
}
@@ -40,6 +57,9 @@
super(b);
}
+ /**
+ * @return {@link ControlAction#TYPE_COMMAND}
+ */
@Override
public int getActionType() {
return TYPE;
diff --git a/core/java/android/service/controls/actions/ControlAction.java b/core/java/android/service/controls/actions/ControlAction.java
index 4141da8..45e63d7 100644
--- a/core/java/android/service/controls/actions/ControlAction.java
+++ b/core/java/android/service/controls/actions/ControlAction.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
+import android.service.controls.Control;
import android.service.controls.IControlsActionCallback;
import android.service.controls.templates.ControlTemplate;
import android.util.Log;
@@ -31,7 +32,7 @@
import java.lang.annotation.RetentionPolicy;
/**
- * An abstract action that is executed from a {@link ControlTemplate}.
+ * An abstract action indicating a user interaction with a {@link Control}.
*
* The action may have a value to authenticate the input, when the provider has requested it to
* complete the action.
@@ -58,6 +59,9 @@
})
public @interface ActionType {};
+ /**
+ * Object returned when there is an unparcelling error.
+ */
public static final @NonNull ControlAction ERROR_ACTION = new ControlAction() {
@Override
public int getActionType() {
@@ -65,6 +69,9 @@
}
};
+ /**
+ * The identifier of {@link #ERROR_ACTION}
+ */
public static final @ActionType int TYPE_ERROR = -1;
/**
@@ -77,10 +84,19 @@
*/
public static final @ActionType int TYPE_FLOAT = 2;
+ /**
+ * The identifier of {@link MultiFloatAction}.
+ */
public static final @ActionType int TYPE_MULTI_FLOAT = 3;
+ /**
+ * The identifier of {@link ModeAction}.
+ */
public static final @ActionType int TYPE_MODE = 4;
+ /**
+ * The identifier of {@link CommandAction}.
+ */
public static final @ActionType int TYPE_COMMAND = 5;
diff --git a/core/java/android/service/controls/actions/ModeAction.java b/core/java/android/service/controls/actions/ModeAction.java
index ca40974..c0e24ad 100644
--- a/core/java/android/service/controls/actions/ModeAction.java
+++ b/core/java/android/service/controls/actions/ModeAction.java
@@ -19,7 +19,15 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
+import android.service.controls.Control;
+import android.service.controls.templates.TemperatureControlTemplate;
+/**
+ * Action sent by the user to indicate a change of mode.
+ *
+ * This action is available when the {@link Control} was created with a
+ * {@link TemperatureControlTemplate}.
+ */
public final class ModeAction extends ControlAction {
private static final @ActionType int TYPE = TYPE_MODE;
@@ -27,16 +35,32 @@
private final int mNewMode;
+ /**
+ * @return {@link ControlAction#TYPE_MODE}.
+ */
@Override
public int getActionType() {
return TYPE;
}
+ /**
+ * @param templateId the identifier of the {@link TemperatureControlTemplate} that originated
+ * this action.
+ * @param newMode new value for the mode.
+ * @param challengeValue a value sent by the user along with the action to authenticate. {@code}
+ * null is sent when no authentication is needed or has not been
+ * requested.
+ */
public ModeAction(@NonNull String templateId, int newMode, @Nullable String challengeValue) {
super(templateId, challengeValue);
mNewMode = newMode;
}
+ /**
+ * @param templateId the identifier of the {@link TemperatureControlTemplate} that originated
+ * this action.
+ * @param newMode new value for the mode.
+ */
public ModeAction(@NonNull String templateId, int newMode) {
this(templateId, newMode, null);
}
diff --git a/core/java/android/service/controls/templates/ControlTemplate.java b/core/java/android/service/controls/templates/ControlTemplate.java
index a5156e3..30efd80 100644
--- a/core/java/android/service/controls/templates/ControlTemplate.java
+++ b/core/java/android/service/controls/templates/ControlTemplate.java
@@ -57,6 +57,9 @@
}
};
+ /**
+ * Object returned when there is an unparcelling error.
+ */
public static final @NonNull ControlTemplate ERROR_TEMPLATE = new ControlTemplate("") {
@Override
public int getTemplateType() {
@@ -80,6 +83,9 @@
})
public @interface TemplateType {}
+ /**
+ * Type identifier of {@link #ERROR_TEMPLATE}.
+ */
public static final @TemplateType int TYPE_ERROR = -1;
/**
@@ -102,10 +108,19 @@
*/
public static final @TemplateType int TYPE_THUMBNAIL = 3;
+ /**
+ * Type identifier of {@link ToggleRangeTemplate}.
+ */
public static final @TemplateType int TYPE_TOGGLE_RANGE = 6;
+ /**
+ * Type identifier of {@link TemperatureControlTemplate}.
+ */
public static final @TemplateType int TYPE_TEMPERATURE = 7;
+ /**
+ * Type identifier of {@link StatelessTemplate}.
+ */
public static final @TemplateType int TYPE_STATELESS = 8;
private @NonNull final String mTemplateId;
diff --git a/core/java/android/service/controls/templates/RangeTemplate.java b/core/java/android/service/controls/templates/RangeTemplate.java
index fe0d167..0d977d3 100644
--- a/core/java/android/service/controls/templates/RangeTemplate.java
+++ b/core/java/android/service/controls/templates/RangeTemplate.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
-import android.os.Parcel;
import android.service.controls.Control;
import android.service.controls.actions.FloatAction;
@@ -85,7 +84,7 @@
}
/**
- * Construct a new {@link RangeTemplate} from a {@link Parcel}.
+ * Construct a new {@link RangeTemplate} from a {@link Bundle}.
*
* @throws IllegalArgumentException if the parameters passed do not make a valid range
* @see RangeTemplate#RangeTemplate(String, float, float, float, float, CharSequence)
diff --git a/core/java/android/service/controls/templates/StatelessTemplate.java b/core/java/android/service/controls/templates/StatelessTemplate.java
index 3f98bea..c052412 100644
--- a/core/java/android/service/controls/templates/StatelessTemplate.java
+++ b/core/java/android/service/controls/templates/StatelessTemplate.java
@@ -18,22 +18,36 @@
import android.annotation.NonNull;
import android.os.Bundle;
+import android.service.controls.Control;
+import android.service.controls.actions.CommandAction;
+/**
+ * A template for a {@link Control} which has no state.
+ *
+ * @see CommandAction
+ */
public final class StatelessTemplate extends ControlTemplate {
+ /**
+ * @return {@link ControlTemplate#TYPE_STATELESS}
+ */
@Override
public int getTemplateType() {
return TYPE_STATELESS;
}
/**
- * @param b
+ * Construct a new {@link StatelessTemplate} from a {@link Bundle}
* @hide
*/
StatelessTemplate(@NonNull Bundle b) {
super(b);
}
+ /**
+ * Construct a new {@link StatelessTemplate}
+ * @param templateId the identifier for this template
+ */
public StatelessTemplate(@NonNull String templateId) {
super(templateId);
}
diff --git a/core/java/android/service/controls/templates/TemperatureControlTemplate.java b/core/java/android/service/controls/templates/TemperatureControlTemplate.java
index 9d8dca62..0818c7e 100644
--- a/core/java/android/service/controls/templates/TemperatureControlTemplate.java
+++ b/core/java/android/service/controls/templates/TemperatureControlTemplate.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.os.Bundle;
+import android.service.controls.Control;
import android.util.Log;
import com.android.internal.util.Preconditions;
@@ -26,6 +27,13 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+/**
+ * A template for a temperature related {@link Control} that supports multiple modes.
+ *
+ * Both the current mode and the active mode for the control can be specified. The combination of
+ * the {@link Control#getDeviceType} and the current and active mode will determine colors and
+ * transitions for the UI element.
+ */
public final class TemperatureControlTemplate extends ControlTemplate {
private static final String TAG = "ThermostatTemplate";
@@ -51,6 +59,7 @@
public @interface Mode {}
private static final int NUM_MODES = 6;
+
public static final @Mode int MODE_UNKNOWN = 0;
public static final @Mode int MODE_OFF = 1;
@@ -102,6 +111,18 @@
private final @Mode int mCurrentActiveMode;
private final @ModeFlag int mModes;
+ /**
+ * Construct a new {@link TemperatureControlTemplate}.
+ *
+ * The current and active mode have to be among the ones supported by the flags.
+ *
+ * @param templateId the identifier for this template object
+ * @param controlTemplate a template to use for interaction with the user
+ * @param currentMode the current mode for the {@link Control}
+ * @param currentActiveMode the current active mode for the {@link Control}
+ * @param modesFlag a flag representing the available modes for the {@link Control}
+ * @throws IllegalArgumentException if the parameters passed do not make a valid template.
+ */
public TemperatureControlTemplate(@NonNull String templateId,
@NonNull ControlTemplate controlTemplate,
@Mode int currentMode,
@@ -179,6 +200,9 @@
return mModes;
}
+ /**
+ * @return {@link ControlTemplate#TYPE_TEMPERATURE}
+ */
@Override
public int getTemplateType() {
return TYPE;
diff --git a/core/java/android/service/controls/templates/ToggleRangeTemplate.java b/core/java/android/service/controls/templates/ToggleRangeTemplate.java
index af43b94..cd6a2fc 100644
--- a/core/java/android/service/controls/templates/ToggleRangeTemplate.java
+++ b/core/java/android/service/controls/templates/ToggleRangeTemplate.java
@@ -18,9 +18,16 @@
import android.annotation.NonNull;
import android.os.Bundle;
+import android.service.controls.Control;
import com.android.internal.util.Preconditions;
+/**
+ * A template for a {@link Control} supporting toggling and a range.
+ *
+ * @see ToggleTemplate
+ * @see RangeTemplate
+ */
public final class ToggleRangeTemplate extends ControlTemplate {
private static final @TemplateType int TYPE = TYPE_TOGGLE_RANGE;
@@ -40,6 +47,12 @@
mRangeTemplate = new RangeTemplate(b.getBundle(KEY_RANGE));
}
+ /**
+ * Constructs a new {@link ToggleRangeTemplate}.
+ * @param templateId the identifier for this template.
+ * @param button a {@link ControlButton} to use for the toggle interface
+ * @param range a {@link RangeTemplate} to use for the range interface
+ */
public ToggleRangeTemplate(@NonNull String templateId,
@NonNull ControlButton button,
@NonNull RangeTemplate range) {
@@ -50,6 +63,14 @@
mRangeTemplate = range;
}
+ /**
+ * Constructs a new {@link ToggleRangeTemplate}.
+ * @param templateId the identifier for this template.
+ * @param checked true if the toggle should be rendered as active.
+ * @param actionDescription action description for the button.
+ * @param range {@link RangeTemplate} to use for the range interface
+ * @see ControlButton
+ */
public ToggleRangeTemplate(@NonNull String templateId,
boolean checked,
@NonNull CharSequence actionDescription,
@@ -86,6 +107,9 @@
return mControlButton.getActionDescription();
}
+ /**
+ * @return {@link ControlTemplate#TYPE_TOGGLE_RANGE}
+ */
@Override
public int getTemplateType() {
return TYPE;
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 28f4929..002d4b8 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -883,7 +883,7 @@
* </p>
*/
public void onWakeUp() {
- mActivity.finishAndRemoveTask();
+ finish();
}
/** {@inheritDoc} */
@@ -904,13 +904,14 @@
public final void finish() {
if (mDebug) Slog.v(TAG, "finish(): mFinished=" + mFinished);
- if (mActivity == null) {
+ if (mActivity != null) {
+ if (!mActivity.isFinishing()) {
+ // In case the activity is not finished yet, do it now.
+ mActivity.finishAndRemoveTask();
+ return;
+ }
+ } else if (!mWindowless) {
Slog.w(TAG, "Finish was called before the dream was attached.");
- } else if (!mActivity.isFinishing()) {
- // In case the activity is not finished yet, do it now. This can happen if someone calls
- // finish() directly, without going through wakeUp().
- mActivity.finishAndRemoveTask();
- return;
}
if (!mFinished) {
@@ -1010,7 +1011,7 @@
* @param started A callback that will be invoked once onDreamingStarted has completed.
*/
private void attach(IBinder dreamToken, boolean canDoze, IRemoteCallback started) {
- if (mActivity != null) {
+ if (mDreamToken != null) {
Slog.e(TAG, "attach() called when dream with token=" + mDreamToken
+ " already attached");
return;
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 6562572..0cd96b8 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -19,6 +19,7 @@
import android.annotation.CurrentTimeMillisLong;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.annotation.TestApi;
@@ -37,6 +38,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.ParceledListSlice;
+import android.content.pm.ShortcutInfo;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
@@ -53,6 +55,7 @@
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.Slog;
import android.widget.RemoteViews;
import com.android.internal.annotations.GuardedBy;
@@ -1566,6 +1569,7 @@
private boolean mCanBubble;
private boolean mVisuallyInterruptive;
private boolean mIsConversation;
+ private ShortcutInfo mShortcutInfo;
private static final int PARCEL_VERSION = 2;
@@ -1599,6 +1603,7 @@
out.writeBoolean(mCanBubble);
out.writeBoolean(mVisuallyInterruptive);
out.writeBoolean(mIsConversation);
+ out.writeParcelable(mShortcutInfo, flags);
}
/** @hide */
@@ -1620,7 +1625,7 @@
mImportance = in.readInt();
mImportanceExplanation = in.readCharSequence(); // may be null
mOverrideGroupKey = in.readString(); // may be null
- mChannel = (NotificationChannel) in.readParcelable(cl); // may be null
+ mChannel = in.readParcelable(cl); // may be null
mOverridePeople = in.createStringArrayList();
mSnoozeCriteria = in.createTypedArrayList(SnoozeCriterion.CREATOR);
mShowBadge = in.readBoolean();
@@ -1633,6 +1638,7 @@
mCanBubble = in.readBoolean();
mVisuallyInterruptive = in.readBoolean();
mIsConversation = in.readBoolean();
+ mShortcutInfo = in.readParcelable(cl);
}
@@ -1840,6 +1846,13 @@
/**
* @hide
*/
+ public @Nullable ShortcutInfo getShortcutInfo() {
+ return mShortcutInfo;
+ }
+
+ /**
+ * @hide
+ */
@VisibleForTesting
public void populate(String key, int rank, boolean matchesInterruptionFilter,
int visibilityOverride, int suppressedVisualEffects, int importance,
@@ -1849,7 +1862,7 @@
int userSentiment, boolean hidden, long lastAudiblyAlertedMs,
boolean noisy, ArrayList<Notification.Action> smartActions,
ArrayList<CharSequence> smartReplies, boolean canBubble,
- boolean visuallyInterruptive, boolean isConversation) {
+ boolean visuallyInterruptive, boolean isConversation, ShortcutInfo shortcutInfo) {
mKey = key;
mRank = rank;
mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW;
@@ -1872,6 +1885,7 @@
mCanBubble = canBubble;
mVisuallyInterruptive = visuallyInterruptive;
mIsConversation = isConversation;
+ mShortcutInfo = shortcutInfo;
}
/**
@@ -1898,7 +1912,8 @@
other.mSmartReplies,
other.mCanBubble,
other.mVisuallyInterruptive,
- other.mIsConversation);
+ other.mIsConversation,
+ other.mShortcutInfo);
}
/**
@@ -1952,7 +1967,10 @@
&& Objects.equals(mSmartReplies, other.mSmartReplies)
&& Objects.equals(mCanBubble, other.mCanBubble)
&& Objects.equals(mVisuallyInterruptive, other.mVisuallyInterruptive)
- && Objects.equals(mIsConversation, other.mIsConversation);
+ && Objects.equals(mIsConversation, other.mIsConversation)
+ // Shortcutinfo doesn't have equals either; use id
+ && Objects.equals((mShortcutInfo == null ? 0 : mShortcutInfo.getId()),
+ (other.mShortcutInfo == null ? 0 : other.mShortcutInfo.getId()));
}
}
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index a88d389..7d070b1 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -227,7 +227,6 @@
@Nullable
private KeyphraseMetadata mKeyphraseMetadata;
private final KeyphraseEnrollmentInfo mKeyphraseEnrollmentInfo;
- private final IVoiceInteractionService mVoiceInteractionService;
private final IVoiceInteractionManagerService mModelManagementService;
private final SoundTriggerListener mInternalCallback;
private final Callback mExternalCallback;
@@ -413,14 +412,11 @@
* @param text The keyphrase text to get the detector for.
* @param locale The java locale for the detector.
* @param callback A non-null Callback for receiving the recognition events.
- * @param voiceInteractionService The current voice interaction service.
* @param modelManagementService A service that allows management of sound models.
- *
* @hide
*/
public AlwaysOnHotwordDetector(String text, Locale locale, Callback callback,
KeyphraseEnrollmentInfo keyphraseEnrollmentInfo,
- IVoiceInteractionService voiceInteractionService,
IVoiceInteractionManagerService modelManagementService) {
mText = text;
mLocale = locale;
@@ -428,7 +424,6 @@
mExternalCallback = callback;
mHandler = new MyHandler();
mInternalCallback = new SoundTriggerListener(mHandler);
- mVoiceInteractionService = voiceInteractionService;
mModelManagementService = modelManagementService;
new RefreshAvailabiltyTask().execute();
}
@@ -471,6 +466,8 @@
/**
* Get the audio capabilities supported by the platform which can be enabled when
* starting a recognition.
+ * Caller must be the active voice interaction service via
+ * Settings.Secure.VOICE_INTERACTION_SERVICE.
*
* @see #AUDIO_CAPABILITY_ECHO_CANCELLATION
* @see #AUDIO_CAPABILITY_NOISE_SUPPRESSION
@@ -488,7 +485,7 @@
private int getSupportedAudioCapabilitiesLocked() {
try {
ModuleProperties properties =
- mModelManagementService.getDspModuleProperties(mVoiceInteractionService);
+ mModelManagementService.getDspModuleProperties();
if (properties != null) {
return properties.audioCapabilities;
}
@@ -501,6 +498,8 @@
/**
* Starts recognition for the associated keyphrase.
+ * Caller must be the active voice interaction service via
+ * Settings.Secure.VOICE_INTERACTION_SERVICE.
*
* @see #RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO
* @see #RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS
@@ -533,6 +532,8 @@
/**
* Stops recognition for the associated keyphrase.
+ * Caller must be the active voice interaction service via
+ * Settings.Secure.VOICE_INTERACTION_SERVICE.
*
* @return Indicates whether the call succeeded or not.
* @throws UnsupportedOperationException if the recognition isn't supported.
@@ -565,6 +566,8 @@
* stopping recognition. Once the model is unloaded, the value will be lost.
* {@link AlwaysOnHotwordDetector#queryParameter} should be checked first before calling this
* method.
+ * Caller must be the active voice interaction service via
+ * Settings.Secure.VOICE_INTERACTION_SERVICE.
*
* @param modelParam {@link ModelParams}
* @param value Value to set
@@ -595,6 +598,8 @@
* value is returned. See {@link ModelParams} for parameter default values.
* {@link AlwaysOnHotwordDetector#queryParameter} should be checked first before
* calling this method.
+ * Caller must be the active voice interaction service via
+ * Settings.Secure.VOICE_INTERACTION_SERVICE.
*
* @param modelParam {@link ModelParams}
* @return value of parameter
@@ -617,6 +622,8 @@
* Determine if parameter control is supported for the given model handle.
* This method should be checked prior to calling {@link AlwaysOnHotwordDetector#setParameter}
* or {@link AlwaysOnHotwordDetector#getParameter}.
+ * Caller must be the active voice interaction service via
+ * Settings.Secure.VOICE_INTERACTION_SERVICE.
*
* @param modelParam {@link ModelParams}
* @return supported range of parameter, null if not supported
@@ -775,7 +782,7 @@
int code = STATUS_ERROR;
try {
- code = mModelManagementService.startRecognition(mVoiceInteractionService,
+ code = mModelManagementService.startRecognition(
mKeyphraseMetadata.id, mLocale.toLanguageTag(), mInternalCallback,
new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
recognitionExtra, null /* additional data */, audioCapabilities));
@@ -791,8 +798,8 @@
private int stopRecognitionLocked() {
int code = STATUS_ERROR;
try {
- code = mModelManagementService.stopRecognition(
- mVoiceInteractionService, mKeyphraseMetadata.id, mInternalCallback);
+ code = mModelManagementService.stopRecognition(mKeyphraseMetadata.id,
+ mInternalCallback);
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in stopRecognition!", e);
}
@@ -805,8 +812,8 @@
private int setParameterLocked(@ModelParams int modelParam, int value) {
try {
- int code = mModelManagementService.setParameter(mVoiceInteractionService,
- mKeyphraseMetadata.id, modelParam, value);
+ int code = mModelManagementService.setParameter(mKeyphraseMetadata.id, modelParam,
+ value);
if (code != STATUS_OK) {
Slog.w(TAG, "setParameter failed with error code " + code);
@@ -820,8 +827,7 @@
private int getParameterLocked(@ModelParams int modelParam) {
try {
- return mModelManagementService.getParameter(mVoiceInteractionService,
- mKeyphraseMetadata.id, modelParam);
+ return mModelManagementService.getParameter(mKeyphraseMetadata.id, modelParam);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -831,8 +837,7 @@
private ModelParamRange queryParameterLocked(@ModelParams int modelParam) {
try {
SoundTrigger.ModelParamRange modelParamRange =
- mModelManagementService.queryParameter(mVoiceInteractionService,
- mKeyphraseMetadata.id, modelParam);
+ mModelManagementService.queryParameter(mKeyphraseMetadata.id, modelParam);
if (modelParamRange == null) {
return null;
@@ -966,7 +971,7 @@
ModuleProperties dspModuleProperties = null;
try {
dspModuleProperties =
- mModelManagementService.getDspModuleProperties(mVoiceInteractionService);
+ mModelManagementService.getDspModuleProperties();
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in getDspProperties!", e);
}
@@ -982,7 +987,7 @@
private void internalUpdateEnrolledKeyphraseMetadata() {
try {
mKeyphraseMetadata = mModelManagementService.getEnrolledKeyphraseMetadata(
- mVoiceInteractionService, mText, mLocale.toLanguageTag());
+ mText, mLocale.toLanguageTag());
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in internalUpdateEnrolledKeyphraseMetadata", e);
}
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index fc99836..b54e4d9 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -199,7 +199,7 @@
throw new IllegalStateException("Not available until onReady() is called");
}
try {
- mSystemService.showSession(mInterface, args, flags);
+ mSystemService.showSession(args, flags);
} catch (RemoteException e) {
}
}
@@ -302,7 +302,7 @@
// Allow only one concurrent recognition via the APIs.
safelyShutdownHotwordDetector();
mHotwordDetector = new AlwaysOnHotwordDetector(keyphrase, locale, callback,
- mKeyphraseEnrollmentInfo, mInterface, mSystemService);
+ mKeyphraseEnrollmentInfo, mSystemService);
}
return mHotwordDetector;
}
@@ -373,7 +373,7 @@
}
try {
- mSystemService.setUiHints(mInterface, hints);
+ mSystemService.setUiHints(hints);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 2c077bb..7238b12 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.Compatibility;
import android.compat.annotation.ChangeId;
@@ -62,7 +61,6 @@
*
* @hide
*/
-@SystemApi
@TestApi
public class TelephonyRegistryManager {
@@ -286,7 +284,6 @@
* @param incomingNumber incoming phone number.
* @hide
*/
- @SystemApi
@TestApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void notifyCallStateChangedForAllSubscriptions(@CallState int state,
@@ -302,7 +299,6 @@
* Notify {@link SubscriptionInfo} change.
* @hide
*/
- @SystemApi
public void notifySubscriptionInfoChanged() {
try {
sRegistry.notifySubscriptionInfoChanged();
@@ -315,7 +311,6 @@
* Notify opportunistic {@link SubscriptionInfo} change.
* @hide
*/
- @SystemApi
public void notifyOpportunisticSubscriptionInfoChanged() {
try {
sRegistry.notifyOpportunisticSubscriptionInfoChanged();
diff --git a/core/java/android/util/proto/ProtoInputStream.java b/core/java/android/util/proto/ProtoInputStream.java
index 24e7d2b..cbe3734 100644
--- a/core/java/android/util/proto/ProtoInputStream.java
+++ b/core/java/android/util/proto/ProtoInputStream.java
@@ -601,6 +601,12 @@
// Limit how much bookkeeping is done by checking how far away the end of the buffer is
// and directly accessing buffer up until the end.
final int fragment = mEnd - mOffset;
+ if (fragment < 0) {
+ throw new ProtoParseException(
+ "Incomplete varint at offset 0x"
+ + Integer.toHexString(getOffset())
+ + dumpDebugData());
+ }
for (int i = 0; i < fragment; i++) {
byte b = mBuffer[(mOffset + i)];
value |= (b & 0x7FL) << shift;
@@ -646,6 +652,12 @@
fillBuffer();
// Find the number of bytes available until the end of the chunk or Fixed32
int fragment = (mEnd - mOffset) < bytesLeft ? (mEnd - mOffset) : bytesLeft;
+ if (fragment < 0) {
+ throw new ProtoParseException(
+ "Incomplete fixed32 at offset 0x"
+ + Integer.toHexString(getOffset())
+ + dumpDebugData());
+ }
incOffset(fragment);
bytesLeft -= fragment;
while (fragment > 0) {
@@ -686,6 +698,12 @@
fillBuffer();
// Find the number of bytes available until the end of the chunk or Fixed64
int fragment = (mEnd - mOffset) < bytesLeft ? (mEnd - mOffset) : bytesLeft;
+ if (fragment < 0) {
+ throw new ProtoParseException(
+ "Incomplete fixed64 at offset 0x"
+ + Integer.toHexString(getOffset())
+ + dumpDebugData());
+ }
incOffset(fragment);
bytesLeft -= fragment;
while (fragment > 0) {
diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl
index 530dffb..a60a5cc 100644
--- a/core/java/android/view/IRecentsAnimationController.aidl
+++ b/core/java/android/view/IRecentsAnimationController.aidl
@@ -67,11 +67,6 @@
void setAnimationTargetsBehindSystemBars(boolean behindSystemBars);
/**
- * Informs the system that the primary split-screen stack should be minimized.
- */
- void setSplitScreenMinimized(boolean minimized);
-
- /**
* Hides the current input method if one is showing.
*/
void hideCurrentInputMethod();
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 1730347..73601d9 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -444,8 +444,7 @@
WindowContentFrameStats getWindowContentFrameStats(IBinder token);
/**
- * @return the dock side the current docked stack is at; must be one of the
- * WindowManagerGlobal.DOCKED_* values
+ * This is a no-op.
*/
@UnsupportedAppUsage
int getDockedStackSide();
@@ -457,27 +456,11 @@
void setDockedStackDividerTouchRegion(in Rect touchableRegion);
/**
- * Registers a listener that will be called when the dock divider changes its visibility or when
- * the docked stack gets added/removed.
- */
- @UnsupportedAppUsage
- void registerDockedStackListener(IDockedStackListener listener);
-
- /**
* Registers a listener that will be called when the pinned stack state changes.
*/
void registerPinnedStackListener(int displayId, IPinnedStackListener listener);
/**
- * Updates the dim layer used while resizing.
- *
- * @param visible Whether the dim layer should be visible.
- * @param targetWindowingMode The windowing mode of the stack the dim layer should be placed on.
- * @param alpha The translucency of the dim layer, between 0 and 1.
- */
- void setResizeDimLayer(boolean visible, int targetWindowingMode, float alpha);
-
- /**
* Requests Keyboard Shortcuts from the displayed window.
*
* @param receiver The receiver to deliver the results to.
diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java
index 67e88a5..74fac2b 100644
--- a/core/java/android/view/ImeFocusController.java
+++ b/core/java/android/view/ImeFocusController.java
@@ -197,6 +197,26 @@
}
/**
+ * Called by {@link ViewRootImpl} to feedback the state of the screen for this view.
+ * @param newScreenState The new state of the screen. Can be either
+ * {@link View#SCREEN_STATE_ON} or {@link View#SCREEN_STATE_OFF}
+ */
+ @UiThread
+ void onScreenStateChanged(int newScreenState) {
+ if (!getImmDelegate().isCurrentRootView(mViewRootImpl)) {
+ return;
+ }
+ // Close input connection and IME when the screen is turn off for security concern.
+ if (newScreenState == View.SCREEN_STATE_OFF && mServedView != null) {
+ if (DEBUG) {
+ Log.d(TAG, "onScreenStateChanged, disconnect input when screen turned off");
+ }
+ mNextServedView = null;
+ mViewRootImpl.dispatchCheckFocus();
+ }
+ }
+
+ /**
* @param windowAttribute {@link WindowManager.LayoutParams} to be checked.
* @return Whether the window is in local focus mode or not.
*/
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index f226369..35286ba 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -16,8 +16,9 @@
package android.view;
+import static android.view.InsetsController.ANIMATION_TYPE_USER;
+import static android.view.InsetsController.AnimationType;
import static android.view.InsetsState.ITYPE_IME;
-import static android.view.InsetsState.toPublicType;
import android.annotation.Nullable;
import android.inputmethodservice.InputMethodService;
@@ -99,6 +100,21 @@
}
}
+ @Override
+ void hide(boolean animationFinished, @AnimationType int animationType) {
+ super.hide();
+
+ if (!animationFinished) {
+ if (animationType == ANIMATION_TYPE_USER) {
+ // if controlWindowInsetsAnimation is hiding keyboard.
+ notifyHidden();
+ }
+ } else {
+ // remove IME surface as IME has finished hide animation.
+ removeSurface();
+ }
+ }
+
/**
* Request {@link InputMethodManager} to show the IME.
* @return @see {@link android.view.InsetsSourceConsumer.ShowResult}.
@@ -110,7 +126,8 @@
// If we had a request before to show from IME (tracked with mImeRequestedShow), reaching
// this code here means that we now got control, so we can start the animation immediately.
- if (fromIme || mImeRequestedShow) {
+ // If client window is trying to control IME and IME is already visible, it is immediate.
+ if (fromIme || mImeRequestedShow || mState.getSource(getType()).isVisible()) {
mImeRequestedShow = false;
return ShowResult.SHOW_IMMEDIATELY;
}
@@ -128,6 +145,11 @@
}
@Override
+ public void removeSurface() {
+ getImm().removeImeSurface();
+ }
+
+ @Override
public void setControl(@Nullable InsetsSourceControl control, int[] showTypes,
int[] hideTypes) {
super.setControl(control, showTypes, hideTypes);
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 0e037c2..9247f4a 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -16,6 +16,7 @@
package android.view;
+import static android.view.InsetsController.AnimationType;
import static android.view.InsetsState.ISIDE_BOTTOM;
import static android.view.InsetsState.ISIDE_FLOATING;
import static android.view.InsetsState.ISIDE_LEFT;
@@ -64,10 +65,10 @@
private final Insets mShownInsets;
private final Matrix mTmpMatrix = new Matrix();
private final InsetsState mInitialInsetsState;
+ private final @AnimationType int mAnimationType;
private final @InsetsType int mTypes;
private final InsetsAnimationControlCallbacks mController;
private final WindowInsetsAnimation mAnimation;
- private final Rect mFrame;
private final boolean mFade;
private Insets mCurrentInsets;
private Insets mPendingInsets;
@@ -83,7 +84,8 @@
InsetsState state, WindowInsetsAnimationControlListener listener,
@InsetsType int types,
InsetsAnimationControlCallbacks controller, long durationMs, Interpolator interpolator,
- boolean fade, @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) {
+ boolean fade, @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
+ @AnimationType int animationType) {
mControls = controls;
mListener = listener;
mTypes = types;
@@ -96,12 +98,12 @@
null /* typeSideMap */);
mShownInsets = calculateInsets(mInitialInsetsState, frame, controls, true /* shown */,
mTypeSideMap);
- mFrame = new Rect(frame);
buildTypeSourcesMap(mTypeSideMap, mSideSourceMap, mControls);
mAnimation = new WindowInsetsAnimation(mTypes, interpolator,
durationMs);
mAnimation.setAlpha(getCurrentAlpha());
+ mAnimationType = animationType;
mController.startAnimation(this, listener, types, mAnimation,
new Bounds(mHiddenInsets, mShownInsets), layoutInsetsDuringAnimation);
}
@@ -135,6 +137,10 @@
return InsetsState.toInternalType(mTypes).contains(type);
}
+ @AnimationType int getAnimationType() {
+ return mAnimationType;
+ }
+
@Override
public void setInsetsAndAlpha(Insets insets, float alpha, float fraction) {
if (mFinished) {
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 4a6a5a0..4cb8e13 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -582,7 +582,7 @@
final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(controls,
frame, mState, listener, typesReady, this, durationMs, interpolator, fade,
- layoutInsetsDuringAnimation);
+ layoutInsetsDuringAnimation, animationType);
mRunningAnimations.add(new RunningAnimation(controller, animationType));
mRunningInsetsAnimations.add(controller.getAnimation());
cancellationSignal.setOnCancelListener(controller::onCancelled);
@@ -598,7 +598,7 @@
int typesReady = 0;
boolean imeReady = true;
for (int i = internalTypes.size() - 1; i >= 0; i--) {
- InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
+ final InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
boolean show = animationType == ANIMATION_TYPE_SHOW
|| animationType == ANIMATION_TYPE_USER;
boolean canRun = false;
@@ -694,7 +694,8 @@
if (shown) {
showDirectly(controller.getTypes());
} else {
- hideDirectly(controller.getTypes());
+ hideDirectly(controller.getTypes(), true /* animationFinished */,
+ controller.getAnimationType());
}
}
@@ -852,10 +853,11 @@
: LAYOUT_INSETS_DURING_ANIMATION_HIDDEN);
}
- private void hideDirectly(@InsetsType int types) {
+ private void hideDirectly(
+ @InsetsType int types, boolean animationFinished, @AnimationType int animationType) {
final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
for (int i = internalTypes.size() - 1; i >= 0; i--) {
- getSourceConsumer(internalTypes.valueAt(i)).hide();
+ getSourceConsumer(internalTypes.valueAt(i)).hide(animationFinished, animationType);
}
}
@@ -887,8 +889,9 @@
if (layoutDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN) {
showDirectly(types);
} else {
- hideDirectly(types);
+ hideDirectly(types, false /* animationFinished */, controller.getAnimationType());
}
+
if (mViewRoot.mView == null) {
return;
}
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index e6497c0..6d07a13 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -16,12 +16,11 @@
package android.view;
-import static android.view.InsetsController.ANIMATION_TYPE_NONE;
+import static android.view.InsetsController.AnimationType;
import static android.view.InsetsState.toPublicType;
import android.annotation.IntDef;
import android.annotation.Nullable;
-import android.util.MutableShort;
import android.view.InsetsState.InternalInsetsType;
import android.view.SurfaceControl.Transaction;
import android.view.WindowInsets.Type.InsetsType;
@@ -59,9 +58,10 @@
protected final InsetsController mController;
protected boolean mRequestedVisible;
+ protected final InsetsState mState;
+ protected final @InternalInsetsType int mType;
+
private final Supplier<Transaction> mTransactionSupplier;
- private final @InternalInsetsType int mType;
- private final InsetsState mState;
private @Nullable InsetsSourceControl mSourceControl;
private boolean mHasWindowFocus;
@@ -137,6 +137,10 @@
setRequestedVisible(false);
}
+ void hide(boolean animationFinished, @AnimationType int animationType) {
+ hide();
+ }
+
/**
* Called when current window gains focus
*/
@@ -201,6 +205,13 @@
}
/**
+ * Remove surface on which this consumer type is drawn.
+ */
+ public void removeSurface() {
+ // no-op for types that always return ShowResult#SHOW_IMMEDIATELY.
+ }
+
+ /**
* Sets requested visibility from the client, regardless of whether we are able to control it at
* the moment.
*/
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 0816e84..e9f10c1 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -1304,6 +1304,11 @@
* @hide
*/
public static final class DisplayConfig {
+ /**
+ * Invalid display config id.
+ */
+ public static final int INVALID_DISPLAY_CONFIG_ID = -1;
+
public int width;
public int height;
public float xDpi;
@@ -1313,6 +1318,14 @@
public long appVsyncOffsetNanos;
public long presentationDeadlineNanos;
+ /**
+ * The config group ID this config is associated to.
+ * Configs in the same group are similar from vendor's perspective and switching between
+ * configs within the same group can be done seamlessly in most cases.
+ * @see: android.hardware.graphics.composer@2.4::IComposerClient::Attribute::CONFIG_GROUP
+ */
+ public int configGroup;
+
@Override
public String toString() {
return "DisplayConfig{width=" + width
@@ -1321,7 +1334,8 @@
+ ", yDpi=" + yDpi
+ ", refreshRate=" + refreshRate
+ ", appVsyncOffsetNanos=" + appVsyncOffsetNanos
- + ", presentationDeadlineNanos=" + presentationDeadlineNanos + "}";
+ + ", presentationDeadlineNanos=" + presentationDeadlineNanos
+ + ", configGroup=" + configGroup + "}";
}
}
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index 4badede..a3b3f1f 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -201,7 +201,7 @@
final WindowManager.LayoutParams lp =
new WindowManager.LayoutParams(width, height,
WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSPARENT);
- relayout(width, height);
+ relayout(lp);
}
/**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 11ab572..53e8b52 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -11468,7 +11468,7 @@
outLocalInsets.setEmpty();
return in;
}
- Pair<Insets, WindowInsets> result = listener.onContentApplyWindowInsets(in);
+ Pair<Insets, WindowInsets> result = listener.onContentApplyWindowInsets(this, in);
outLocalInsets.set(result.first.toRect());
return result.second;
} else {
@@ -29794,7 +29794,7 @@
// accessibility
CharSequence contentDescription = getContentDescription();
- stream.addProperty("accessibility:contentDescription",
+ stream.addUserProperty("accessibility:contentDescription",
contentDescription == null ? "" : contentDescription.toString());
stream.addProperty("accessibility:labelFor", getLabelFor());
stream.addProperty("accessibility:importantForAccessibility", getImportantForAccessibility());
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 2f44fe0..8a5be75 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -1209,6 +1209,7 @@
ByteArrayOutputStream baOut = new ByteArrayOutputStream();
final ViewHierarchyEncoder encoder = new ViewHierarchyEncoder(baOut);
+ encoder.setUserPropertiesEnabled(false);
encoder.addProperty("window:left", view.mAttachInfo.mWindowLeft);
encoder.addProperty("window:top", view.mAttachInfo.mWindowTop);
view.encode(encoder);
diff --git a/core/java/android/view/ViewHierarchyEncoder.java b/core/java/android/view/ViewHierarchyEncoder.java
index b0e0524..ace05a6 100644
--- a/core/java/android/view/ViewHierarchyEncoder.java
+++ b/core/java/android/view/ViewHierarchyEncoder.java
@@ -66,10 +66,16 @@
private short mPropertyId = 1;
private Charset mCharset = Charset.forName("utf-8");
+ private boolean mUserPropertiesEnabled = true;
+
public ViewHierarchyEncoder(@NonNull ByteArrayOutputStream stream) {
mStream = new DataOutputStream(stream);
}
+ public void setUserPropertiesEnabled(boolean enabled) {
+ mUserPropertiesEnabled = enabled;
+ }
+
public void beginObject(@NonNull Object o) {
startPropertyMap();
addProperty("meta:__name__", o.getClass().getName());
@@ -121,6 +127,17 @@
}
/**
+ * Encodes a user defined property if they are allowed to be encoded
+ *
+ * @see #setUserPropertiesEnabled(boolean)
+ */
+ public void addUserProperty(@NonNull String name, @Nullable String s) {
+ if (mUserPropertiesEnabled) {
+ addProperty(name, s);
+ }
+ }
+
+ /**
* Writes the given name as the property name, and leaves it to the callee
* to fill in value for this property.
*/
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 918b5a3..9733448 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1445,6 +1445,7 @@
final int newScreenState = toViewScreenState(newDisplayState);
if (oldScreenState != newScreenState) {
mView.dispatchScreenStateChanged(newScreenState);
+ mImeFocusController.onScreenStateChanged(newScreenState);
}
if (oldDisplayState == Display.STATE_OFF) {
// Draw was suppressed so we need to for it to happen here.
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 4b284db..000fd80 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -712,13 +712,14 @@
* {@link View#setOnApplyWindowInsetsListener(OnApplyWindowInsetsListener)} through the view
* hierarchy.
*
+ * @param view The view for which to apply insets. Must not be directly modified.
* @param insets The root level insets that are about to be dispatched
* @return A pair, with the first element containing the insets to apply as margin to the
* root-level content views, and the second element determining what should be
* dispatched to the content view.
*/
@NonNull
- Pair<Insets, WindowInsets> onContentApplyWindowInsets(
+ Pair<Insets, WindowInsets> onContentApplyWindowInsets(@NonNull View view,
@NonNull WindowInsets insets);
}
diff --git a/core/java/android/view/inputmethod/InlineSuggestionInfo.java b/core/java/android/view/inputmethod/InlineSuggestionInfo.java
index 024de4d..cb0320e 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionInfo.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionInfo.java
@@ -69,6 +69,9 @@
/** The type of the UI. */
private final @NonNull @Type String mType;
+ /** Whether the suggestion should be pinned or not. */
+ private final boolean mPinned;
+
/**
* Creates a new {@link InlineSuggestionInfo}, for testing purpose.
*
@@ -79,8 +82,8 @@
public static InlineSuggestionInfo newInlineSuggestionInfo(
@NonNull InlinePresentationSpec presentationSpec,
@NonNull @Source String source,
- @Nullable String[] autofillHints) {
- return new InlineSuggestionInfo(presentationSpec, source, autofillHints, TYPE_SUGGESTION);
+ @Nullable String[] autofillHints, @NonNull @Type String type, boolean isPinned) {
+ return new InlineSuggestionInfo(presentationSpec, source, autofillHints, type, isPinned);
}
@@ -127,6 +130,8 @@
* Hints for the type of data being suggested.
* @param type
* The type of the UI.
+ * @param pinned
+ * Whether the suggestion should be pinned or not.
* @hide
*/
@DataClass.Generated.Member
@@ -134,7 +139,8 @@
@NonNull InlinePresentationSpec presentationSpec,
@NonNull @Source String source,
@Nullable String[] autofillHints,
- @NonNull @Type String type) {
+ @NonNull @Type String type,
+ boolean pinned) {
this.mPresentationSpec = presentationSpec;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mPresentationSpec);
@@ -163,6 +169,7 @@
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mType);
+ this.mPinned = pinned;
// onConstructed(); // You can define this method to get a callback
}
@@ -199,6 +206,14 @@
return mType;
}
+ /**
+ * Whether the suggestion should be pinned or not.
+ */
+ @DataClass.Generated.Member
+ public boolean isPinned() {
+ return mPinned;
+ }
+
@Override
@DataClass.Generated.Member
public String toString() {
@@ -209,7 +224,8 @@
"presentationSpec = " + mPresentationSpec + ", " +
"source = " + mSource + ", " +
"autofillHints = " + java.util.Arrays.toString(mAutofillHints) + ", " +
- "type = " + mType +
+ "type = " + mType + ", " +
+ "pinned = " + mPinned +
" }";
}
@@ -229,7 +245,8 @@
&& java.util.Objects.equals(mPresentationSpec, that.mPresentationSpec)
&& java.util.Objects.equals(mSource, that.mSource)
&& java.util.Arrays.equals(mAutofillHints, that.mAutofillHints)
- && java.util.Objects.equals(mType, that.mType);
+ && java.util.Objects.equals(mType, that.mType)
+ && mPinned == that.mPinned;
}
@Override
@@ -243,6 +260,7 @@
_hash = 31 * _hash + java.util.Objects.hashCode(mSource);
_hash = 31 * _hash + java.util.Arrays.hashCode(mAutofillHints);
_hash = 31 * _hash + java.util.Objects.hashCode(mType);
+ _hash = 31 * _hash + Boolean.hashCode(mPinned);
return _hash;
}
@@ -253,6 +271,7 @@
// void parcelFieldName(Parcel dest, int flags) { ... }
byte flg = 0;
+ if (mPinned) flg |= 0x10;
if (mAutofillHints != null) flg |= 0x4;
dest.writeByte(flg);
dest.writeTypedObject(mPresentationSpec, flags);
@@ -273,6 +292,7 @@
// static FieldType unparcelFieldName(Parcel in) { ... }
byte flg = in.readByte();
+ boolean pinned = (flg & 0x10) != 0;
InlinePresentationSpec presentationSpec = (InlinePresentationSpec) in.readTypedObject(InlinePresentationSpec.CREATOR);
String source = in.readString();
String[] autofillHints = (flg & 0x4) == 0 ? null : in.createStringArray();
@@ -306,6 +326,7 @@
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mType);
+ this.mPinned = pinned;
// onConstructed(); // You can define this method to get a callback
}
@@ -325,10 +346,10 @@
};
@DataClass.Generated(
- time = 1579806757327L,
+ time = 1582753084046L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionInfo.java",
- inputSignatures = "public static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_AUTOFILL\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_PLATFORM\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_SUGGESTION\npublic static final @android.annotation.SuppressLint({\"IntentName\"}) @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_ACTION\nprivate final @android.annotation.NonNull android.view.inline.InlinePresentationSpec mPresentationSpec\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String mSource\nprivate final @android.annotation.Nullable java.lang.String[] mAutofillHints\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String mType\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(android.view.inline.InlinePresentationSpec,java.lang.String,java.lang.String[])\nclass InlineSuggestionInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
+ inputSignatures = "public static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_AUTOFILL\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_PLATFORM\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_SUGGESTION\npublic static final @android.annotation.SuppressLint({\"IntentName\"}) @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_ACTION\nprivate final @android.annotation.NonNull android.view.inline.InlinePresentationSpec mPresentationSpec\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String mSource\nprivate final @android.annotation.Nullable java.lang.String[] mAutofillHints\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String mType\nprivate final boolean mPinned\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(android.view.inline.InlinePresentationSpec,java.lang.String,java.lang.String[],java.lang.String,boolean)\nclass InlineSuggestionInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index 71c9e33..869a929 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -348,6 +348,27 @@
* {@link InputMethodManager#RESULT_UNCHANGED_HIDDEN InputMethodManager.RESULT_UNCHANGED_HIDDEN},
* {@link InputMethodManager#RESULT_SHOWN InputMethodManager.RESULT_SHOWN}, or
* {@link InputMethodManager#RESULT_HIDDEN InputMethodManager.RESULT_HIDDEN}.
+ * @param hideInputToken an opaque {@link android.os.Binder} token to identify which API call
+ * of {@link InputMethodManager#hideSoftInputFromWindow(IBinder, int)}} is associated
+ * with this callback.
+ * @hide
+ */
+ @MainThread
+ public void hideSoftInputWithToken(int flags, ResultReceiver resultReceiver,
+ IBinder hideInputToken);
+
+ /**
+ * Request that any soft input part of the input method be hidden from the user.
+ * @param flags Provides additional information about the show request.
+ * Currently always 0.
+ * @param resultReceiver The client requesting the show may wish to
+ * be told the impact of their request, which should be supplied here.
+ * The result code should be
+ * {@link InputMethodManager#RESULT_UNCHANGED_SHOWN InputMethodManager.RESULT_UNCHANGED_SHOWN},
+ * {@link InputMethodManager#RESULT_UNCHANGED_HIDDEN
+ * InputMethodManager.RESULT_UNCHANGED_HIDDEN},
+ * {@link InputMethodManager#RESULT_SHOWN InputMethodManager.RESULT_SHOWN}, or
+ * {@link InputMethodManager#RESULT_HIDDEN InputMethodManager.RESULT_HIDDEN}.
*/
@MainThread
public void hideSoftInput(int flags, ResultReceiver resultReceiver);
@@ -366,4 +387,13 @@
* @hide
*/
public void setCurrentShowInputToken(IBinder showInputToken);
+
+ /**
+ * Update token of the client window requesting {@link #hideSoftInput(int, ResultReceiver)}
+ * @param hideInputToken dummy app window token for window requesting
+ * {@link InputMethodManager#hideSoftInputFromWindow(IBinder, int)}
+ * @hide
+ */
+ public void setCurrentHideInputToken(IBinder hideInputToken);
+
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 39d5f5c..f3aa314 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1709,7 +1709,7 @@
}
try {
- return mService.hideSoftInput(mClient, flags, resultReceiver);
+ return mService.hideSoftInput(mClient, windowToken, flags, resultReceiver);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1986,7 +1986,8 @@
@UnsupportedAppUsage
void closeCurrentInput() {
try {
- mService.hideSoftInput(mClient, HIDE_NOT_ALWAYS, null);
+ mService.hideSoftInput(
+ mClient, mCurRootView.getView().getWindowToken(), HIDE_NOT_ALWAYS, null);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2056,6 +2057,21 @@
}
/**
+ * Notify IME directly to remove surface as it is no longer visible.
+ * @hide
+ */
+ public void removeImeSurface() {
+ synchronized (mH) {
+ try {
+ if (mCurMethod != null) {
+ mCurMethod.removeImeSurface();
+ }
+ } catch (RemoteException re) {
+ }
+ }
+ }
+
+ /**
* Report the current selection range.
*
* <p><strong>Editor authors</strong>, you need to call this method whenever
diff --git a/core/java/android/view/inputmethod/InputMethodSession.java b/core/java/android/view/inputmethod/InputMethodSession.java
index eb81628..0d688ff 100644
--- a/core/java/android/view/inputmethod/InputMethodSession.java
+++ b/core/java/android/view/inputmethod/InputMethodSession.java
@@ -191,4 +191,10 @@
* @hide
*/
public void notifyImeHidden();
+
+ /**
+ * Notify IME directly to remove surface as it is no longer visible.
+ * @hide
+ */
+ public void removeImeSurface();
}
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 2d27a78..53541f7 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -369,10 +369,22 @@
public abstract boolean getDisplayZoomControls();
/**
- * Enables or disables file access within WebView. File access is enabled by
- * default. Note that this enables or disables file system access only.
- * Assets and resources are still accessible using file:///android_asset and
- * file:///android_res.
+ * Enables or disables file access within WebView.
+ * Note that this enables or disables file system access only. Assets and resources
+ * are still accessible using file:///android_asset and file:///android_res.
+ * <p class="note">
+ * <b>Note:</b> Apps should not open {@code file://} URLs from any external source in
+ * WebView, don't enable this if your app accepts arbitrary URLs from external sources.
+ * It's recommended to always use
+ * <a href="{@docRoot}reference/androidx/webkit/WebViewAssetLoader">
+ * androidx.webkit.WebViewAssetLoader</a> to access files including assets and resources over
+ * {@code http(s)://} schemes, instead of {@code file://} URLs. To prevent possible security
+ * issues targeting {@link android.os.Build.VERSION_CODES#Q} and earlier, you should explicitly
+ * set this value to {@code false}.
+ * <p>
+ * The default value is {@code true} for apps targeting
+ * {@link android.os.Build.VERSION_CODES#Q} and below, and {@code false} when targeting
+ * {@link android.os.Build.VERSION_CODES#R} and above.
*/
public abstract void setAllowFileAccess(boolean allow);
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index a299b01..8ea824d 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -353,8 +353,9 @@
// Gets the startX for new style, which should be bounded by the horizontal bounds.
// Also calculates the left/right cut width for pixel copy.
- leftBound += mViewCoordinatesInSurface[0];
- rightBound += mViewCoordinatesInSurface[0];
+ leftBound = Math.max(leftBound + mViewCoordinatesInSurface[0], 0);
+ rightBound = Math.min(
+ rightBound + mViewCoordinatesInSurface[0], mContentCopySurface.mWidth);
mLeftCutWidth = Math.max(0, leftBound - startX);
mRightCutWidth = Math.max(0, startX + mSourceWidth - rightBound);
startX = Math.max(startX, leftBound);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 815cc5c..f3243aa 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -13174,7 +13174,7 @@
stream.addProperty("text:selectionStart", getSelectionStart());
stream.addProperty("text:selectionEnd", getSelectionEnd());
stream.addProperty("text:curTextColor", mCurTextColor);
- stream.addProperty("text:text", mText == null ? null : mText.toString());
+ stream.addUserProperty("text:text", mText == null ? null : mText.toString());
stream.addProperty("text:gravity", mGravity);
}
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 9e4ebfe..7d97a91 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -31,9 +31,7 @@
import android.compat.annotation.EnabledAfter;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
-import android.content.res.Configuration;
import android.content.res.Resources;
-import android.graphics.PixelFormat;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
@@ -43,11 +41,8 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
-import android.view.Gravity;
-import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
-import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import com.android.internal.annotations.GuardedBy;
@@ -122,6 +117,7 @@
private final Binder mToken;
private final Context mContext;
private final Handler mHandler;
+ private final ToastPresenter mPresenter;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
final TN mTN;
@UnsupportedAppUsage
@@ -172,7 +168,8 @@
looper = getLooper(looper);
mHandler = new Handler(looper);
mCallbacks = new ArrayList<>();
- mTN = new TN(context.getPackageName(), mToken, mCallbacks, looper);
+ mPresenter = new ToastPresenter(context, AccessibilityManager.getInstance(context));
+ mTN = new TN(mPresenter, context.getPackageName(), mToken, mCallbacks, looper);
mTN.mY = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.toast_y_offset);
mTN.mGravity = context.getResources().getInteger(
@@ -504,13 +501,7 @@
return result;
} else {
Toast result = new Toast(context, looper);
-
- LayoutInflater inflate = (LayoutInflater)
- context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);
- TextView tv = (TextView) v.findViewById(com.android.internal.R.id.message);
- tv.setText(text);
-
+ View v = result.mPresenter.getTextToastView(text);
result.mNextView = v;
result.mDuration = duration;
@@ -611,34 +602,20 @@
final String mPackageName;
final Binder mToken;
+ private final ToastPresenter mPresenter;
@GuardedBy("mCallbacks")
private final List<Callback> mCallbacks;
- static final long SHORT_DURATION_TIMEOUT = 4000;
- static final long LONG_DURATION_TIMEOUT = 7000;
-
/**
* Creates a {@link ITransientNotification} object.
*
* The parameter {@code callbacks} is not copied and is accessed with itself as its own
* lock.
*/
- TN(String packageName, Binder token, List<Callback> callbacks, @Nullable Looper looper) {
- // XXX This should be changed to use a Dialog, with a Theme.Toast
- // defined that sets up the layout params appropriately.
- final WindowManager.LayoutParams params = mParams;
- params.height = WindowManager.LayoutParams.WRAP_CONTENT;
- params.width = WindowManager.LayoutParams.WRAP_CONTENT;
- params.format = PixelFormat.TRANSLUCENT;
- params.windowAnimations = com.android.internal.R.style.Animation_Toast;
- params.type = WindowManager.LayoutParams.TYPE_TOAST;
- params.setFitInsetsIgnoringVisibility(true);
- params.setTitle("Toast");
- params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
-
+ TN(ToastPresenter presenter, String packageName, Binder token, List<Callback> callbacks,
+ @Nullable Looper looper) {
+ mPresenter = presenter;
mPackageName = packageName;
mToken = token;
mCallbacks = callbacks;
@@ -673,6 +650,8 @@
}
}
};
+
+ presenter.startLayoutParams(mParams, packageName);
}
private List<Callback> getCallbacks() {
@@ -718,30 +697,12 @@
handleHide();
mView = mNextView;
Context context = mView.getContext().getApplicationContext();
- String packageName = mView.getContext().getOpPackageName();
if (context == null) {
context = mView.getContext();
}
- mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
- // We can resolve the Gravity here by using the Locale for getting
- // the layout direction
- final Configuration config = mView.getContext().getResources().getConfiguration();
- final int gravity = Gravity.getAbsoluteGravity(mGravity, config.getLayoutDirection());
- mParams.gravity = gravity;
- if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
- mParams.horizontalWeight = 1.0f;
- }
- if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
- mParams.verticalWeight = 1.0f;
- }
- mParams.x = mX;
- mParams.y = mY;
- mParams.verticalMargin = mVerticalMargin;
- mParams.horizontalMargin = mHorizontalMargin;
- mParams.packageName = packageName;
- mParams.hideTimeoutMilliseconds = mDuration ==
- Toast.LENGTH_LONG ? LONG_DURATION_TIMEOUT : SHORT_DURATION_TIMEOUT;
- mParams.token = windowToken;
+ mWM = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ mPresenter.adjustLayoutParams(mParams, windowToken, mDuration, mGravity, mX, mY,
+ mHorizontalMargin, mVerticalMargin);
if (mView.getParent() != null) {
if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
mWM.removeView(mView);
@@ -753,7 +714,7 @@
// invalidated. Let us hedge against that.
try {
mWM.addView(mView, mParams);
- trySendAccessibilityEvent();
+ mPresenter.trySendAccessibilityEvent(mView, mPackageName);
for (Callback callback : getCallbacks()) {
callback.onToastShown();
}
@@ -763,22 +724,6 @@
}
}
- private void trySendAccessibilityEvent() {
- AccessibilityManager accessibilityManager =
- AccessibilityManager.getInstance(mView.getContext());
- if (!accessibilityManager.isEnabled()) {
- return;
- }
- // treat toasts as notifications since they are used to
- // announce a transient piece of information to the user
- AccessibilityEvent event = AccessibilityEvent.obtain(
- AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
- event.setClassName(getClass().getName());
- event.setPackageName(mView.getContext().getPackageName());
- mView.dispatchPopulateAccessibilityEvent(event);
- accessibilityManager.sendAccessibilityEvent(event);
- }
-
@UnsupportedAppUsage
public void handleHide() {
if (localLOGV) Log.v(TAG, "HANDLE HIDE: " + this + " mView=" + mView);
diff --git a/core/java/android/widget/ToastPresenter.java b/core/java/android/widget/ToastPresenter.java
new file mode 100644
index 0000000..0447b6b
--- /dev/null
+++ b/core/java/android/widget/ToastPresenter.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.os.IBinder;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+
+import com.android.internal.R;
+import com.android.internal.util.ArrayUtils;
+
+/**
+ * Class responsible for toast presentation inside app's process and in system UI.
+ *
+ * @hide
+ */
+public class ToastPresenter {
+ private static final long SHORT_DURATION_TIMEOUT = 4000;
+ private static final long LONG_DURATION_TIMEOUT = 7000;
+
+ private final Context mContext;
+ private final Resources mResources;
+ private final AccessibilityManager mAccessibilityManager;
+
+ public ToastPresenter(Context context, AccessibilityManager accessibilityManager) {
+ mContext = context;
+ mResources = context.getResources();
+ mAccessibilityManager = accessibilityManager;
+ }
+
+ /**
+ * Initializes {@code params} with default values for toasts.
+ */
+ public void startLayoutParams(WindowManager.LayoutParams params, String packageName) {
+ params.height = WindowManager.LayoutParams.WRAP_CONTENT;
+ params.width = WindowManager.LayoutParams.WRAP_CONTENT;
+ params.format = PixelFormat.TRANSLUCENT;
+ params.windowAnimations = R.style.Animation_Toast;
+ params.type = WindowManager.LayoutParams.TYPE_TOAST;
+ params.setFitInsetsIgnoringVisibility(true);
+ params.setTitle("Toast");
+ params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+ setShowForAllUsersIfApplicable(params, packageName);
+ }
+
+ /**
+ * Customizes {@code params} according to other parameters, ready to be passed to {@link
+ * WindowManager#addView(View, ViewGroup.LayoutParams)}.
+ */
+ public void adjustLayoutParams(WindowManager.LayoutParams params, IBinder windowToken,
+ int duration, int gravity, int xOffset, int yOffset, float horizontalMargin,
+ float verticalMargin) {
+ Configuration config = mResources.getConfiguration();
+ int absGravity = Gravity.getAbsoluteGravity(gravity, config.getLayoutDirection());
+ params.gravity = absGravity;
+ if ((absGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
+ params.horizontalWeight = 1.0f;
+ }
+ if ((absGravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
+ params.verticalWeight = 1.0f;
+ }
+ params.x = xOffset;
+ params.y = yOffset;
+ params.horizontalMargin = horizontalMargin;
+ params.verticalMargin = verticalMargin;
+ params.packageName = mContext.getPackageName();
+ params.hideTimeoutMilliseconds =
+ (duration == Toast.LENGTH_LONG) ? LONG_DURATION_TIMEOUT : SHORT_DURATION_TIMEOUT;
+ params.token = windowToken;
+ }
+
+ /**
+ * Sets {@link WindowManager.LayoutParams#SYSTEM_FLAG_SHOW_FOR_ALL_USERS} flag if {@code
+ * packageName} is a cross-user package.
+ *
+ * Implementation note:
+ * This code is safe to be executed in SystemUI and the app's process:
+ * <li>SystemUI: It's running on a trusted domain so apps can't tamper with it. SystemUI
+ * has the permission INTERNAL_SYSTEM_WINDOW needed by the flag, so SystemUI can add
+ * the flag on behalf of those packages, which all contain INTERNAL_SYSTEM_WINDOW
+ * permission.
+ * <li>App: The flag being added is protected behind INTERNAL_SYSTEM_WINDOW permission
+ * and any app can already add that flag via getWindowParams() if it has that
+ * permission, so we are just doing this automatically for cross-user packages.
+ */
+ private void setShowForAllUsersIfApplicable(WindowManager.LayoutParams params,
+ String packageName) {
+ if (isCrossUserPackage(packageName)) {
+ params.privateFlags = WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+ }
+ }
+
+ private boolean isCrossUserPackage(String packageName) {
+ String[] packages = mResources.getStringArray(R.array.config_toastCrossUserPackages);
+ return ArrayUtils.contains(packages, packageName);
+ }
+
+ /**
+ * Returns the default text toast view for message {@code text}.
+ */
+ public View getTextToastView(CharSequence text) {
+ View view = LayoutInflater.from(mContext).inflate(
+ R.layout.transient_notification, null);
+ TextView textView = view.findViewById(com.android.internal.R.id.message);
+ textView.setText(text);
+ return view;
+ }
+
+ /**
+ * Sends {@link AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED} event if accessibility is
+ * enabled.
+ */
+ public void trySendAccessibilityEvent(View view, String packageName) {
+ if (!mAccessibilityManager.isEnabled()) {
+ return;
+ }
+ AccessibilityEvent event = AccessibilityEvent.obtain(
+ AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
+ event.setClassName(Toast.class.getName());
+ event.setPackageName(packageName);
+ view.dispatchPopulateAccessibilityEvent(event);
+ mAccessibilityManager.sendAccessibilityEvent(event);
+ }
+}
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index dc6942c..6a0b443 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -71,8 +71,8 @@
void noteSyncStart(String name, int uid);
void noteSyncFinish(String name, int uid);
- void noteJobStart(String name, int uid, int standbyBucket, int jobid);
- void noteJobFinish(String name, int uid, int stopReason, int standbyBucket, int jobid);
+ void noteJobStart(String name, int uid);
+ void noteJobFinish(String name, int uid, int stopReason);
void noteStartWakelock(int uid, int pid, String name, String historyName,
int type, boolean unimportantForLogging);
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 417e23f..71ee8af 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -33,7 +33,7 @@
import android.service.voice.IVoiceInteractionSession;
interface IVoiceInteractionManagerService {
- void showSession(IVoiceInteractionService service, in Bundle sessionArgs, int flags);
+ void showSession(in Bundle sessionArgs, int flags);
boolean deliverNewSession(IBinder token, IVoiceInteractionSession session,
IVoiceInteractor interactor);
boolean showSessionFromSession(IBinder token, in Bundle sessionArgs, int flags);
@@ -52,68 +52,91 @@
/**
* Gets the registered Sound model for keyphrase detection for the current user.
* May be null if no matching sound model exists.
+ * Caller must either be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}, or the caller must be a voice model
+ * enrollment application detected by
+ * {@link android.hardware.soundtrigger.KeyphraseEnrollmentInfo}.
*
* @param keyphraseId The unique identifier for the keyphrase.
* @param bcp47Locale The BCP47 language tag for the keyphrase's locale.
+ * @RequiresPermission Manifest.permission.MANAGE_VOICE_KEYPHRASES
*/
@UnsupportedAppUsage
SoundTrigger.KeyphraseSoundModel getKeyphraseSoundModel(int keyphraseId, in String bcp47Locale);
/**
- * Add/Update the given keyphrase sound model.
+ * Add/Update the given keyphrase sound model for the current user.
+ * Caller must either be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}, or the caller must be a voice model
+ * enrollment application detected by
+ * {@link android.hardware.soundtrigger.KeyphraseEnrollmentInfo}.
+ *
+ * @param model The keyphrase sound model to store peristantly.
+ * @RequiresPermission Manifest.permission.MANAGE_VOICE_KEYPHRASES
*/
int updateKeyphraseSoundModel(in SoundTrigger.KeyphraseSoundModel model);
/**
* Deletes the given keyphrase sound model for the current user.
+ * Caller must either be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}, or the caller must be a voice model
+ * enrollment application detected by
+ * {@link android.hardware.soundtrigger.KeyphraseEnrollmentInfo}.
*
* @param keyphraseId The unique identifier for the keyphrase.
* @param bcp47Locale The BCP47 language tag for the keyphrase's locale.
+ * @RequiresPermission Manifest.permission.MANAGE_VOICE_KEYPHRASES
*/
int deleteKeyphraseSoundModel(int keyphraseId, in String bcp47Locale);
/**
* Gets the properties of the DSP hardware on this device, null if not present.
+ * Caller must be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}.
*/
- SoundTrigger.ModuleProperties getDspModuleProperties(in IVoiceInteractionService service);
+ SoundTrigger.ModuleProperties getDspModuleProperties();
/**
* Indicates if there's a keyphrase sound model available for the given keyphrase ID and the
* user ID of the caller.
+ * Caller must be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}.
*
- * @param service The current VoiceInteractionService.
* @param keyphraseId The unique identifier for the keyphrase.
* @param bcp47Locale The BCP47 language tag for the keyphrase's locale.
*/
- boolean isEnrolledForKeyphrase(IVoiceInteractionService service, int keyphraseId,
- String bcp47Locale);
+ boolean isEnrolledForKeyphrase(int keyphraseId, String bcp47Locale);
/**
* Generates KeyphraseMetadata for an enrolled sound model based on keyphrase string, locale,
* and the user ID of the caller.
+ * Caller must be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}.
*
- * @param service The current VoiceInteractionService
* @param keyphrase Keyphrase text associated with the enrolled model
* @param bcp47Locale The BCP47 language tag for the keyphrase's locale.
* @return The metadata for the enrolled voice model bassed on the passed in parameters. Null if
* no matching voice model exists.
*/
- KeyphraseMetadata getEnrolledKeyphraseMetadata(IVoiceInteractionService service,
- String keyphrase, String bcp47Locale);
+ KeyphraseMetadata getEnrolledKeyphraseMetadata(String keyphrase, String bcp47Locale);
/**
* Starts a recognition for the given keyphrase.
+ * Caller must be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}.
*/
- int startRecognition(in IVoiceInteractionService service, int keyphraseId,
- in String bcp47Locale, in IRecognitionStatusCallback callback,
+ int startRecognition(int keyphraseId, in String bcp47Locale,
+ in IRecognitionStatusCallback callback,
in SoundTrigger.RecognitionConfig recognitionConfig);
/**
* Stops a recognition for the given keyphrase.
+ * Caller must be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}.
*/
- int stopRecognition(in IVoiceInteractionService service, int keyphraseId,
- in IRecognitionStatusCallback callback);
+ int stopRecognition(int keyphraseId, in IRecognitionStatusCallback callback);
/**
* Set a model specific ModelParams with the given value. This
* parameter will keep its value for the duration the model is loaded regardless of starting and
* stopping recognition. Once the model is unloaded, the value will be lost.
* queryParameter should be checked first before calling this method.
+ * Caller must be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}.
*
- * @param service The current VoiceInteractionService.
* @param keyphraseId The unique identifier for the keyphrase.
* @param modelParam ModelParams
* @param value Value to set
@@ -123,36 +146,37 @@
* - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence or
* if API is not supported by HAL
*/
- int setParameter(in IVoiceInteractionService service, int keyphraseId,
- in ModelParams modelParam, int value);
+ int setParameter(int keyphraseId, in ModelParams modelParam, int value);
/**
* Get a model specific ModelParams. This parameter will keep its value
* for the duration the model is loaded regardless of starting and stopping recognition.
* Once the model is unloaded, the value will be lost. If the value is not set, a default
* value is returned. See ModelParams for parameter default values.
* queryParameter should be checked first before calling this method.
+ * Caller must be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}.
*
- * @param service The current VoiceInteractionService.
* @param keyphraseId The unique identifier for the keyphrase.
* @param modelParam ModelParams
* @return value of parameter
*/
- int getParameter(in IVoiceInteractionService service, int keyphraseId,
- in ModelParams modelParam);
+ int getParameter(int keyphraseId, in ModelParams modelParam);
/**
* Determine if parameter control is supported for the given model handle.
* This method should be checked prior to calling setParameter or getParameter.
+ * Caller must be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}.
*
- * @param service The current VoiceInteractionService.
* @param keyphraseId The unique identifier for the keyphrase.
* @param modelParam ModelParams
* @return supported range of parameter, null if not supported
*/
- @nullable SoundTrigger.ModelParamRange queryParameter(in IVoiceInteractionService service,
- int keyphraseId, in ModelParams modelParam);
+ @nullable SoundTrigger.ModelParamRange queryParameter(int keyphraseId,
+ in ModelParams modelParam);
/**
* @return the component name for the currently active voice interaction service
+ * @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE
*/
ComponentName getActiveServiceComponentName();
@@ -164,59 +188,70 @@
* @param sourceFlags flags indicating the source of this show
* @param showCallback optional callback to be notified when the session was shown
* @param activityToken optional token of activity that needs to be on top
+ * @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE
*/
boolean showSessionForActiveService(in Bundle args, int sourceFlags,
IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken);
/**
* Hides the session from the active service, if it is showing.
+ * @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE
*/
void hideCurrentSession();
/**
* Notifies the active service that a launch was requested from the Keyguard. This will only
* be called if {@link #activeServiceSupportsLaunchFromKeyguard()} returns true.
+ * @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE
*/
void launchVoiceAssistFromKeyguard();
/**
* Indicates whether there is a voice session running (but not necessarily showing).
+ * @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE
*/
boolean isSessionRunning();
/**
* Indicates whether the currently active voice interaction service is capable of handling the
* assist gesture.
+ * @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE
*/
boolean activeServiceSupportsAssist();
/**
* Indicates whether the currently active voice interaction service is capable of being launched
* from the lockscreen.
+ * @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE
*/
boolean activeServiceSupportsLaunchFromKeyguard();
/**
* Called when the lockscreen got shown.
+ * @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE
*/
void onLockscreenShown();
/**
* Register a voice interaction listener.
+ * @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE
*/
void registerVoiceInteractionSessionListener(IVoiceInteractionSessionListener listener);
/**
* Checks the availability of a set of voice actions for the current active voice service.
* Returns all supported voice actions.
+ * @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE
*/
void getActiveServiceSupportedActions(in List<String> voiceActions,
in IVoiceActionCheckCallback callback);
/**
* Provide hints for showing UI.
+ * Caller must be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}.
*/
- void setUiHints(in IVoiceInteractionService service, in Bundle hints);
+ void setUiHints(in Bundle hints);
/**
* Requests a list of supported actions from a specific activity.
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index ef9b3d10..73ef8c6 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -23,6 +23,7 @@
import android.content.Intent;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
+import android.database.DatabaseUtils;
import android.database.MatrixCursor;
import android.database.MatrixCursor.RowBuilder;
import android.graphics.Point;
@@ -38,6 +39,7 @@
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsProvider;
import android.provider.MediaStore;
+import android.provider.MediaStore.Files.FileColumns;
import android.provider.MetadataReader;
import android.system.Int64Ref;
import android.text.TextUtils;
@@ -333,15 +335,17 @@
if (isDirectory) {
FileUtils.deleteContents(file);
}
- if (!file.delete()) {
+ // We could be deleting pending media which doesn't have any content yet, so only throw
+ // if the file exists and we fail to delete it.
+ if (file.exists() && !file.delete()) {
throw new IllegalStateException("Failed to delete " + file);
}
onDocIdChanged(docId);
- removeFromMediaStore(visibleFile, isDirectory);
+ removeFromMediaStore(visibleFile);
}
- private void removeFromMediaStore(@Nullable File visibleFile, boolean isFolder)
+ private void removeFromMediaStore(@Nullable File visibleFile)
throws FileNotFoundException {
// visibleFolder is null if we're removing a document from external thumb drive or SD card.
if (visibleFile != null) {
@@ -350,21 +354,19 @@
try {
final ContentResolver resolver = getContext().getContentResolver();
final Uri externalUri = MediaStore.Files.getContentUri("external");
+ final Bundle queryArgs = new Bundle();
+ queryArgs.putInt(MediaStore.QUERY_ARG_MATCH_PENDING, MediaStore.MATCH_INCLUDE);
- // Remove media store entries for any files inside this directory, using
- // path prefix match. Logic borrowed from MtpDatabase.
- if (isFolder) {
- final String path = visibleFile.getAbsolutePath() + "/";
- resolver.delete(externalUri,
- "_data LIKE ?1 AND lower(substr(_data,1,?2))=lower(?3)",
- new String[]{path + "%", Integer.toString(path.length()), path});
- }
-
- // Remove media store entry for this exact file.
- final String path = visibleFile.getAbsolutePath();
- resolver.delete(externalUri,
- "_data LIKE ?1 AND lower(_data)=lower(?2)",
- new String[]{path, path});
+ // Remove the media store entry corresponding to visibleFile and if it is a
+ // directory, also remove media store entries for any files inside this directory.
+ // Logic borrowed from com.android.providers.media.scan.ModernMediaScanner.
+ final String pathEscapedForLike = DatabaseUtils.escapeForLike(
+ visibleFile.getAbsolutePath());
+ ContentResolver.includeSqlSelectionArgs(queryArgs,
+ FileColumns.DATA + " LIKE ? ESCAPE '\\' OR "
+ + FileColumns.DATA + " LIKE ? ESCAPE '\\'",
+ new String[] {pathEscapedForLike + "/%", pathEscapedForLike});
+ resolver.delete(externalUri, queryArgs);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
index 20cd7c2..9a22686 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
@@ -41,5 +41,5 @@
boolean shouldOfferSwitchingToNextInputMethod();
void notifyUserAction();
void reportPreRendered(in EditorInfo info);
- void applyImeVisibility(IBinder showInputToken, boolean setVisible);
+ void applyImeVisibility(IBinder showOrHideInputToken, boolean setVisible);
}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
index 9eeef96..e5475f8 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -371,18 +371,20 @@
/**
* Calls {@link IInputMethodPrivilegedOperations#applyImeVisibility(IBinder, boolean)}.
*
- * @param showInputToken dummy token that maps to window requesting
- * {@link android.view.inputmethod.InputMethodManager#showSoftInput(View, int)}
+ * @param showOrHideInputToken dummy token that maps to window requesting
+ * {@link android.view.inputmethod.InputMethodManager#showSoftInput(View, int)} or
+ * {@link android.view.inputmethod.InputMethodManager#hideSoftInputFromWindow
+ * (IBinder, int)}
* @param setVisible {@code true} to set IME visible, else hidden.
*/
@AnyThread
- public void applyImeVisibility(IBinder showInputToken, boolean setVisible) {
+ public void applyImeVisibility(IBinder showOrHideInputToken, boolean setVisible) {
final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
if (ops == null) {
return;
}
try {
- ops.applyImeVisibility(showInputToken, setVisible);
+ ops.applyImeVisibility(showOrHideInputToken, setVisible);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/com/android/internal/logging/InstanceIdSequence.java b/core/java/com/android/internal/logging/InstanceIdSequence.java
index aa507e5..3464310 100644
--- a/core/java/com/android/internal/logging/InstanceIdSequence.java
+++ b/core/java/com/android/internal/logging/InstanceIdSequence.java
@@ -25,7 +25,7 @@
import java.util.Random;
/**
- * Generates random InstanceIds in range [0, instanceIdMax) for passing to
+ * Generates random InstanceIds in range [1, instanceIdMax] for passing to
* UiEventLogger.logWithInstanceId(). Holds a SecureRandom, which self-seeds on
* first use; try to give it a long lifetime. Safe for concurrent use.
*/
@@ -34,12 +34,12 @@
private final Random mRandom = new SecureRandom();
/**
- * Constructs a sequence with identifiers [0, instanceIdMax). Capped at INSTANCE_ID_MAX.
+ * Constructs a sequence with identifiers [1, instanceIdMax]. Capped at INSTANCE_ID_MAX.
* @param instanceIdMax Limiting value of identifiers. Normally positive: otherwise you get
- * an all-zero sequence.
+ * an all-1 sequence.
*/
public InstanceIdSequence(int instanceIdMax) {
- mInstanceIdMax = min(max(0, instanceIdMax), InstanceId.INSTANCE_ID_MAX);
+ mInstanceIdMax = min(max(1, instanceIdMax), InstanceId.INSTANCE_ID_MAX);
}
/**
@@ -47,7 +47,7 @@
* @return new InstanceId
*/
public InstanceId newInstanceId() {
- return newInstanceIdInternal(mRandom.nextInt(mInstanceIdMax));
+ return newInstanceIdInternal(1 + mRandom.nextInt(mInstanceIdMax));
}
/**
diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java
index 75eb4aa..140c410 100644
--- a/core/java/com/android/internal/logging/MetricsLogger.java
+++ b/core/java/com/android/internal/logging/MetricsLogger.java
@@ -55,11 +55,8 @@
protected void saveLog(LogMaker log) {
// TODO(b/116684537): Flag guard logging to event log and statsd socket.
EventLogTags.writeSysuiMultiAction(log.serialize());
- if (log.getCategory() != MetricsEvent.RESERVED_FOR_LOGBUILDER_COUNTER
- && log.getCategory() != MetricsEvent.RESERVED_FOR_LOGBUILDER_HISTOGRAM) {
- FrameworkStatsLog.write(FrameworkStatsLog.KEY_VALUE_PAIRS_ATOM,
- /* UID is retrieved from statsd side */ 0, log.getEntries());
- }
+ FrameworkStatsLog.write(FrameworkStatsLog.KEY_VALUE_PAIRS_ATOM,
+ /* UID is retrieved from statsd side */ 0, log.getEntries());
}
public static final int VIEW_UNKNOWN = MetricsEvent.VIEW_UNKNOWN;
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index a47bd17..54cf693 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -19,6 +19,9 @@
import static android.system.OsConstants.S_IRWXG;
import static android.system.OsConstants.S_IRWXO;
+import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__SECONDARY_ZYGOTE_INIT_START;
+import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__ZYGOTE_INIT_START;
+
import android.app.ApplicationLoaders;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.SharedLibraryInfo;
@@ -52,7 +55,7 @@
import android.webkit.WebViewFactory;
import android.widget.TextView;
-import com.android.internal.logging.MetricsLogger;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.Preconditions;
import dalvik.system.DexFile;
@@ -863,11 +866,10 @@
Runnable caller;
try {
- // Report Zygote start time to tron unless it is a runtime restart
- if (!"1".equals(SystemProperties.get("sys.boot_completed"))) {
- MetricsLogger.histogram(null, "boot_zygote_init",
- (int) SystemClock.elapsedRealtime());
- }
+ // Store now for StatsLogging later.
+ final long startTime = SystemClock.elapsedRealtime();
+ final boolean isRuntimeRestarted = "1".equals(
+ SystemProperties.get("sys.boot_completed"));
String bootTimeTag = Process.is64Bit() ? "Zygote64Timing" : "Zygote32Timing";
TimingsTraceLog bootTimingsTraceLog = new TimingsTraceLog(bootTimeTag,
@@ -894,6 +896,17 @@
}
final boolean isPrimaryZygote = zygoteSocketName.equals(Zygote.PRIMARY_SOCKET_NAME);
+ if (!isRuntimeRestarted) {
+ if (isPrimaryZygote) {
+ FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
+ BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__ZYGOTE_INIT_START,
+ startTime);
+ } else if (zygoteSocketName.equals(Zygote.SECONDARY_SOCKET_NAME)) {
+ FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
+ BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__SECONDARY_ZYGOTE_INIT_START,
+ startTime);
+ }
+ }
if (abiList == null) {
throw new RuntimeException("No ABI list supplied.");
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 24222d3..138d0dd 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -135,6 +135,29 @@
private final static String TAG = "PhoneWindow";
+ /**
+ * @see Window#setDecorFitsSystemWindows
+ */
+ private static final OnContentApplyWindowInsetsListener sDefaultContentInsetsApplier =
+ (view, insets) -> {
+ if ((view.getWindowSystemUiVisibility() & SYSTEM_UI_LAYOUT_FLAGS) != 0) {
+ return new Pair<>(Insets.NONE, insets);
+ }
+
+ boolean includeIme = (view.getViewRootImpl().mWindowAttributes.softInputMode
+ & SOFT_INPUT_MASK_ADJUST)
+ == SOFT_INPUT_ADJUST_RESIZE;
+ Insets insetsToApply;
+ if (ViewRootImpl.sNewInsetsMode == 0) {
+ insetsToApply = insets.getSystemWindowInsets();
+ } else {
+ insetsToApply = insets.getInsets(systemBars() | (includeIme ? ime() : 0));
+ }
+ insets = insets.inset(insetsToApply);
+ return new Pair<>(insetsToApply,
+ insets.inset(insetsToApply).consumeSystemWindowInsets());
+ };
+
/* If true, shadows drawn around the window will be rendered by the system compositor. If
* false, shadows will be drawn by the client by setting an elevation on the root view and
* the contents will be inset by the shadow radius. */
@@ -320,8 +343,8 @@
/** @see ViewRootImpl#mActivityConfigCallback */
private ActivityConfigCallback mActivityConfigCallback;
- private OnContentApplyWindowInsetsListener mPendingOnContentApplyWindowInsetsListener
- = createDefaultContentWindowInsetsListener();
+ private OnContentApplyWindowInsetsListener mPendingOnContentApplyWindowInsetsListener =
+ sDefaultContentInsetsApplier;
static class WindowManagerHolder {
static final IWindowManager sWindowManager = IWindowManager.Stub.asInterface(
@@ -2120,27 +2143,6 @@
mPendingOnContentApplyWindowInsetsListener = null;
}
- private OnContentApplyWindowInsetsListener createDefaultContentWindowInsetsListener() {
- return insets -> {
- if ((getDecorView().getWindowSystemUiVisibility() & SYSTEM_UI_LAYOUT_FLAGS) != 0) {
- return new Pair<>(Insets.NONE, insets);
- }
-
- boolean includeIme =
- (getViewRootImpl().mWindowAttributes.softInputMode & SOFT_INPUT_MASK_ADJUST)
- == SOFT_INPUT_ADJUST_RESIZE;
- Insets insetsToApply;
- if (ViewRootImpl.sNewInsetsMode == 0) {
- insetsToApply = insets.getSystemWindowInsets();
- } else {
- insetsToApply = insets.getInsets(systemBars() | (includeIme ? ime() : 0));
- }
- insets = insets.inset(insetsToApply);
- return new Pair<>(insetsToApply,
- insets.inset(insetsToApply).consumeSystemWindowInsets());
- };
- }
-
static private final String FOCUSED_ID_TAG = "android:focusedViewId";
static private final String VIEWS_TAG = "android:views";
static private final String PANELS_TAG = "android:Panels";
@@ -3907,7 +3909,7 @@
public void setDecorFitsSystemWindows(boolean decorFitsSystemWindows) {
ViewRootImpl impl = getViewRootImplOrNull();
OnContentApplyWindowInsetsListener listener = decorFitsSystemWindows
- ? createDefaultContentWindowInsetsListener()
+ ? sDefaultContentInsetsApplier
: null;
if (impl != null) {
impl.setOnContentApplyWindowInsetsListener(listener);
diff --git a/core/java/com/android/internal/view/IInlineSuggestionsResponseCallback.aidl b/core/java/com/android/internal/view/IInlineSuggestionsResponseCallback.aidl
index 0f93b8e..5a78e0d 100644
--- a/core/java/com/android/internal/view/IInlineSuggestionsResponseCallback.aidl
+++ b/core/java/com/android/internal/view/IInlineSuggestionsResponseCallback.aidl
@@ -16,6 +16,7 @@
package com.android.internal.view;
+import android.view.autofill.AutofillId;
import android.view.inputmethod.InlineSuggestionsResponse;
/**
@@ -23,5 +24,5 @@
* {@hide}
*/
oneway interface IInlineSuggestionsResponseCallback {
- void onInlineSuggestionsResponse(in InlineSuggestionsResponse response);
+ void onInlineSuggestionsResponse(in AutofillId fieldId, in InlineSuggestionsResponse response);
}
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index fd4b5ab..40e4f4d 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -55,7 +55,7 @@
void showSoftInput(in IBinder showInputToken, int flags, in ResultReceiver resultReceiver);
- void hideSoftInput(int flags, in ResultReceiver resultReceiver);
+ void hideSoftInput(in IBinder hideInputToken, int flags, in ResultReceiver resultReceiver);
void changeInputMethodSubtype(in InputMethodSubtype subtype);
}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 0337ddd..3f03f2a 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -43,7 +43,7 @@
boolean showSoftInput(in IInputMethodClient client, IBinder windowToken, int flags,
in ResultReceiver resultReceiver);
- boolean hideSoftInput(in IInputMethodClient client, int flags,
+ boolean hideSoftInput(in IInputMethodClient client, IBinder windowToken, int flags,
in ResultReceiver resultReceiver);
// If windowToken is null, this just does startInput(). Otherwise this reports that a window
// has gained focus, and if 'attribute' is non-null then also does startInput.
diff --git a/core/java/com/android/internal/view/IInputMethodSession.aidl b/core/java/com/android/internal/view/IInputMethodSession.aidl
index 664643c..0319e36 100644
--- a/core/java/com/android/internal/view/IInputMethodSession.aidl
+++ b/core/java/com/android/internal/view/IInputMethodSession.aidl
@@ -50,4 +50,6 @@
void updateCursorAnchorInfo(in CursorAnchorInfo cursorAnchorInfo);
void notifyImeHidden();
+
+ void removeImeSurface();
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index d27be27..ade2c7d 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -208,7 +208,6 @@
"libseccomp_policy",
"libgrallocusage",
"libscrypt_static",
- "libstatssocket",
],
shared_libs: [
@@ -266,6 +265,7 @@
"libdl",
"libdl_android",
"libstatslog",
+ "libstatssocket",
"libtimeinstate",
"server_configurable_flags",
"libstatspull",
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index a9ef257..b2ca0a7 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -91,6 +91,7 @@
jfieldID refreshRate;
jfieldID appVsyncOffsetNanos;
jfieldID presentationDeadlineNanos;
+ jfieldID configGroup;
} gDisplayConfigClassInfo;
static struct {
@@ -874,6 +875,7 @@
config.appVsyncOffset);
env->SetLongField(object, gDisplayConfigClassInfo.presentationDeadlineNanos,
config.presentationDeadline);
+ env->SetIntField(object, gDisplayConfigClassInfo.configGroup, config.configGroup);
env->SetObjectArrayElement(configArray, static_cast<jsize>(c), object);
env->DeleteLocalRef(object);
}
@@ -1614,6 +1616,7 @@
GetFieldIDOrDie(env, configClazz, "appVsyncOffsetNanos", "J");
gDisplayConfigClassInfo.presentationDeadlineNanos =
GetFieldIDOrDie(env, configClazz, "presentationDeadlineNanos", "J");
+ gDisplayConfigClassInfo.configGroup = GetFieldIDOrDie(env, configClazz, "configGroup", "I");
jclass rectClazz = FindClassOrDie(env, "android/graphics/Rect");
gRectClassInfo.bottom = GetFieldIDOrDie(env, rectClazz, "bottom", "I");
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index e4141e0..359fd48 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -626,12 +626,6 @@
// Set the jemalloc decay time to 1.
mallopt(M_DECAY_TIME, 1);
-
- // Maybe initialize GWP-ASan here. Must be called after
- // mallopt(M_SET_ZYGOTE_CHILD).
- bool ForceEnableGwpAsan = false;
- android_mallopt(M_INITIALIZE_GWP_ASAN, &ForceEnableGwpAsan,
- sizeof(ForceEnableGwpAsan));
}
static void SetUpSeccompFilter(uid_t uid, bool is_child_zygote) {
diff --git a/core/proto/android/app/job/enums.proto b/core/proto/android/app/job/enums.proto
index d2bf205..41863bb 100644
--- a/core/proto/android/app/job/enums.proto
+++ b/core/proto/android/app/job/enums.proto
@@ -34,5 +34,5 @@
STOP_REASON_TIMEOUT = 3;
STOP_REASON_DEVICE_IDLE = 4;
STOP_REASON_DEVICE_THERMAL = 5;
- STOP_REASON_RESTRAINED = 6;
+ STOP_REASON_RESTRICTED_BUCKET = 6;
}
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index 4bef2e3..bd1bae6 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -670,7 +670,7 @@
repeated ExecutionStats execution_stats = 4;
- optional AlarmListener in_quota_alarm_listener = 5;
+ reserved 5; // in_quota_alarm_listener
}
repeated PackageStats package_stats = 5;
@@ -683,7 +683,25 @@
}
repeated UidPackageMapping uid_to_package_cache = 7;
- // Next tag: 8
+ message InQuotaAlarmListener {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ // The time at which the alarm is set to go off, in the elapsed realtime timebase.
+ optional int64 trigger_time_elapsed = 1;
+
+ message Alarm {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional Package pkg = 1;
+
+ // The time at which the package will be in quota, in the elapsed realtime timebase.
+ optional int64 in_quota_time_elapsed = 2;
+ }
+ repeated Alarm alarms = 2;
+ }
+ optional InQuotaAlarmListener in_quota_alarm_listener = 8;
+
+ // Next tag: 9
}
message StorageController {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto
index 6850d01..0455d58 100644
--- a/core/proto/android/server/powermanagerservice.proto
+++ b/core/proto/android/server/powermanagerservice.proto
@@ -225,9 +225,10 @@
message ScreenBrightnessSettingLimitsProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
- optional int32 setting_minimum = 1;
- optional int32 setting_maximum = 2;
- optional int32 setting_default = 3;
+ reserved 1, 2, 3; // setting_minimum, setting_maximum, setting_default
+ optional float setting_minimum_float = 4;
+ optional float setting_maximum_float = 5;
+ optional float setting_default_float = 6;
}
// True to decouple auto-suspend mode from the display state.
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 08db454..e8a0b46 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -167,7 +167,7 @@
optional WindowContainerProto window_container = 1;
optional int32 id = 2;
reserved 3; // stacks
- optional DockedStackDividerControllerProto docked_stack_divider_controller = 4;
+ optional DockedStackDividerControllerProto docked_stack_divider_controller = 4 [deprecated=true];
// Will be removed soon.
optional PinnedStackControllerProto pinned_stack_controller = 5 [deprecated=true];
/* non app windows */
@@ -229,7 +229,7 @@
message DockedStackDividerControllerProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
- optional bool minimized_dock = 1;
+ optional bool minimized_dock = 1 [deprecated=true];
}
/* represents PinnedStackController */
diff --git a/core/res/res/layout/resolve_grid_item.xml b/core/res/res/layout/resolve_grid_item.xml
index 9aecfd9..fdd965f 100644
--- a/core/res/res/layout/resolve_grid_item.xml
+++ b/core/res/res/layout/resolve_grid_item.xml
@@ -18,7 +18,7 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
- android:layout_width="@dimen/chooser_target_width"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="100dp"
android:gravity="center"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index fb9158f..eae28a0 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3959,14 +3959,14 @@
<!-- Component name for the default module metadata provider on this device -->
<string name="config_defaultModuleMetadataProvider" translatable="false">com.android.modulemetadata</string>
- <!-- This is the default launcher component to use on secondary displays that support system
- decorations.
- This launcher activity must support multiple instances and have corresponding launch mode
- set in AndroidManifest.
+ <!-- This is the default launcher package with an activity to use on secondary displays that
+ support system decorations.
+ This launcher package must have an activity that supports multiple instances and has
+ corresponding launch mode set in AndroidManifest.
{@see android.view.Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS} -->
- <string name="config_secondaryHomeComponent" translatable="false">com.android.launcher3/com.android.launcher3.SecondaryDisplayLauncher</string>
+ <string name="config_secondaryHomePackage" translatable="false">com.android.launcher3</string>
- <!-- Force secondary home launcher specified in config_secondaryHomeComponent always. If this is
+ <!-- Force secondary home launcher specified in config_secondaryHomePackage always. If this is
not set, secondary home launcher can be replaced by user. -->
<bool name ="config_useSystemProvidedLauncherForSecondary">false</bool>
@@ -4410,4 +4410,13 @@
<!-- Whether to default to an expanded list of users on the lock screen user switcher. -->
<bool name="config_expandLockScreenUserSwitcher">false</bool>
+
+ <!-- Toasts posted from these packages will be shown to the current user, regardless of the user
+ the process belongs to. This is useful for packages that run under a single user but serve
+ multiple users, e.g. the system.
+ These packages MUST be able to add flag SYSTEM_FLAG_SHOW_FOR_ALL_USERS to a window. -->
+ <string-array name="config_toastCrossUserPackages" translatable="false">
+ <item>android</item>
+ <item>com.android.systemui</item>
+ </string-array>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index e1d94f50f..c9e6dd8 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -764,7 +764,6 @@
<dimen name="chooser_preview_image_border">1dp</dimen>
<dimen name="chooser_preview_image_max_dimen">200dp</dimen>
<dimen name="chooser_preview_width">-1px</dimen>
- <dimen name="chooser_target_width">90dp</dimen>
<dimen name="chooser_header_scroll_elevation">4dp</dimen>
<dimen name="chooser_max_collapsed_height">288dp</dimen>
<dimen name="chooser_direct_share_label_placeholder_max_width">72dp</dimen>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index df1f46f..5f390b2 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -584,6 +584,12 @@
<!-- Format for build summary info [CHAR LIMIT=NONE] -->
<string name="bugreport_status" translatable="false">%s (%s)</string>
+ <!-- Toast for taking screenshot with bugreport successfully. [CHAR_LIMIT=100] -->
+ <string name="bugreport_screenshot_success_toast">Screenshot taken with bug report</string>
+
+ <!-- Toast for failed to take screenshot with bugreport. [CHAR_LIMIT=100] -->
+ <string name="bugreport_screenshot_failure_toast">Failed to take screenshot with bug report</string>
+
<!-- label for item that enables silent mode in phone options dialog -->
<string name="global_action_toggle_silent_mode">Silent mode</string>
@@ -4918,6 +4924,8 @@
<string name="importance_from_user">You set the importance of these notifications.</string>
<string name="importance_from_person">This is important because of the people involved.</string>
+ <string name="notification_history_title_placeholder">Custom app notification</string>
+
<!-- Message to user that app trying to create user for an account that already exists. [CHAR LIMIT=none] -->
<string name="user_creation_account_exists">Allow <xliff:g id="app" example="Gmail">%1$s</xliff:g> to create a new User with <xliff:g id="account" example="foobar@gmail.com">%2$s</xliff:g> (a User with this account already exists) ?</string>
<!-- Message to user that app is trying to create user for a specified account. [CHAR LIMIT=none] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7690b94..61e4000a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1789,6 +1789,8 @@
<java-symbol type="string" name="bugreport_option_full_title" />
<java-symbol type="string" name="bugreport_option_interactive_summary" />
<java-symbol type="string" name="bugreport_option_interactive_title" />
+ <java-symbol type="string" name="bugreport_screenshot_failure_toast" />
+ <java-symbol type="string" name="bugreport_screenshot_success_toast" />
<java-symbol type="string" name="bugreport_status" />
<java-symbol type="string" name="bugreport_title" />
<java-symbol type="string" name="faceunlock_multiple_failures" />
@@ -2734,7 +2736,6 @@
<java-symbol type="string" name="chooser_no_direct_share_targets" />
<java-symbol type="drawable" name="chooser_row_layer_list" />
<java-symbol type="dimen" name="chooser_view_spacing" />
- <java-symbol type="dimen" name="chooser_target_width" />
<java-symbol type="dimen" name="chooser_edge_margin_thin" />
<java-symbol type="dimen" name="chooser_edge_margin_normal" />
<java-symbol type="dimen" name="chooser_preview_image_font_size"/>
@@ -3678,7 +3679,7 @@
<java-symbol type="string" name="config_defaultModuleMetadataProvider" />
<!-- For Secondary Launcher -->
- <java-symbol type="string" name="config_secondaryHomeComponent" />
+ <java-symbol type="string" name="config_secondaryHomePackage" />
<java-symbol type="bool" name="config_useSystemProvidedLauncherForSecondary" />
<java-symbol type="string" name="battery_saver_notification_channel_name" />
@@ -3904,4 +3905,9 @@
<java-symbol type="bool" name="config_expandLockScreenUserSwitcher" />
<java-symbol type="string" name="loading" />
+
+ <java-symbol type="array" name="config_toastCrossUserPackages" />
+
+ <java-symbol type="string" name="notification_history_title_placeholder" />
+
</resources>
diff --git a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
index 4c2ca7e..13000e9 100644
--- a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
+++ b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
@@ -284,12 +284,7 @@
}
@Override
- public void loadAvailableControls(Consumer<List<Control>> cb) {
- cb.accept(mControls);
- }
-
- @Override
- public Publisher<Control> publisherFor(List<String> ids) {
+ public Publisher<Control> createPublisherForAllAvailable() {
return new Publisher<Control>() {
public void subscribe(final Subscriber s) {
s.onSubscribe(createSubscription(s, mControls));
@@ -298,7 +293,16 @@
}
@Override
- public Publisher<Control> publisherForSuggested() {
+ public Publisher<Control> createPublisherFor(List<String> ids) {
+ return new Publisher<Control>() {
+ public void subscribe(final Subscriber s) {
+ s.onSubscribe(createSubscription(s, mControls));
+ }
+ };
+ }
+
+ @Override
+ public Publisher<Control> createPublisherForSuggested() {
return new Publisher<Control>() {
public void subscribe(final Subscriber s) {
s.onSubscribe(createSubscription(s, mControls));
diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
index aa5101e..50cd5a3 100644
--- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -123,7 +123,7 @@
mController = new InsetsAnimationControlImpl(controls,
new Rect(0, 0, 500, 500), mInsetsState, mMockListener, systemBars(),
mMockController, 10 /* durationMs */, new LinearInterpolator(),
- false /* fade */, LAYOUT_INSETS_DURING_ANIMATION_SHOWN);
+ false /* fade */, LAYOUT_INSETS_DURING_ANIMATION_SHOWN, 0 /* animationType */);
}
@Test
diff --git a/data/etc/com.android.documentsui.xml b/data/etc/com.android.documentsui.xml
index 4d98603..b6671db 100644
--- a/data/etc/com.android.documentsui.xml
+++ b/data/etc/com.android.documentsui.xml
@@ -18,5 +18,8 @@
<privapp-permissions package="com.android.documentsui">
<permission name="android.permission.CHANGE_OVERLAY_PACKAGES"/>
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
+ <!-- Permissions required for reading and logging compat changes -->
+ <permission name="android.permission.LOG_COMPAT_CHANGE"/>
+ <permission name="android.permission.READ_COMPAT_CHANGE_CONFIG"/>
</privapp-permissions>
</permissions>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 145fd8b..99605ad 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -697,6 +697,12 @@
"group": "WM_DEBUG_FOCUS_LIGHT",
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
},
+ "-668956537": {
+ "message": " THUMBNAIL %s: CREATE",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/SurfaceFreezer.java"
+ },
"-666510420": {
"message": "With display frozen, orientationChangeComplete=%b",
"level": "VERBOSE",
diff --git a/data/keyboards/Vendor_0079_Product_18d4.kl b/data/keyboards/Vendor_0079_Product_18d4.kl
new file mode 100644
index 0000000..b9a2b67
--- /dev/null
+++ b/data/keyboards/Vendor_0079_Product_18d4.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# GPD Win 2 X-Box Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_044f_Product_b326.kl b/data/keyboards/Vendor_044f_Product_b326.kl
new file mode 100644
index 0000000..d248d71
--- /dev/null
+++ b/data/keyboards/Vendor_044f_Product_b326.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Thrustmaster Gamepad GP XID
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_045e_Product_028f.kl b/data/keyboards/Vendor_045e_Product_028f.kl
new file mode 100644
index 0000000..cc5b33b
--- /dev/null
+++ b/data/keyboards/Vendor_045e_Product_028f.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Microsoft X-Box 360 pad v2
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_046d_Product_c21e.kl b/data/keyboards/Vendor_046d_Product_c21e.kl
new file mode 100644
index 0000000..9980743
--- /dev/null
+++ b/data/keyboards/Vendor_046d_Product_c21e.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Logitech Gamepad F510
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_046d_Product_c242.kl b/data/keyboards/Vendor_046d_Product_c242.kl
new file mode 100644
index 0000000..51eb44a
--- /dev/null
+++ b/data/keyboards/Vendor_046d_Product_c242.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Logitech Chillstream Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_056e_Product_2004.kl b/data/keyboards/Vendor_056e_Product_2004.kl
new file mode 100644
index 0000000..9eaa36d
--- /dev/null
+++ b/data/keyboards/Vendor_056e_Product_2004.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Elecom JC-U3613M
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_06a3_Product_f51a.kl b/data/keyboards/Vendor_06a3_Product_f51a.kl
new file mode 100644
index 0000000..e52f257
--- /dev/null
+++ b/data/keyboards/Vendor_06a3_Product_f51a.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Saitek P3600
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0738_Product_4716.kl b/data/keyboards/Vendor_0738_Product_4716.kl
new file mode 100644
index 0000000..5f3d4aa
--- /dev/null
+++ b/data/keyboards/Vendor_0738_Product_4716.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Mad Catz Wired Xbox 360 Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0738_Product_4718.kl b/data/keyboards/Vendor_0738_Product_4718.kl
new file mode 100644
index 0000000..756e1e7
--- /dev/null
+++ b/data/keyboards/Vendor_0738_Product_4718.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Mad Catz Street Fighter IV FightStick SE
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0738_Product_4726.kl b/data/keyboards/Vendor_0738_Product_4726.kl
new file mode 100644
index 0000000..9d8deb3
--- /dev/null
+++ b/data/keyboards/Vendor_0738_Product_4726.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Mad Catz Xbox 360 Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0738_Product_4736.kl b/data/keyboards/Vendor_0738_Product_4736.kl
new file mode 100644
index 0000000..c556e25
--- /dev/null
+++ b/data/keyboards/Vendor_0738_Product_4736.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Mad Catz MicroCon Gamepad
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0738_Product_4740.kl b/data/keyboards/Vendor_0738_Product_4740.kl
new file mode 100644
index 0000000..cdb7268
--- /dev/null
+++ b/data/keyboards/Vendor_0738_Product_4740.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Mad Catz Beat Pad
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0738_Product_9871.kl b/data/keyboards/Vendor_0738_Product_9871.kl
new file mode 100644
index 0000000..f404065
--- /dev/null
+++ b/data/keyboards/Vendor_0738_Product_9871.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Mad Catz Portable Drum
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0738_Product_b726.kl b/data/keyboards/Vendor_0738_Product_b726.kl
new file mode 100644
index 0000000..05b737f
--- /dev/null
+++ b/data/keyboards/Vendor_0738_Product_b726.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Mad Catz Xbox controller - MW2
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0738_Product_beef.kl b/data/keyboards/Vendor_0738_Product_beef.kl
new file mode 100644
index 0000000..f969e73
--- /dev/null
+++ b/data/keyboards/Vendor_0738_Product_beef.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Mad Catz JOYTECH NEO SE Advanced GamePad
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0738_Product_cb02.kl b/data/keyboards/Vendor_0738_Product_cb02.kl
new file mode 100644
index 0000000..bc2fc35
--- /dev/null
+++ b/data/keyboards/Vendor_0738_Product_cb02.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Saitek Cyborg Rumble Pad - PC/Xbox 360
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0738_Product_cb03.kl b/data/keyboards/Vendor_0738_Product_cb03.kl
new file mode 100644
index 0000000..dcbf6b7
--- /dev/null
+++ b/data/keyboards/Vendor_0738_Product_cb03.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Saitek P3200 Rumble Pad - PC/Xbox 360
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0738_Product_cb29.kl b/data/keyboards/Vendor_0738_Product_cb29.kl
new file mode 100644
index 0000000..fe81d1c
--- /dev/null
+++ b/data/keyboards/Vendor_0738_Product_cb29.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Saitek Aviator Stick AV8R02
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0738_Product_f738.kl b/data/keyboards/Vendor_0738_Product_f738.kl
new file mode 100644
index 0000000..2c99380
--- /dev/null
+++ b/data/keyboards/Vendor_0738_Product_f738.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Super SFIV FightStick TE S
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_07ff_Product_ffff.kl b/data/keyboards/Vendor_07ff_Product_ffff.kl
new file mode 100644
index 0000000..637c01b
--- /dev/null
+++ b/data/keyboards/Vendor_07ff_Product_ffff.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Mad Catz GamePad
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0113.kl b/data/keyboards/Vendor_0e6f_Product_0113.kl
new file mode 100644
index 0000000..90e1f75
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0113.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Afterglow AX.1 Gamepad for Xbox 360
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_011f.kl b/data/keyboards/Vendor_0e6f_Product_011f.kl
new file mode 100644
index 0000000..8c63c6b
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_011f.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Rock Candy Gamepad Wired Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0131.kl b/data/keyboards/Vendor_0e6f_Product_0131.kl
new file mode 100644
index 0000000..368c3760
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0131.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PDP EA Sports Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0133.kl b/data/keyboards/Vendor_0e6f_Product_0133.kl
new file mode 100644
index 0000000..815902e
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0133.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Xbox 360 Wired Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0139.kl b/data/keyboards/Vendor_0e6f_Product_0139.kl
new file mode 100644
index 0000000..8e2ae13
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0139.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Afterglow Prismatic Wired Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_013a.kl b/data/keyboards/Vendor_0e6f_Product_013a.kl
new file mode 100644
index 0000000..3f81983
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_013a.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PDP Xbox One Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0146.kl b/data/keyboards/Vendor_0e6f_Product_0146.kl
new file mode 100644
index 0000000..6ddd056
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0146.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Rock Candy Wired Controller for Xbox One
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0147.kl b/data/keyboards/Vendor_0e6f_Product_0147.kl
new file mode 100644
index 0000000..6745b7c
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0147.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PDP Marvel Xbox One Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0161.kl b/data/keyboards/Vendor_0e6f_Product_0161.kl
new file mode 100644
index 0000000..3f81983
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0161.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PDP Xbox One Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0162.kl b/data/keyboards/Vendor_0e6f_Product_0162.kl
new file mode 100644
index 0000000..3f81983
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0162.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PDP Xbox One Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0163.kl b/data/keyboards/Vendor_0e6f_Product_0163.kl
new file mode 100644
index 0000000..3f81983
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0163.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PDP Xbox One Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0164.kl b/data/keyboards/Vendor_0e6f_Product_0164.kl
new file mode 100644
index 0000000..0fdfd32
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0164.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PDP Battlefield One
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0165.kl b/data/keyboards/Vendor_0e6f_Product_0165.kl
new file mode 100644
index 0000000..f9731e0
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0165.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PDP Titanfall 2
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0201.kl b/data/keyboards/Vendor_0e6f_Product_0201.kl
new file mode 100644
index 0000000..5b4c167
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0201.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Pelican PL-3601 'TSZ' Wired Xbox 360 Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0213.kl b/data/keyboards/Vendor_0e6f_Product_0213.kl
new file mode 100644
index 0000000..9317346
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0213.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Afterglow Gamepad for Xbox 360
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_021f.kl b/data/keyboards/Vendor_0e6f_Product_021f.kl
new file mode 100644
index 0000000..f8d3f0c
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_021f.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Rock Candy Gamepad for Xbox 360
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0246.kl b/data/keyboards/Vendor_0e6f_Product_0246.kl
new file mode 100644
index 0000000..daf8e45
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0246.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Rock Candy Gamepad for Xbox One 2015
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_02a6.kl b/data/keyboards/Vendor_0e6f_Product_02a6.kl
new file mode 100644
index 0000000..99a5931
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_02a6.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PDP Wired Controller for Xbox One - Camo Series
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_02ab.kl b/data/keyboards/Vendor_0e6f_Product_02ab.kl
new file mode 100644
index 0000000..071a56c
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_02ab.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PDP Controller for Xbox One
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0301.kl b/data/keyboards/Vendor_0e6f_Product_0301.kl
new file mode 100644
index 0000000..a3b982d
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0301.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Logic3 Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0346.kl b/data/keyboards/Vendor_0e6f_Product_0346.kl
new file mode 100644
index 0000000..6fefbf7
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0346.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Rock Candy Gamepad for Xbox One 2016
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0401.kl b/data/keyboards/Vendor_0e6f_Product_0401.kl
new file mode 100644
index 0000000..a3b982d
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0401.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Logic3 Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0413.kl b/data/keyboards/Vendor_0e6f_Product_0413.kl
new file mode 100644
index 0000000..90e1f75
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0413.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Afterglow AX.1 Gamepad for Xbox 360
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_0501.kl b/data/keyboards/Vendor_0e6f_Product_0501.kl
new file mode 100644
index 0000000..35831d1
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_0501.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PDP Xbox 360 Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0e6f_Product_f900.kl b/data/keyboards/Vendor_0e6f_Product_f900.kl
new file mode 100644
index 0000000..44848ba
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_f900.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PDP Afterglow AX.1
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0f0d_Product_000a.kl b/data/keyboards/Vendor_0f0d_Product_000a.kl
new file mode 100644
index 0000000..b3aea04
--- /dev/null
+++ b/data/keyboards/Vendor_0f0d_Product_000a.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Hori Co. DOA4 FightStick
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0f0d_Product_000c.kl b/data/keyboards/Vendor_0f0d_Product_000c.kl
new file mode 100644
index 0000000..49c3add
--- /dev/null
+++ b/data/keyboards/Vendor_0f0d_Product_000c.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Hori PadEX Turbo
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_0f0d_Product_0067.kl b/data/keyboards/Vendor_0f0d_Product_0067.kl
new file mode 100644
index 0000000..0dfcceb
--- /dev/null
+++ b/data/keyboards/Vendor_0f0d_Product_0067.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# HORIPAD ONE
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1038_Product_1430.kl b/data/keyboards/Vendor_1038_Product_1430.kl
new file mode 100644
index 0000000..e635c1d
--- /dev/null
+++ b/data/keyboards/Vendor_1038_Product_1430.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# SteelSeries Stratus Duo
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1038_Product_1431.kl b/data/keyboards/Vendor_1038_Product_1431.kl
new file mode 100644
index 0000000..e635c1d
--- /dev/null
+++ b/data/keyboards/Vendor_1038_Product_1431.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# SteelSeries Stratus Duo
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_11c9_Product_55f0.kl b/data/keyboards/Vendor_11c9_Product_55f0.kl
new file mode 100644
index 0000000..dbb4a7e
--- /dev/null
+++ b/data/keyboards/Vendor_11c9_Product_55f0.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Nacon GC-100XF
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_12ab_Product_0301.kl b/data/keyboards/Vendor_12ab_Product_0301.kl
new file mode 100644
index 0000000..36956c1
--- /dev/null
+++ b/data/keyboards/Vendor_12ab_Product_0301.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PDP AFTERGLOW AX.1
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1430_Product_4748.kl b/data/keyboards/Vendor_1430_Product_4748.kl
new file mode 100644
index 0000000..dbe8308
--- /dev/null
+++ b/data/keyboards/Vendor_1430_Product_4748.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# RedOctane Guitar Hero X-plorer
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1430_Product_f801.kl b/data/keyboards/Vendor_1430_Product_f801.kl
new file mode 100644
index 0000000..a8f9146
--- /dev/null
+++ b/data/keyboards/Vendor_1430_Product_f801.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# RedOctane Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_146b_Product_0601.kl b/data/keyboards/Vendor_146b_Product_0601.kl
new file mode 100644
index 0000000..ea2f221
--- /dev/null
+++ b/data/keyboards/Vendor_146b_Product_0601.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# BigBen Interactive XBOX 360 Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1532_Product_0037.kl b/data/keyboards/Vendor_1532_Product_0037.kl
new file mode 100644
index 0000000..39d8b2e
--- /dev/null
+++ b/data/keyboards/Vendor_1532_Product_0037.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Razer Sabertooth
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1532_Product_0a03.kl b/data/keyboards/Vendor_1532_Product_0a03.kl
new file mode 100644
index 0000000..75775e9
--- /dev/null
+++ b/data/keyboards/Vendor_1532_Product_0a03.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Razer Wildcat
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_15e4_Product_3f00.kl b/data/keyboards/Vendor_15e4_Product_3f00.kl
new file mode 100644
index 0000000..0d641cf
--- /dev/null
+++ b/data/keyboards/Vendor_15e4_Product_3f00.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Power A Mini Pro Elite
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_15e4_Product_3f0a.kl b/data/keyboards/Vendor_15e4_Product_3f0a.kl
new file mode 100644
index 0000000..9e98aee
--- /dev/null
+++ b/data/keyboards/Vendor_15e4_Product_3f0a.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Xbox Airflo wired controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_15e4_Product_3f10.kl b/data/keyboards/Vendor_15e4_Product_3f10.kl
new file mode 100644
index 0000000..7fb0fea
--- /dev/null
+++ b/data/keyboards/Vendor_15e4_Product_3f10.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Batarang Xbox 360 controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_162e_Product_beef.kl b/data/keyboards/Vendor_162e_Product_beef.kl
new file mode 100644
index 0000000..e7fab5d
--- /dev/null
+++ b/data/keyboards/Vendor_162e_Product_beef.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Joytech Neo-Se Take2
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1bad_Product_0002.kl b/data/keyboards/Vendor_1bad_Product_0002.kl
new file mode 100644
index 0000000..d8eaaba
--- /dev/null
+++ b/data/keyboards/Vendor_1bad_Product_0002.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Harmonix Rock Band Guitar
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1bad_Product_f021.kl b/data/keyboards/Vendor_1bad_Product_f021.kl
new file mode 100644
index 0000000..9fd688b
--- /dev/null
+++ b/data/keyboards/Vendor_1bad_Product_f021.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Mad Cats Ghost Recon FS GamePad
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1bad_Product_f025.kl b/data/keyboards/Vendor_1bad_Product_f025.kl
new file mode 100644
index 0000000..03aab44
--- /dev/null
+++ b/data/keyboards/Vendor_1bad_Product_f025.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Mad Catz Call Of Duty
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1bad_Product_f028.kl b/data/keyboards/Vendor_1bad_Product_f028.kl
new file mode 100644
index 0000000..5173331
--- /dev/null
+++ b/data/keyboards/Vendor_1bad_Product_f028.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Street Fighter IV FightPad
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1bad_Product_f038.kl b/data/keyboards/Vendor_1bad_Product_f038.kl
new file mode 100644
index 0000000..79e147d
--- /dev/null
+++ b/data/keyboards/Vendor_1bad_Product_f038.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Street Fighter IV FightStick TE
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1bad_Product_f501.kl b/data/keyboards/Vendor_1bad_Product_f501.kl
new file mode 100644
index 0000000..1282532
--- /dev/null
+++ b/data/keyboards/Vendor_1bad_Product_f501.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# HoriPad EX2 Turbo
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1bad_Product_f506.kl b/data/keyboards/Vendor_1bad_Product_f506.kl
new file mode 100644
index 0000000..3a9d462
--- /dev/null
+++ b/data/keyboards/Vendor_1bad_Product_f506.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Hori Real Arcade Pro.EX Premium VLX
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1bad_Product_f900.kl b/data/keyboards/Vendor_1bad_Product_f900.kl
new file mode 100644
index 0000000..9cfceb4
--- /dev/null
+++ b/data/keyboards/Vendor_1bad_Product_f900.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Harmonix Xbox 360 Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1bad_Product_f901.kl b/data/keyboards/Vendor_1bad_Product_f901.kl
new file mode 100644
index 0000000..86d45e5
--- /dev/null
+++ b/data/keyboards/Vendor_1bad_Product_f901.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Gamestop Xbox 360 Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1bad_Product_f903.kl b/data/keyboards/Vendor_1bad_Product_f903.kl
new file mode 100644
index 0000000..f61c050
--- /dev/null
+++ b/data/keyboards/Vendor_1bad_Product_f903.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Tron Xbox 360 controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1bad_Product_f904.kl b/data/keyboards/Vendor_1bad_Product_f904.kl
new file mode 100644
index 0000000..3e02a24
--- /dev/null
+++ b/data/keyboards/Vendor_1bad_Product_f904.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PDP Versus Fighting Pad
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1bad_Product_fa01.kl b/data/keyboards/Vendor_1bad_Product_fa01.kl
new file mode 100644
index 0000000..517413d
--- /dev/null
+++ b/data/keyboards/Vendor_1bad_Product_fa01.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# MadCatz GamePad
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1bad_Product_fd00.kl b/data/keyboards/Vendor_1bad_Product_fd00.kl
new file mode 100644
index 0000000..fc6a4f8
--- /dev/null
+++ b/data/keyboards/Vendor_1bad_Product_fd00.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Razer Onza TE
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_1bad_Product_fd01.kl b/data/keyboards/Vendor_1bad_Product_fd01.kl
new file mode 100644
index 0000000..8882abf
--- /dev/null
+++ b/data/keyboards/Vendor_1bad_Product_fd01.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Razer Onza
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_24c6_Product_5300.kl b/data/keyboards/Vendor_24c6_Product_5300.kl
new file mode 100644
index 0000000..303e906
--- /dev/null
+++ b/data/keyboards/Vendor_24c6_Product_5300.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PowerA MINI PROEX Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_24c6_Product_5303.kl b/data/keyboards/Vendor_24c6_Product_5303.kl
new file mode 100644
index 0000000..9e98aee
--- /dev/null
+++ b/data/keyboards/Vendor_24c6_Product_5303.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Xbox Airflo wired controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_24c6_Product_530a.kl b/data/keyboards/Vendor_24c6_Product_530a.kl
new file mode 100644
index 0000000..aa88515
--- /dev/null
+++ b/data/keyboards/Vendor_24c6_Product_530a.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Xbox 360 Pro EX Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_24c6_Product_531a.kl b/data/keyboards/Vendor_24c6_Product_531a.kl
new file mode 100644
index 0000000..09a5c6a
--- /dev/null
+++ b/data/keyboards/Vendor_24c6_Product_531a.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PowerA Pro Ex
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_24c6_Product_5397.kl b/data/keyboards/Vendor_24c6_Product_5397.kl
new file mode 100644
index 0000000..66b896a3
--- /dev/null
+++ b/data/keyboards/Vendor_24c6_Product_5397.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# FUS1ON Tournament Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_24c6_Product_541a.kl b/data/keyboards/Vendor_24c6_Product_541a.kl
new file mode 100644
index 0000000..24271fb
--- /dev/null
+++ b/data/keyboards/Vendor_24c6_Product_541a.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PowerA Xbox One Mini Wired Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_24c6_Product_542a.kl b/data/keyboards/Vendor_24c6_Product_542a.kl
new file mode 100644
index 0000000..623bd13
--- /dev/null
+++ b/data/keyboards/Vendor_24c6_Product_542a.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Xbox ONE spectra
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_24c6_Product_543a.kl b/data/keyboards/Vendor_24c6_Product_543a.kl
new file mode 100644
index 0000000..59769c4
--- /dev/null
+++ b/data/keyboards/Vendor_24c6_Product_543a.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PowerA Xbox One wired controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_24c6_Product_5500.kl b/data/keyboards/Vendor_24c6_Product_5500.kl
new file mode 100644
index 0000000..d76d7d0
--- /dev/null
+++ b/data/keyboards/Vendor_24c6_Product_5500.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Hori XBOX 360 EX 2 with Turbo
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_24c6_Product_5501.kl b/data/keyboards/Vendor_24c6_Product_5501.kl
new file mode 100644
index 0000000..64d901a
--- /dev/null
+++ b/data/keyboards/Vendor_24c6_Product_5501.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Hori Real Arcade Pro VX-SA
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_24c6_Product_5506.kl b/data/keyboards/Vendor_24c6_Product_5506.kl
new file mode 100644
index 0000000..bfb23c3
--- /dev/null
+++ b/data/keyboards/Vendor_24c6_Product_5506.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Hori SOULCALIBUR V Stick
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_24c6_Product_550d.kl b/data/keyboards/Vendor_24c6_Product_550d.kl
new file mode 100644
index 0000000..24852b0
--- /dev/null
+++ b/data/keyboards/Vendor_24c6_Product_550d.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Hori GEM Xbox controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_24c6_Product_551a.kl b/data/keyboards/Vendor_24c6_Product_551a.kl
new file mode 100644
index 0000000..5e338a5
--- /dev/null
+++ b/data/keyboards/Vendor_24c6_Product_551a.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PowerA FUSION Pro Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_24c6_Product_561a.kl b/data/keyboards/Vendor_24c6_Product_561a.kl
new file mode 100644
index 0000000..57b7ddc
--- /dev/null
+++ b/data/keyboards/Vendor_24c6_Product_561a.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# PowerA FUSION Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_24c6_Product_5b02.kl b/data/keyboards/Vendor_24c6_Product_5b02.kl
new file mode 100644
index 0000000..bcf354d
--- /dev/null
+++ b/data/keyboards/Vendor_24c6_Product_5b02.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Thrustmaster, Inc. GPX Controller
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_24c6_Product_5d04.kl b/data/keyboards/Vendor_24c6_Product_5d04.kl
new file mode 100644
index 0000000..39d8b2e
--- /dev/null
+++ b/data/keyboards/Vendor_24c6_Product_5d04.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Razer Sabertooth
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/data/keyboards/Vendor_24c6_Product_fafe.kl b/data/keyboards/Vendor_24c6_Product_fafe.kl
new file mode 100644
index 0000000..f8d3f0c
--- /dev/null
+++ b/data/keyboards/Vendor_24c6_Product_fafe.kl
@@ -0,0 +1,58 @@
+# 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.
+
+#
+# Rock Candy Gamepad for Xbox 360
+# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708)
+#
+
+# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+# Two overlapping rectangles
+key 314 BUTTON_SELECT
+# Hamburger - 3 parallel lines
+key 315 BUTTON_START
+
+# Xbox key
+key 316 BUTTON_MODE
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index 853165d..b09082e 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -19,6 +19,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.os.LocaleList;
@@ -219,6 +220,27 @@
Preconditions.checkNotNull(am, "assetManager can not be null");
Preconditions.checkNotNull(path, "path can not be null");
+ // Attempt to open as FD, which should work unless the asset is compressed
+ AssetFileDescriptor assetFD;
+ try {
+ if (isAsset) {
+ assetFD = am.openFd(path);
+ } else if (cookie > 0) {
+ assetFD = am.openNonAssetFd(cookie, path);
+ } else {
+ assetFD = am.openNonAssetFd(path);
+ }
+
+ try (FileInputStream fis = assetFD.createInputStream()) {
+ final FileChannel fc = fis.getChannel();
+ long startOffset = assetFD.getStartOffset();
+ long declaredLength = assetFD.getDeclaredLength();
+ return fc.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength);
+ }
+ } catch (IOException e) {
+ // failed to open as FD so now we will attempt to open as an input stream
+ }
+
try (InputStream assetStream = isAsset ? am.open(path, AssetManager.ACCESS_BUFFER)
: am.openNonAsset(cookie, path, AssetManager.ACCESS_BUFFER)) {
diff --git a/keystore/java/android/security/keystore/AttestationUtils.java b/keystore/java/android/security/keystore/AttestationUtils.java
index 8a7b623..f82d8b6 100644
--- a/keystore/java/android/security/keystore/AttestationUtils.java
+++ b/keystore/java/android/security/keystore/AttestationUtils.java
@@ -74,7 +74,8 @@
public static final int ID_TYPE_MEID = 3;
/**
- * Specifies that the device should attest its MEIDs. For use with {@link #attestDeviceIds}.
+ * Specifies that the device should sign the attestation record using its device-unique
+ * attestation certificate. For use with {@link #attestDeviceIds}.
*
* @see #attestDeviceIds
*/
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 3f2f349..f87f98a 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -191,4 +191,3 @@
shared_libs: common_test_libs,
data: ["tests/data/**/*.apk"],
}
-
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 35cebd4..2bfc7fc 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -22,6 +22,7 @@
#include <androidfw/Asset.h>
#include <androidfw/LocaleData.h>
+#include <androidfw/StringPiece.h>
#include <utils/Errors.h>
#include <utils/String16.h>
#include <utils/Vector.h>
@@ -34,6 +35,7 @@
#include <android/configuration.h>
+#include <array>
#include <memory>
namespace android {
@@ -1676,42 +1678,64 @@
*/
struct ResTable_overlayable_policy_header
{
- struct ResChunk_header header;
-
+ /**
+ * Flags for a bitmask for all possible overlayable policy options.
+ *
+ * Any changes to this set should also update aidl/android/os/OverlayablePolicy.aidl
+ */
enum PolicyFlags : uint32_t {
+ // Base
+ NONE = 0x00000000,
+
// Any overlay can overlay these resources.
- POLICY_PUBLIC = 0x00000001,
+ PUBLIC = 0x00000001,
// The overlay must reside of the system partition or must have existed on the system partition
// before an upgrade to overlay these resources.
- POLICY_SYSTEM_PARTITION = 0x00000002,
+ SYSTEM_PARTITION = 0x00000002,
// The overlay must reside of the vendor partition or must have existed on the vendor partition
// before an upgrade to overlay these resources.
- POLICY_VENDOR_PARTITION = 0x00000004,
+ VENDOR_PARTITION = 0x00000004,
// The overlay must reside of the product partition or must have existed on the product
// partition before an upgrade to overlay these resources.
- POLICY_PRODUCT_PARTITION = 0x00000008,
+ PRODUCT_PARTITION = 0x00000008,
- // The overlay must be signed with the same signature as the actor of the target resource,
- // which can be separate or the same as the target package with the resource.
- POLICY_SIGNATURE = 0x00000010,
+ // The overlay must be signed with the same signature as the package containing the target
+ // resource
+ SIGNATURE = 0x00000010,
// The overlay must reside of the odm partition or must have existed on the odm
// partition before an upgrade to overlay these resources.
- POLICY_ODM_PARTITION = 0x00000020,
+ ODM_PARTITION = 0x00000020,
// The overlay must reside of the oem partition or must have existed on the oem
// partition before an upgrade to overlay these resources.
- POLICY_OEM_PARTITION = 0x00000040,
+ OEM_PARTITION = 0x00000040,
+
+ // The overlay must be signed with the same signature as the actor declared for the target
+ // resource
+ ACTOR_SIGNATURE = 0x00000080,
};
- uint32_t policy_flags;
+
+ using PolicyBitmask = uint32_t;
+
+ struct ResChunk_header header;
+
+ PolicyFlags policy_flags;
// The number of ResTable_ref that follow this header.
uint32_t entry_count;
};
+inline ResTable_overlayable_policy_header::PolicyFlags& operator |=(
+ ResTable_overlayable_policy_header::PolicyFlags& first,
+ ResTable_overlayable_policy_header::PolicyFlags second) {
+ first = static_cast<ResTable_overlayable_policy_header::PolicyFlags>(first | second);
+ return first;
+}
+
#pragma pack(push, 1)
struct Idmap_header {
// Always 0x504D4449 ('IDMP')
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index 8615069..2d69dfe 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -41,6 +41,8 @@
using ::testing::SizeIs;
using ::testing::StrEq;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace android {
TEST(LoadedArscTest, LoadSinglePackageArsc) {
@@ -240,29 +242,29 @@
ASSERT_THAT(info, NotNull());
EXPECT_THAT(info->name, Eq("OverlayableResources1"));
EXPECT_THAT(info->actor, Eq("overlay://theme"));
- EXPECT_THAT(info->policy_flags, Eq(ResTable_overlayable_policy_header::POLICY_PUBLIC));
+ EXPECT_THAT(info->policy_flags, Eq(PolicyFlags::PUBLIC));
info = package->GetOverlayableInfo(overlayable::R::string::overlayable2);
ASSERT_THAT(info, NotNull());
EXPECT_THAT(info->name, Eq("OverlayableResources1"));
EXPECT_THAT(info->actor, Eq("overlay://theme"));
EXPECT_THAT(info->policy_flags,
- Eq(ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION
- | ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION));
+ Eq(PolicyFlags::SYSTEM_PARTITION
+ | PolicyFlags::PRODUCT_PARTITION));
info = package->GetOverlayableInfo(overlayable::R::string::overlayable3);
ASSERT_THAT(info, NotNull());
EXPECT_THAT(info->name, Eq("OverlayableResources2"));
EXPECT_THAT(info->actor, Eq("overlay://com.android.overlayable"));
EXPECT_THAT(info->policy_flags,
- Eq(ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION
- | ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION));
+ Eq(PolicyFlags::VENDOR_PARTITION
+ | PolicyFlags::PRODUCT_PARTITION));
info = package->GetOverlayableInfo(overlayable::R::string::overlayable4);
EXPECT_THAT(info->name, Eq("OverlayableResources1"));
EXPECT_THAT(info->actor, Eq("overlay://theme"));
ASSERT_THAT(info, NotNull());
- EXPECT_THAT(info->policy_flags, Eq(ResTable_overlayable_policy_header::POLICY_PUBLIC));
+ EXPECT_THAT(info->policy_flags, Eq(PolicyFlags::PUBLIC));
}
TEST(LoadedArscTest, ResourceIdentifierIterator) {
diff --git a/location/java/android/location/GnssNavigationMessage.java b/location/java/android/location/GnssNavigationMessage.java
index ca0bfb1..27d3637 100644
--- a/location/java/android/location/GnssNavigationMessage.java
+++ b/location/java/android/location/GnssNavigationMessage.java
@@ -272,6 +272,14 @@
* range of 1-12</li>
* <li> For Galileo I/NAV nominal frame structure, this refers to the subframe number in the
* range of 1-24</li>
+ * <li> For SBAS and Beidou CNAV2, this is unused and can be set to -1.</li>
+ * <li> For QZSS L1 C/A subframe 4 and 5, this value corresponds to the 'frame id' of the
+ * navigation message, in the range of 1-25 (Subframe 1, 2, 3 does not contain a 'frame id' and
+ * this value can be set to -1.)</li>
+ * <li> For Beidou CNAV1 this refers to the page type number in the range of 1-63.</li>
+ * <li> For IRNSS L5 C/A subframe 3 and 4, this value corresponds to the Message Id of the
+ * navigation message, in the range of 1-63. (Subframe 1 and 2 does not contain a message type
+ * id and this value can be set to -1.)</li>
* </ul>
*/
public int getMessageId() {
@@ -299,6 +307,13 @@
* <li>For Galileo in particular, the type information embedded within the data bits may be even
* more useful in interpretation, than the nominal page and word types provided in this
* field.</li>
+ * <li> For SBAS, the submessage id corresponds to the message type, in the range 1-63.</li>
+ * <li> For Beidou CNAV1, the submessage id corresponds to the subframe number of the
+ * navigation message, in the range of 1-3.</li>
+ * <li> For Beidou CNAV2, the submessage id corresponds to the message type, in the range
+ * 1-63.</li>
+ * <li> For IRNSS L5 C/A, the submessage id corresponds to the subframe number of the
+ * navigation message, in the range of 1-4.</li>
* </ul>
*/
public int getSubmessageId() {
@@ -333,6 +348,13 @@
* <li>For Galileo I/NAV, each page contains 2 page parts, even and odd, with a total of 2x114 =
* 228 bits, (sync & tail excluded) that should be fit into 29 bytes, with MSB first (skip
* B229-B232).</li>
+ * <li>For SBAS, each block consists of 250 data bits, that should be fit into 32 bytes. MSB
+ * first (skip B251-B256).</li>
+ * <li>For Beidou CNAV1, subframe #1 consists of 14 data bits, that should be fit into 2
+ * bytes. MSB first (skip B15-B16). subframe #2 consists of 600 bits that should be fit into
+ * 75 bytes. subframe #3 consists of 264 data bits that should be fit into 33 bytes.</li>
+ * <li>For Beidou CNAV2, each subframe consists of 288 data bits, that should be fit into 36
+ * bytes.</li>
* </ul>
*/
@NonNull
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 3410919..68df7d2 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -749,7 +749,7 @@
/**
* Callback for receiving events about media route discovery.
*/
- public static class RouteCallback {
+ public abstract static class RouteCallback {
/**
* Called when routes are added. Whenever you registers a callback, this will
* be invoked with known routes.
@@ -777,7 +777,7 @@
/**
* Callback for receiving events on media transfer.
*/
- public static class TransferCallback {
+ public abstract static class TransferCallback {
/**
* Called when a media is transferred between two different routing controllers.
* This can happen by calling {@link #transferTo(MediaRoute2Info)} or
@@ -826,7 +826,7 @@
/**
* Callback for receiving {@link RoutingController} updates.
*/
- public static class ControllerCallback {
+ public abstract static class ControllerCallback {
/**
* Called when a controller is updated. (e.g., the selected routes of the
* controller is changed or the volume of the controller is changed.)
diff --git a/media/java/android/media/soundtrigger_middleware/SoundModel.aidl b/media/java/android/media/soundtrigger_middleware/SoundModel.aidl
index fba1ee50..81d8291 100644
--- a/media/java/android/media/soundtrigger_middleware/SoundModel.aidl
+++ b/media/java/android/media/soundtrigger_middleware/SoundModel.aidl
@@ -32,5 +32,7 @@
* was build for */
String vendorUuid;
/** Opaque data transparent to Android framework */
- byte[] data;
+ FileDescriptor data;
+ /** Size of the above data, in bytes. */
+ int dataSize;
}
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 73769be..c79e72d 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -18,11 +18,13 @@
import android.annotation.BytesLong;
import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
+import android.hardware.tv.tuner.V1_0.Constants;
import android.media.tv.TvInputService;
import android.media.tv.tuner.TunerConstants.Result;
import android.media.tv.tuner.dvr.DvrPlayback;
@@ -45,6 +47,8 @@
import android.os.Looper;
import android.os.Message;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -67,6 +71,22 @@
private static final int MSG_ON_FILTER_STATUS = 3;
private static final int MSG_ON_LNB_EVENT = 4;
+ /** @hide */
+ @IntDef(prefix = "DVR_TYPE_", value = {DVR_TYPE_RECORD, DVR_TYPE_PLAYBACK})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DvrType {}
+
+ /**
+ * DVR for recording.
+ * @hide
+ */
+ public static final int DVR_TYPE_RECORD = Constants.DvrType.RECORD;
+ /**
+ * DVR for playback of recorded programs.
+ * @hide
+ */
+ public static final int DVR_TYPE_PLAYBACK = Constants.DvrType.PLAYBACK;
+
static {
System.loadLibrary("media_tv_tuner");
nativeInit();
@@ -196,8 +216,8 @@
private native int nativeSetLnb(int lnbId);
private native int nativeSetLna(boolean enable);
private native FrontendStatus nativeGetFrontendStatus(int[] statusTypes);
- private native int nativeGetAvSyncHwId(Filter filter);
- private native long nativeGetAvSyncTime(int avSyncId);
+ private native Integer nativeGetAvSyncHwId(Filter filter);
+ private native Long nativeGetAvSyncTime(int avSyncId);
private native int nativeConnectCiCam(int ciCamId);
private native int nativeDisconnectCiCam();
private native FrontendInfo nativeGetFrontendInfo(int id);
@@ -443,7 +463,8 @@
@RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
public int getAvSyncHwId(@NonNull Filter filter) {
TunerUtils.checkTunerPermission(mContext);
- return nativeGetAvSyncHwId(filter);
+ Integer id = nativeGetAvSyncHwId(filter);
+ return id == null ? TunerConstants.INVALID_AV_SYNC_ID : id;
}
/**
@@ -458,7 +479,8 @@
@RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
public long getAvSyncTime(int avSyncHwId) {
TunerUtils.checkTunerPermission(mContext);
- return nativeGetAvSyncTime(avSyncHwId);
+ Long time = nativeGetAvSyncTime(avSyncHwId);
+ return time == null ? TunerConstants.TIMESTAMP_UNAVAILABLE : time;
}
/**
diff --git a/media/java/android/media/tv/tuner/TunerConstants.java b/media/java/android/media/tv/tuner/TunerConstants.java
index 82af658..6d89962 100644
--- a/media/java/android/media/tv/tuner/TunerConstants.java
+++ b/media/java/android/media/tv/tuner/TunerConstants.java
@@ -46,6 +46,19 @@
* Invalid AV Sync ID.
*/
public static final int INVALID_AV_SYNC_ID = Constants.Constant.INVALID_AV_SYNC_ID;
+ /**
+ * Timestamp is unavailable.
+ *
+ * <p>Returned by {@link android.media.tv.tuner.filter.TimeFilter#getSourceTime()},
+ * {@link android.media.tv.tuner.filter.TimeFilter#getTimeStamp()}, or
+ * {@link Tuner#getAvSyncTime(int)} when the requested timestamp is not available.
+ *
+ * @see android.media.tv.tuner.filter.TimeFilter#getSourceTime()
+ * @see android.media.tv.tuner.filter.TimeFilter#getTimeStamp()
+ * @see Tuner#getAvSyncTime(int)
+ * @hide
+ */
+ public static final long TIMESTAMP_UNAVAILABLE = -1L;
/** @hide */
@IntDef(prefix = "SCAN_TYPE_", value = {SCAN_TYPE_UNDEFINED, SCAN_TYPE_AUTO, SCAN_TYPE_BLIND})
diff --git a/media/java/android/media/tv/tuner/dvr/Dvr.java b/media/java/android/media/tv/tuner/dvr/Dvr.java
deleted file mode 100644
index 4183e3b..0000000
--- a/media/java/android/media/tv/tuner/dvr/Dvr.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * 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.
- */
-
-package android.media.tv.tuner.dvr;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.hardware.tv.tuner.V1_0.Constants;
-import android.media.tv.tuner.TunerConstants.Result;
-import android.media.tv.tuner.filter.Filter;
-import android.os.ParcelFileDescriptor;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Digital Video Record (DVR) interface provides record control on Demux's output buffer and
- * playback control on Demux's input buffer.
- *
- * @hide
- */
-@SystemApi
-public class Dvr implements AutoCloseable {
-
- /** @hide */
- @IntDef(prefix = "TYPE_", value = {TYPE_RECORD, TYPE_PLAYBACK})
- @Retention(RetentionPolicy.SOURCE)
- public @interface Type {}
-
- /**
- * DVR for recording.
- */
- public static final int TYPE_RECORD = Constants.DvrType.RECORD;
- /**
- * DVR for playback of recorded programs.
- */
- public static final int TYPE_PLAYBACK = Constants.DvrType.PLAYBACK;
-
-
- final int mType;
- long mNativeContext;
-
- private native int nativeAttachFilter(Filter filter);
- private native int nativeDetachFilter(Filter filter);
- private native int nativeConfigureDvr(DvrSettings settings);
- private native int nativeStartDvr();
- private native int nativeStopDvr();
- private native int nativeFlushDvr();
- private native int nativeClose();
- private native void nativeSetFileDescriptor(int fd);
-
- protected Dvr(int type) {
- mType = type;
- }
-
- /**
- * Attaches a filter to DVR interface for recording.
- *
- * @param filter the filter to be attached.
- * @return result status of the operation.
- */
- @Result
- public int attachFilter(@NonNull Filter filter) {
- return nativeAttachFilter(filter);
- }
-
- /**
- * Detaches a filter from DVR interface.
- *
- * @param filter the filter to be detached.
- * @return result status of the operation.
- */
- @Result
- public int detachFilter(@NonNull Filter filter) {
- return nativeDetachFilter(filter);
- }
-
- /**
- * Configures the DVR.
- *
- * @param settings the settings of the DVR interface.
- * @return result status of the operation.
- */
- @Result
- public int configure(@NonNull DvrSettings settings) {
- return nativeConfigureDvr(settings);
- }
-
- /**
- * Starts DVR.
- *
- * <p>Starts consuming playback data or producing data for recording.
- *
- * @return result status of the operation.
- */
- @Result
- public int start() {
- return nativeStartDvr();
- }
-
- /**
- * Stops DVR.
- *
- * <p>Stops consuming playback data or producing data for recording.
- *
- * @return result status of the operation.
- */
- @Result
- public int stop() {
- return nativeStopDvr();
- }
-
- /**
- * Flushed DVR data.
- *
- * <p>The data in DVR buffer is cleared.
- *
- * @return result status of the operation.
- */
- @Result
- public int flush() {
- return nativeFlushDvr();
- }
-
- /**
- * Closes the DVR instance to release resources.
- */
- public void close() {
- nativeClose();
- }
-
- /**
- * Sets file descriptor to read/write data.
- *
- * @param fd the file descriptor to read/write data.
- */
- public void setFileDescriptor(@NonNull ParcelFileDescriptor fd) {
- nativeSetFileDescriptor(fd.getFd());
- }
-
- @Type
- int getType() {
- return mType;
- }
-}
diff --git a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
index eb31574..7c15bb7 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
@@ -21,6 +21,9 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.hardware.tv.tuner.V1_0.Constants;
+import android.media.tv.tuner.TunerConstants.Result;
+import android.media.tv.tuner.filter.Filter;
+import android.os.ParcelFileDescriptor;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -33,7 +36,7 @@
* @hide
*/
@SystemApi
-public class DvrPlayback extends Dvr {
+public class DvrPlayback implements AutoCloseable {
/** @hide */
@@ -66,17 +69,110 @@
*/
public static final int PLAYBACK_STATUS_FULL = Constants.PlaybackStatus.SPACE_FULL;
+ long mNativeContext;
-
+ private native int nativeAttachFilter(Filter filter);
+ private native int nativeDetachFilter(Filter filter);
+ private native int nativeConfigureDvr(DvrSettings settings);
+ private native int nativeStartDvr();
+ private native int nativeStopDvr();
+ private native int nativeFlushDvr();
+ private native int nativeClose();
+ private native void nativeSetFileDescriptor(int fd);
private native long nativeRead(long size);
private native long nativeRead(byte[] bytes, long offset, long size);
private DvrPlayback() {
- super(Dvr.TYPE_PLAYBACK);
}
/**
+ * Attaches a filter to DVR interface for recording.
+ *
+ * @param filter the filter to be attached.
+ * @return result status of the operation.
+ */
+ @Result
+ public int attachFilter(@NonNull Filter filter) {
+ return nativeAttachFilter(filter);
+ }
+
+ /**
+ * Detaches a filter from DVR interface.
+ *
+ * @param filter the filter to be detached.
+ * @return result status of the operation.
+ */
+ @Result
+ public int detachFilter(@NonNull Filter filter) {
+ return nativeDetachFilter(filter);
+ }
+
+ /**
+ * Configures the DVR.
+ *
+ * @param settings the settings of the DVR interface.
+ * @return result status of the operation.
+ */
+ @Result
+ public int configure(@NonNull DvrSettings settings) {
+ return nativeConfigureDvr(settings);
+ }
+
+ /**
+ * Starts DVR.
+ *
+ * <p>Starts consuming playback data or producing data for recording.
+ *
+ * @return result status of the operation.
+ */
+ @Result
+ public int start() {
+ return nativeStartDvr();
+ }
+
+ /**
+ * Stops DVR.
+ *
+ * <p>Stops consuming playback data or producing data for recording.
+ *
+ * @return result status of the operation.
+ */
+ @Result
+ public int stop() {
+ return nativeStopDvr();
+ }
+
+ /**
+ * Flushed DVR data.
+ *
+ * <p>The data in DVR buffer is cleared.
+ *
+ * @return result status of the operation.
+ */
+ @Result
+ public int flush() {
+ return nativeFlushDvr();
+ }
+
+ /**
+ * Closes the DVR instance to release resources.
+ */
+ @Override
+ public void close() {
+ nativeClose();
+ }
+
+ /**
+ * Sets file descriptor to read/write data.
+ *
+ * @param fd the file descriptor to read/write data.
+ */
+ public void setFileDescriptor(@NonNull ParcelFileDescriptor fd) {
+ nativeSetFileDescriptor(fd.getFd());
+ }
+
+ /**
* Reads data from the file for DVR playback.
*
* @param size the maximum number of bytes to read.
diff --git a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
index 3128ca5..52ef5e6 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
@@ -19,6 +19,9 @@
import android.annotation.BytesLong;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.media.tv.tuner.TunerConstants.Result;
+import android.media.tv.tuner.filter.Filter;
+import android.os.ParcelFileDescriptor;
/**
* Digital Video Record (DVR) recorder class which provides record control on Demux's output buffer.
@@ -26,12 +29,108 @@
* @hide
*/
@SystemApi
-public class DvrRecorder extends Dvr {
+public class DvrRecorder implements AutoCloseable {
+ long mNativeContext;
+
+ private native int nativeAttachFilter(Filter filter);
+ private native int nativeDetachFilter(Filter filter);
+ private native int nativeConfigureDvr(DvrSettings settings);
+ private native int nativeStartDvr();
+ private native int nativeStopDvr();
+ private native int nativeFlushDvr();
+ private native int nativeClose();
+ private native void nativeSetFileDescriptor(int fd);
private native long nativeWrite(long size);
private native long nativeWrite(byte[] bytes, long offset, long size);
private DvrRecorder() {
- super(Dvr.TYPE_RECORD);
+ }
+
+
+ /**
+ * Attaches a filter to DVR interface for recording.
+ *
+ * @param filter the filter to be attached.
+ * @return result status of the operation.
+ */
+ @Result
+ public int attachFilter(@NonNull Filter filter) {
+ return nativeAttachFilter(filter);
+ }
+
+ /**
+ * Detaches a filter from DVR interface.
+ *
+ * @param filter the filter to be detached.
+ * @return result status of the operation.
+ */
+ @Result
+ public int detachFilter(@NonNull Filter filter) {
+ return nativeDetachFilter(filter);
+ }
+
+ /**
+ * Configures the DVR.
+ *
+ * @param settings the settings of the DVR interface.
+ * @return result status of the operation.
+ */
+ @Result
+ public int configure(@NonNull DvrSettings settings) {
+ return nativeConfigureDvr(settings);
+ }
+
+ /**
+ * Starts DVR.
+ *
+ * <p>Starts consuming playback data or producing data for recording.
+ *
+ * @return result status of the operation.
+ */
+ @Result
+ public int start() {
+ return nativeStartDvr();
+ }
+
+ /**
+ * Stops DVR.
+ *
+ * <p>Stops consuming playback data or producing data for recording.
+ *
+ * @return result status of the operation.
+ */
+ @Result
+ public int stop() {
+ return nativeStopDvr();
+ }
+
+ /**
+ * Flushed DVR data.
+ *
+ * <p>The data in DVR buffer is cleared.
+ *
+ * @return result status of the operation.
+ */
+ @Result
+ public int flush() {
+ return nativeFlushDvr();
+ }
+
+ /**
+ * Closes the DVR instance to release resources.
+ */
+ @Override
+ public void close() {
+ nativeClose();
+ }
+
+ /**
+ * Sets file descriptor to read/write data.
+ *
+ * @param fd the file descriptor to read/write data.
+ */
+ public void setFileDescriptor(@NonNull ParcelFileDescriptor fd) {
+ nativeSetFileDescriptor(fd.getFd());
}
/**
diff --git a/media/java/android/media/tv/tuner/dvr/DvrSettings.java b/media/java/android/media/tv/tuner/dvr/DvrSettings.java
index f9dc682..362b108 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrSettings.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrSettings.java
@@ -30,7 +30,7 @@
import java.lang.annotation.RetentionPolicy;
/**
- * DVR settings used to configure {@link Dvr}.
+ * DVR settings used to configure {@link DvrPlayback} and {@link DvrRecorder}.
*
* @hide
*/
diff --git a/media/java/android/media/voice/KeyphraseModelManager.java b/media/java/android/media/voice/KeyphraseModelManager.java
index 3fa38e0..8ec8967 100644
--- a/media/java/android/media/voice/KeyphraseModelManager.java
+++ b/media/java/android/media/voice/KeyphraseModelManager.java
@@ -37,7 +37,8 @@
* manage voice based sound trigger models.
* Callers of this class are expected to have whitelist manifest permission MANAGE_VOICE_KEYPHRASES.
* Callers of this class are expected to be the designated voice interaction service via
- * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}.
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE} or a bundled voice model enrollment application
+ * detected by {@link android.hardware.soundtrigger.KeyphraseEnrollmentInfo}.
* @hide
*/
@SystemApi
@@ -65,6 +66,10 @@
* {@link #updateKeyphraseSoundModel}.
* If the active voice interaction service changes from the current user, all requests will be
* rejected, and any registered models will be unregistered.
+ * Caller must either be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}, or the caller must be a voice model
+ * enrollment application detected by
+ * {@link android.hardware.soundtrigger.KeyphraseEnrollmentInfo}.
*
* @param keyphraseId The unique identifier for the keyphrase.
* @param locale The locale language tag supported by the desired model.
@@ -93,6 +98,10 @@
* will be overwritten with the new model.
* If the active voice interaction service changes from the current user, all requests will be
* rejected, and any registered models will be unregistered.
+ * Caller must either be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}, or the caller must be a voice model
+ * enrollment application detected by
+ * {@link android.hardware.soundtrigger.KeyphraseEnrollmentInfo}.
*
* @param model Keyphrase sound model to be updated.
* @throws ServiceSpecificException Thrown with error code if failed to update the keyphrase
@@ -120,6 +129,10 @@
* {@link #updateKeyphraseSoundModel}.
* If the active voice interaction service changes from the current user, all requests will be
* rejected, and any registered models will be unregistered.
+ * Caller must either be the active voice interaction service via
+ * {@link Settings.Secure.VOICE_INTERACTION_SERVICE}, or the caller must be a voice model
+ * enrollment application detected by
+ * {@link android.hardware.soundtrigger.KeyphraseEnrollmentInfo}.
*
* @param keyphraseId The unique identifier for the keyphrase.
* @param locale The locale language tag supported by the desired model.
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 893e516..737c95d 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -242,6 +242,13 @@
});
}
+void JMediaCodec::releaseAsync() {
+ if (mCodec != NULL) {
+ mCodec->releaseAsync();
+ }
+ mInitStatus = NO_INIT;
+}
+
JMediaCodec::~JMediaCodec() {
if (mLooper != NULL) {
/* MediaCodec and looper should have been released explicitly already
@@ -1124,7 +1131,10 @@
}
static void android_media_MediaCodec_release(JNIEnv *env, jobject thiz) {
- setMediaCodec(env, thiz, NULL);
+ sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+ if (codec != NULL) {
+ codec->releaseAsync();
+ }
}
static void throwCodecException(JNIEnv *env, status_t err, int32_t actionCode, const char *msg) {
@@ -2797,7 +2807,7 @@
static void android_media_MediaCodec_native_finalize(
JNIEnv *env, jobject thiz) {
- android_media_MediaCodec_release(env, thiz);
+ setMediaCodec(env, thiz, NULL);
}
// MediaCodec.LinearBlock
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index 8899fee..400ce1b 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -61,6 +61,7 @@
void registerSelf();
void release();
+ void releaseAsync();
status_t enableOnFrameRenderedListener(jboolean enable);
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 5c39f29..e68ccfa 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -119,7 +119,6 @@
using ::android::hardware::tv::tuner::V1_0::ITuner;
using ::android::hardware::tv::tuner::V1_0::PlaybackSettings;
using ::android::hardware::tv::tuner::V1_0::RecordSettings;
-using ::android::hardware::tv::tuner::V1_0::Result;
struct fields_t {
jfieldID tunerContext;
@@ -845,22 +844,85 @@
return (int)result;
}
-bool JTuner::openDemux() {
+Result JTuner::openDemux() {
if (mTuner == nullptr) {
- return false;
+ return Result::NOT_INITIALIZED;
}
if (mDemux != nullptr) {
- return true;
+ return Result::SUCCESS;
}
- mTuner->openDemux([&](Result, uint32_t demuxId, const sp<IDemux>& demux) {
+ Result res;
+ mTuner->openDemux([&](Result r, uint32_t demuxId, const sp<IDemux>& demux) {
mDemux = demux;
mDemuxId = demuxId;
+ res = r;
ALOGD("open demux, id = %d", demuxId);
});
- if (mDemux == nullptr) {
- return false;
+ return res;
+}
+
+jobject JTuner::getAvSyncHwId(sp<Filter> filter) {
+ if (mDemux == NULL) {
+ return NULL;
}
- return true;
+
+ uint32_t avSyncHwId;
+ Result res;
+ sp<IFilter> iFilterSp = filter->getIFilter();
+ mDemux->getAvSyncHwId(iFilterSp,
+ [&](Result r, uint32_t id) {
+ res = r;
+ avSyncHwId = id;
+ });
+ if (res == Result::SUCCESS) {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ jclass integerClazz = env->FindClass("java/lang/Integer");
+ jmethodID intInit = env->GetMethodID(integerClazz, "<init>", "(I)V");
+ return env->NewObject(integerClazz, intInit, avSyncHwId);
+ }
+ return NULL;
+}
+
+jobject JTuner::getAvSyncTime(jint id) {
+ if (mDemux == NULL) {
+ return NULL;
+ }
+ uint64_t time;
+ Result res;
+ mDemux->getAvSyncTime(static_cast<uint32_t>(id),
+ [&](Result r, uint64_t ts) {
+ res = r;
+ time = ts;
+ });
+ if (res == Result::SUCCESS) {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ jclass longClazz = env->FindClass("java/lang/Long");
+ jmethodID longInit = env->GetMethodID(longClazz, "<init>", "(J)V");
+ return env->NewObject(longClazz, longInit, static_cast<jlong>(time));
+ }
+ return NULL;
+}
+
+int JTuner::connectCiCam(jint id) {
+ if (mDemux == NULL) {
+ Result r = openDemux();
+ if (r != Result::SUCCESS) {
+ return (int) r;
+ }
+ }
+ Result r = mDemux->connectCiCam(static_cast<uint32_t>(id));
+ return (int) r;
+}
+
+int JTuner::disconnectCiCam() {
+ if (mDemux == NULL) {
+ Result r = openDemux();
+ if (r != Result::SUCCESS) {
+ return (int) r;
+ }
+ }
+ Result r = mDemux->disconnectCiCam();
+ return (int) r;
}
jobject JTuner::openDescrambler() {
@@ -892,7 +954,7 @@
jobject JTuner::openFilter(DemuxFilterType type, int bufferSize) {
if (mDemux == NULL) {
- if (!openDemux()) {
+ if (openDemux() != Result::SUCCESS) {
return NULL;
}
}
@@ -917,7 +979,6 @@
env->NewObject(
env->FindClass("android/media/tv/tuner/filter/Filter"),
gFields.filterInitID,
- mObject,
(jint) fId);
sp<Filter> filterSp = new Filter(iFilterSp, filterObj);
@@ -931,7 +992,7 @@
jobject JTuner::openTimeFilter() {
if (mDemux == NULL) {
- if (!openDemux()) {
+ if (openDemux() != Result::SUCCESS) {
return NULL;
}
}
@@ -962,7 +1023,7 @@
jobject JTuner::openDvr(DvrType type, int bufferSize) {
ALOGD("JTuner::openDvr");
if (mDemux == NULL) {
- if (!openDemux()) {
+ if (openDemux() != Result::SUCCESS) {
return NULL;
}
}
@@ -1608,20 +1669,30 @@
return NULL;
}
-static int android_media_tv_Tuner_gat_av_sync_hw_id(JNIEnv*, jobject, jobject) {
- return 0;
+static jobject android_media_tv_Tuner_get_av_sync_hw_id(
+ JNIEnv *env, jobject thiz, jobject filter) {
+ sp<Filter> filterSp = getFilter(env, filter);
+ if (filterSp == NULL) {
+ ALOGD("Failed to get sync ID. Filter not found");
+ return NULL;
+ }
+ sp<JTuner> tuner = getTuner(env, thiz);
+ return tuner->getAvSyncHwId(filterSp);
}
-static jlong android_media_tv_Tuner_gat_av_sync_time(JNIEnv*, jobject, jint) {
- return 0;
+static jobject android_media_tv_Tuner_get_av_sync_time(JNIEnv *env, jobject thiz, jint id) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ return tuner->getAvSyncTime(id);
}
-static int android_media_tv_Tuner_connect_cicam(JNIEnv*, jobject, jint) {
- return 0;
+static int android_media_tv_Tuner_connect_cicam(JNIEnv *env, jobject thiz, jint id) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ return tuner->connectCiCam(id);
}
-static int android_media_tv_Tuner_disconnect_cicam(JNIEnv*, jobject) {
- return 0;
+static int android_media_tv_Tuner_disconnect_cicam(JNIEnv *env, jobject thiz) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ return tuner->disconnectCiCam();
}
static jobject android_media_tv_Tuner_get_frontend_info(JNIEnv *env, jobject thiz, jint id) {
@@ -2500,9 +2571,10 @@
{ "nativeSetLna", "(Z)I", (void *)android_media_tv_Tuner_set_lna },
{ "nativeGetFrontendStatus", "([I)Landroid/media/tv/tuner/frontend/FrontendStatus;",
(void *)android_media_tv_Tuner_get_frontend_status },
- { "nativeGetAvSyncHwId", "(Landroid/media/tv/tuner/filter/Filter;)I",
- (void *)android_media_tv_Tuner_gat_av_sync_hw_id },
- { "nativeGetAvSyncTime", "(I)J", (void *)android_media_tv_Tuner_gat_av_sync_time },
+ { "nativeGetAvSyncHwId", "(Landroid/media/tv/tuner/filter/Filter;)Ljava/lang/Integer;",
+ (void *)android_media_tv_Tuner_get_av_sync_hw_id },
+ { "nativeGetAvSyncTime", "(I)Ljava/lang/Long;",
+ (void *)android_media_tv_Tuner_get_av_sync_time },
{ "nativeConnectCiCam", "(I)I", (void *)android_media_tv_Tuner_connect_cicam },
{ "nativeDisconnectCiCam", "()I", (void *)android_media_tv_Tuner_disconnect_cicam },
{ "nativeGetFrontendInfo", "(I)Landroid/media/tv/tuner/frontend/FrontendInfo;",
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index 32d4899..b786fc4 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -60,6 +60,7 @@
using ::android::hardware::tv::tuner::V1_0::LnbId;
using ::android::hardware::tv::tuner::V1_0::PlaybackStatus;
using ::android::hardware::tv::tuner::V1_0::RecordStatus;
+using ::android::hardware::tv::tuner::V1_0::Result;
using FilterMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
using DvrMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
@@ -141,6 +142,10 @@
struct JTuner : public RefBase {
JTuner(JNIEnv *env, jobject thiz);
sp<ITuner> getTunerService();
+ jobject getAvSyncHwId(sp<Filter> filter);
+ jobject getAvSyncTime(jint id);
+ int connectCiCam(jint id);
+ int disconnectCiCam();
jobject getFrontendIds();
jobject openFrontendById(int id);
jobject getFrontendInfo(int id);
@@ -158,7 +163,7 @@
jobject openDvr(DvrType type, int bufferSize);
protected:
- bool openDemux();
+ Result openDemux();
virtual ~JTuner();
private:
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
index bcff6a1..4e12859 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
@@ -123,7 +123,7 @@
@Test
public void testOnRoutesRemovedAndAdded() throws Exception {
- RouteCallback routeCallback = new RouteCallback();
+ RouteCallback routeCallback = new RouteCallback() {};
mRouteCallbacks.add(routeCallback);
mRouter2.registerRouteCallback(mExecutor, routeCallback,
new RouteDiscoveryPreference.Builder(FEATURES_ALL, true).build());
@@ -201,7 +201,7 @@
addManagerCallback(new MediaRouter2Manager.Callback());
//TODO: remove this when it's not necessary.
- addRouterCallback(new MediaRouter2.RouteCallback());
+ addRouterCallback(new MediaRouter2.RouteCallback() {});
addTransferCallback(new MediaRouter2.TransferCallback() {
@Override
public void onTransferred(MediaRouter2.RoutingController oldController,
@@ -228,7 +228,7 @@
CountDownLatch latch = new CountDownLatch(1);
Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
- addRouterCallback(new RouteCallback());
+ addRouterCallback(new RouteCallback() {});
addManagerCallback(new MediaRouter2Manager.Callback() {
@Override
public void onSessionCreated(MediaRouter2Manager.RoutingController controller) {
@@ -263,7 +263,7 @@
@Test
public void testSelectAndTransferAndRelease() throws Exception {
Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
- addRouterCallback(new RouteCallback());
+ addRouterCallback(new RouteCallback() {});
CountDownLatch onSessionCreatedLatch = new CountDownLatch(1);
@@ -346,7 +346,7 @@
@Test
public void testSetSessionVolume() throws Exception {
Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
- addRouterCallback(new RouteCallback());
+ addRouterCallback(new RouteCallback() {});
CountDownLatch onSessionCreatedLatch = new CountDownLatch(1);
CountDownLatch volumeChangedLatch = new CountDownLatch(2);
@@ -471,7 +471,7 @@
CountDownLatch featuresLatch = new CountDownLatch(1);
// A dummy callback is required to send route feature info.
- RouteCallback routeCallback = new RouteCallback();
+ RouteCallback routeCallback = new RouteCallback() {};
MediaRouter2Manager.Callback managerCallback = new MediaRouter2Manager.Callback() {
@Override
public void onRoutesAdded(List<MediaRoute2Info> routes) {
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
index 3a52015..b2c16b3 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
@@ -16,6 +16,12 @@
package com.android.systemui.navigationbar.car;
+import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.containsType;
+
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
+
import android.content.Context;
import android.graphics.PixelFormat;
import android.inputmethodservice.InputMethodService;
@@ -37,9 +43,12 @@
import com.android.systemui.car.CarDeviceProvisionedListener;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.statusbar.AutoHideUiElement;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NavigationBarController;
import com.android.systemui.statusbar.SuperStatusBarViewFactory;
+import com.android.systemui.statusbar.phone.AutoHideController;
+import com.android.systemui.statusbar.phone.BarTransitions;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -57,6 +66,7 @@
private final WindowManager mWindowManager;
private final CarDeviceProvisionedController mCarDeviceProvisionedController;
private final CommandQueue mCommandQueue;
+ private final AutoHideController mAutoHideController;
private final ButtonSelectionStateListener mButtonSelectionStateListener;
private final Handler mMainHandler;
private final Lazy<KeyguardStateController> mKeyguardStateControllerLazy;
@@ -64,6 +74,8 @@
private final SuperStatusBarViewFactory mSuperStatusBarViewFactory;
private final ButtonSelectionStateController mButtonSelectionStateController;
+ private final int mDisplayId;
+
private IStatusBarService mBarService;
private ActivityManagerWrapper mActivityManagerWrapper;
@@ -86,12 +98,16 @@
private boolean mDeviceIsSetUpForUser = true;
private boolean mIsUserSetupInProgress = false;
+ private @BarTransitions.TransitionMode int mNavigationBarMode;
+ private boolean mTransientShown;
+
@Inject
public CarNavigationBar(Context context,
CarNavigationBarController carNavigationBarController,
WindowManager windowManager,
DeviceProvisionedController deviceProvisionedController,
CommandQueue commandQueue,
+ AutoHideController autoHideController,
ButtonSelectionStateListener buttonSelectionStateListener,
@Main Handler mainHandler,
Lazy<KeyguardStateController> keyguardStateControllerLazy,
@@ -104,12 +120,15 @@
mCarDeviceProvisionedController = (CarDeviceProvisionedController)
deviceProvisionedController;
mCommandQueue = commandQueue;
+ mAutoHideController = autoHideController;
mButtonSelectionStateListener = buttonSelectionStateListener;
mMainHandler = mainHandler;
mKeyguardStateControllerLazy = keyguardStateControllerLazy;
mNavigationBarControllerLazy = navigationBarControllerLazy;
mSuperStatusBarViewFactory = superStatusBarViewFactory;
mButtonSelectionStateController = buttonSelectionStateController;
+
+ mDisplayId = mWindowManager.getDefaultDisplay().getDisplayId();
}
@Override
@@ -133,6 +152,23 @@
ex.rethrowFromSystemServer();
}
+ mAutoHideController.addAutoHideUiElement(new AutoHideUiElement() {
+ @Override
+ public void synchronizeState() {
+ // No op.
+ }
+
+ @Override
+ public boolean isVisible() {
+ return mTransientShown;
+ }
+
+ @Override
+ public void hide() {
+ clearTransient();
+ }
+ });
+
mDeviceIsSetUpForUser = mCarDeviceProvisionedController.isCurrentUserSetup();
mIsUserSetupInProgress = mCarDeviceProvisionedController.isCurrentUserSetupInProgress();
mCarDeviceProvisionedController.addCallback(
@@ -341,10 +377,57 @@
}
@Override
+ public void showTransient(int displayId, int[] types) {
+ if (displayId != mDisplayId) {
+ return;
+ }
+ if (!containsType(types, ITYPE_NAVIGATION_BAR)) {
+ return;
+ }
+
+ if (!mTransientShown) {
+ mTransientShown = true;
+ handleTransientChanged();
+ }
+ }
+
+ @Override
+ public void abortTransient(int displayId, int[] types) {
+ if (displayId != mDisplayId) {
+ return;
+ }
+ if (!containsType(types, ITYPE_NAVIGATION_BAR)) {
+ return;
+ }
+ clearTransient();
+ }
+
+ private void clearTransient() {
+ if (mTransientShown) {
+ mTransientShown = false;
+ handleTransientChanged();
+ }
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.print(" mTaskStackListener=");
pw.println(mButtonSelectionStateListener);
pw.print(" mBottomNavigationBarView=");
pw.println(mBottomNavigationBarView);
}
+
+ private void handleTransientChanged() {
+ updateBarMode(mTransientShown ? MODE_SEMI_TRANSPARENT : MODE_TRANSPARENT);
+ }
+
+ // Returns true if the bar mode is changed.
+ private boolean updateBarMode(int barMode) {
+ if (mNavigationBarMode != barMode) {
+ mNavigationBarMode = barMode;
+ mAutoHideController.touchAutoHide();
+ return true;
+ }
+ return false;
+ }
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index a4eada4..f39f912 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -122,6 +122,7 @@
import com.android.systemui.statusbar.phone.LockscreenWallpaper;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -324,6 +325,7 @@
KeyguardDismissUtil keyguardDismissUtil,
ExtensionController extensionController,
UserInfoControllerImpl userInfoControllerImpl,
+ PhoneStatusBarPolicy phoneStatusBarPolicy,
DismissCallbackRegistry dismissCallbackRegistry,
StatusBarTouchableRegionManager statusBarTouchableRegionManager,
/* Car Settings injected components. */
@@ -407,6 +409,7 @@
keyguardDismissUtil,
extensionController,
userInfoControllerImpl,
+ phoneStatusBarPolicy,
dismissCallbackRegistry,
statusBarTouchableRegionManager);
mUserSwitcherController = userSwitcherController;
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
index 7294965..843e7c5 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
@@ -82,6 +82,7 @@
import com.android.systemui.statusbar.phone.LockscreenWallpaper;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -197,6 +198,7 @@
KeyguardDismissUtil keyguardDismissUtil,
ExtensionController extensionController,
UserInfoControllerImpl userInfoControllerImpl,
+ PhoneStatusBarPolicy phoneStatusBarPolicy,
DismissCallbackRegistry dismissCallbackRegistry,
StatusBarTouchableRegionManager statusBarTouchableRegionManager,
CarServiceProvider carServiceProvider,
@@ -278,6 +280,7 @@
keyguardDismissUtil,
extensionController,
userInfoControllerImpl,
+ phoneStatusBarPolicy,
dismissCallbackRegistry,
statusBarTouchableRegionManager,
carServiceProvider,
diff --git a/packages/CompanionDeviceManager/AndroidManifest.xml b/packages/CompanionDeviceManager/AndroidManifest.xml
index a9c6685..ea9b52c 100644
--- a/packages/CompanionDeviceManager/AndroidManifest.xml
+++ b/packages/CompanionDeviceManager/AndroidManifest.xml
@@ -30,11 +30,13 @@
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-permission android:name="android.permission.RADIO_SCAN_WITHOUT_LOCATION"/>
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
<application
android:allowClearUserData="true"
android:label="@string/app_label"
android:allowBackup="false"
+ android:forceQueryable="true"
android:supportsRtl="true">
<service
diff --git a/packages/Incremental/NativeAdbDataLoader/Android.bp b/packages/Incremental/NativeAdbDataLoader/Android.bp
deleted file mode 100644
index 5d7b5b6..0000000
--- a/packages/Incremental/NativeAdbDataLoader/Android.bp
+++ /dev/null
@@ -1,22 +0,0 @@
-// 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.
-
-android_app {
- name: "NativeAdbDataLoaderService",
- srcs: ["src/**/*.java"],
- jni_libs: [ "libnativeadbdataloaderservice_jni"],
- privileged: true,
- certificate: "platform",
- platform_apis: true,
-}
diff --git a/packages/Incremental/NativeAdbDataLoader/AndroidManifest.xml b/packages/Incremental/NativeAdbDataLoader/AndroidManifest.xml
deleted file mode 100644
index c4d8f35..0000000
--- a/packages/Incremental/NativeAdbDataLoader/AndroidManifest.xml
+++ /dev/null
@@ -1,39 +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.
-*/
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- coreApp="true"
- package="com.android.incremental.nativeadb"
- android:sharedUserId="android.uid.system">
- <uses-permission android:name="android.permission.INTERNET" />
-
- <application android:label="@string/app_name"
- android:directBootAware="true">
-
- <service android:enabled="true"
- android:name="com.android.incremental.nativeadb.NativeAdbDataLoaderService"
- android:label="@string/app_name"
- android:exported="false">
- <intent-filter>
- <action android:name="android.intent.action.LOAD_DATA" />
- </intent-filter>
- </service>
- </application>
-
-</manifest>
diff --git a/packages/Incremental/NativeAdbDataLoader/jni/Android.bp b/packages/Incremental/NativeAdbDataLoader/jni/Android.bp
deleted file mode 100644
index 0fcfd28..0000000
--- a/packages/Incremental/NativeAdbDataLoader/jni/Android.bp
+++ /dev/null
@@ -1,37 +0,0 @@
-// 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.
-
-cc_library_shared {
- name: "libnativeadbdataloaderservice_jni",
- cpp_std: "c++2a",
- cflags: [
- "-Wall",
- "-Werror",
- "-Wunused",
- "-Wunreachable-code",
- "-Wno-unused-parameter",
- ],
-
- srcs: ["com_android_incremental_nativeadb_DataLoaderService.cpp"],
-
- shared_libs: [
- "libbase",
- "libcutils",
- "libincfs",
- "libdataloader",
- "liblog",
- "libnativehelper",
- "libutils",
- ],
-}
diff --git a/packages/Incremental/NativeAdbDataLoader/jni/com_android_incremental_nativeadb_DataLoaderService.cpp b/packages/Incremental/NativeAdbDataLoader/jni/com_android_incremental_nativeadb_DataLoaderService.cpp
deleted file mode 100644
index c0cd527..0000000
--- a/packages/Incremental/NativeAdbDataLoader/jni/com_android_incremental_nativeadb_DataLoaderService.cpp
+++ /dev/null
@@ -1,525 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-#define ATRACE_TAG ATRACE_TAG_ADB
-#define LOG_TAG "NativeAdbDataLoaderService"
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
-#include <android-base/thread_annotations.h>
-#include <android-base/unique_fd.h>
-#include <cutils/trace.h>
-#include <fcntl.h>
-#include <sys/eventfd.h>
-#include <sys/poll.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <utils/Log.h>
-
-#include <charconv>
-#include <span>
-#include <string>
-#include <thread>
-#include <type_traits>
-#include <unordered_map>
-#include <unordered_set>
-
-#include "dataloader.h"
-
-#ifndef _WIN32
-#include <endian.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#else
-#define be32toh(x) _byteswap_ulong(x)
-#define be16toh(x) _byteswap_ushort(x)
-#endif
-
-namespace {
-
-using android::base::unique_fd;
-
-using namespace std::literals;
-
-using BlockSize = int16_t;
-using FileId = int16_t;
-using BlockIdx = int32_t;
-using NumBlocks = int32_t;
-using CompressionType = int16_t;
-using RequestType = int16_t;
-
-static constexpr int COMMAND_SIZE = 2 + 2 + 4; // bytes
-static constexpr int HEADER_SIZE = 2 + 2 + 4 + 2; // bytes
-static constexpr std::string_view OKAY = "OKAY"sv;
-
-static constexpr auto PollTimeoutMs = 5000;
-
-static constexpr auto ReadLogBufferSize = 128 * 1024 * 1024;
-static constexpr auto ReadLogMaxEntrySize = 128;
-
-struct BlockHeader {
- FileId fileId = -1;
- CompressionType compressionType = -1;
- BlockIdx blockIdx = -1;
- BlockSize blockSize = -1;
-} __attribute__((packed));
-
-static_assert(sizeof(BlockHeader) == HEADER_SIZE);
-
-static constexpr RequestType EXIT = 0;
-static constexpr RequestType BLOCK_MISSING = 1;
-static constexpr RequestType PREFETCH = 2;
-
-struct RequestCommand {
- RequestType requestType;
- FileId fileId;
- BlockIdx blockIdx;
-} __attribute__((packed));
-
-static_assert(COMMAND_SIZE == sizeof(RequestCommand));
-
-static bool sendRequest(int fd, RequestType requestType, FileId fileId = -1,
- BlockIdx blockIdx = -1) {
- const RequestCommand command{.requestType = static_cast<int16_t>(be16toh(requestType)),
- .fileId = static_cast<int16_t>(be16toh(fileId)),
- .blockIdx = static_cast<int32_t>(be32toh(blockIdx))};
- return android::base::WriteFully(fd, &command, sizeof(command));
-}
-
-static int waitForDataOrSignal(int fd, int event_fd) {
- struct pollfd pfds[2] = {{fd, POLLIN, 0}, {event_fd, POLLIN, 0}};
- // Wait indefinitely until either data is ready or stop signal is received
- int res = poll(pfds, 2, PollTimeoutMs);
- if (res <= 0) {
- return res;
- }
- // First check if there is a stop signal
- if (pfds[1].revents == POLLIN) {
- return event_fd;
- }
- // Otherwise check if incoming data is ready
- if (pfds[0].revents == POLLIN) {
- return fd;
- }
- return -1;
-}
-
-static bool readChunk(int fd, std::vector<uint8_t>& data) {
- int32_t size;
- if (!android::base::ReadFully(fd, &size, sizeof(size))) {
- return false;
- }
- size = int32_t(be32toh(size));
- if (size <= 0) {
- return false;
- }
- data.resize(size);
- return android::base::ReadFully(fd, data.data(), data.size());
-}
-
-static BlockHeader readHeader(std::span<uint8_t>& data) {
- BlockHeader header;
- if (data.size() < sizeof(header)) {
- return header;
- }
-
- header.fileId = static_cast<FileId>(be16toh(*reinterpret_cast<uint16_t*>(&data[0])));
- header.compressionType =
- static_cast<CompressionType>(be16toh(*reinterpret_cast<uint16_t*>(&data[2])));
- header.blockIdx = static_cast<BlockIdx>(be32toh(*reinterpret_cast<uint32_t*>(&data[4])));
- header.blockSize = static_cast<BlockSize>(be16toh(*reinterpret_cast<uint16_t*>(&data[8])));
- data = data.subspan(sizeof(header));
-
- return header;
-}
-
-static std::string extractPackageName(const std::string& staticArgs) {
- static constexpr auto kPrefix = "package="sv;
- static constexpr auto kSuffix = "&"sv;
-
- const auto startPos = staticArgs.find(kPrefix);
- if (startPos == staticArgs.npos || startPos + kPrefix.size() >= staticArgs.size()) {
- return {};
- }
- const auto endPos = staticArgs.find(kSuffix, startPos + kPrefix.size());
- return staticArgs.substr(startPos + kPrefix.size(),
- endPos == staticArgs.npos ? staticArgs.npos
- : (endPos - (startPos + kPrefix.size())));
-}
-
-class AdbDataLoader : public android::dataloader::DataLoader {
-private:
- // Lifecycle.
- bool onCreate(const android::dataloader::DataLoaderParams& params,
- android::dataloader::FilesystemConnectorPtr ifs,
- android::dataloader::StatusListenerPtr statusListener,
- android::dataloader::ServiceConnectorPtr,
- android::dataloader::ServiceParamsPtr) final {
- CHECK(ifs) << "ifs can't be null";
- CHECK(statusListener) << "statusListener can't be null";
- ALOGE("[AdbDataLoader] onCreate: %d/%s/%s/%s/%d", params.type(),
- params.packageName().c_str(), params.className().c_str(), params.arguments().c_str(),
- (int)params.dynamicArgs().size());
-
- if (params.dynamicArgs().empty()) {
- ALOGE("[AdbDataLoader] Invalid DataLoaderParams. Need in/out FDs.");
- return false;
- }
- for (auto const& namedFd : params.dynamicArgs()) {
- if (namedFd.name == "inFd") {
- mInFd.reset(dup(namedFd.fd));
- }
- if (namedFd.name == "outFd") {
- mOutFd.reset(dup(namedFd.fd));
- }
- }
- if (mInFd < 0 || mOutFd < 0) {
- ALOGE("[AdbDataLoader] Failed to dup FDs.");
- return false;
- }
-
- mEventFd.reset(eventfd(0, EFD_CLOEXEC));
- if (mEventFd < 0) {
- ALOGE("[AdbDataLoader] Failed to create eventfd.");
- return false;
- }
-
- std::string logFile;
- if (const auto packageName = extractPackageName(params.arguments()); !packageName.empty()) {
- logFile = android::base::GetProperty("adb.readlog." + packageName, "");
- }
- if (logFile.empty()) {
- logFile = android::base::GetProperty("adb.readlog", "");
- }
- if (!logFile.empty()) {
- int flags = O_WRONLY | O_CREAT | O_CLOEXEC;
- mReadLogFd.reset(TEMP_FAILURE_RETRY(open(logFile.c_str(), flags, 0666)));
- }
-
- mIfs = ifs;
- mStatusListener = statusListener;
- ALOGE("[AdbDataLoader] Successfully created data loader.");
- return true;
- }
-
- bool onStart() final {
- char okay_buf[OKAY.size()];
- if (!android::base::ReadFully(mInFd, okay_buf, OKAY.size())) {
- ALOGE("[AdbDataLoader] Failed to receive OKAY. Abort.");
- return false;
- }
- if (std::string_view(okay_buf, OKAY.size()) != OKAY) {
- ALOGE("[AdbDataLoader] Received '%.*s', expecting '%.*s'", (int)OKAY.size(), okay_buf,
- (int)OKAY.size(), OKAY.data());
- return false;
- }
-
- mReceiverThread = std::thread([this]() { receiver(); });
- ALOGI("[AdbDataLoader] started loading...");
- return true;
- }
-
- void onStop() final {
- mStopReceiving = true;
- eventfd_write(mEventFd, 1);
- if (mReceiverThread.joinable()) {
- mReceiverThread.join();
- }
- }
-
- void onDestroy() final {
- ALOGE("[AdbDataLoader] Sending EXIT to server.");
- sendRequest(mOutFd, EXIT);
- // Make sure the receiver thread was stopped
- CHECK(!mReceiverThread.joinable());
-
- mInFd.reset();
- mOutFd.reset();
-
- mNodeToMetaMap.clear();
- mIdToNodeMap.clear();
-
- flushReadLog();
- mReadLogFd.reset();
- }
-
- // Installation callback
- bool onPrepareImage(const android::dataloader::DataLoaderInstallationFiles& addedFiles) final {
- return true;
- }
-
- // IFS callbacks.
- void onPendingReads(const android::dataloader::PendingReads& pendingReads) final {
- std::lock_guard lock{mMapsMutex};
- CHECK(mIfs);
- for (auto&& pendingRead : pendingReads) {
- const android::dataloader::FileId id = pendingRead.id;
- const auto blockIdx = static_cast<BlockIdx>(pendingRead.block);
- /*
- ALOGI("[AdbDataLoader] Missing: %d", (int) blockIdx);
- */
- auto fileIdOr = getFileId(id);
- if (!fileIdOr) {
- ALOGE("[AdbDataLoader] Failed to handle event for fileid=%s. "
- "Ignore.",
- android::incfs::toString(id).c_str());
- continue;
- }
- const FileId fileId = *fileIdOr;
- if (mRequestedFiles.insert(fileId).second) {
- if (!sendRequest(mOutFd, PREFETCH, fileId, blockIdx)) {
- ALOGE("[AdbDataLoader] Failed to request prefetch for "
- "fileid=%s. Ignore.",
- android::incfs::toString(id).c_str());
- mRequestedFiles.erase(fileId);
- mStatusListener->reportStatus(DATA_LOADER_NO_CONNECTION);
- }
- }
- sendRequest(mOutFd, BLOCK_MISSING, fileId, blockIdx);
- }
- }
-
- struct TracedRead {
- uint64_t timestampUs;
- android::dataloader::FileId fileId;
- uint32_t firstBlockIdx;
- uint32_t count;
- };
- void onPageReads(const android::dataloader::PageReads& pageReads) final {
- auto trace = atrace_is_tag_enabled(ATRACE_TAG);
- auto log = mReadLogFd != -1;
- if (CC_LIKELY(!(trace || log))) {
- return;
- }
-
- TracedRead last = {};
- std::lock_guard lock{mMapsMutex};
- for (auto&& read : pageReads) {
- if (read.id != last.fileId || read.block != last.firstBlockIdx + last.count) {
- traceOrLogRead(last, trace, log);
- last = {read.bootClockTsUs, read.id, (uint32_t)read.block, 1};
- } else {
- ++last.count;
- }
- }
- traceOrLogRead(last, trace, log);
- }
- void onFileCreated(android::dataloader::FileId fileid,
- const android::dataloader::RawMetadata& metadata) {}
-
-private:
- void receiver() {
- std::vector<uint8_t> data;
- std::vector<IncFsDataBlock> instructions;
- std::unordered_map<android::dataloader::FileId, unique_fd> writeFds;
- while (!mStopReceiving) {
- const int res = waitForDataOrSignal(mInFd, mEventFd);
- if (res == 0) {
- flushReadLog();
- continue;
- }
- if (res < 0) {
- ALOGE("[AdbDataLoader] failed to poll. Abort.");
- mStatusListener->reportStatus(DATA_LOADER_NO_CONNECTION);
- break;
- }
- if (res == mEventFd) {
- ALOGE("[AdbDataLoader] received stop signal. Exit.");
- break;
- }
- if (!readChunk(mInFd, data)) {
- ALOGE("[AdbDataLoader] failed to read a message. Abort.");
- mStatusListener->reportStatus(DATA_LOADER_NO_CONNECTION);
- break;
- }
- auto remainingData = std::span(data);
- while (!remainingData.empty()) {
- auto header = readHeader(remainingData);
- if (header.fileId == -1 && header.compressionType == 0 && header.blockIdx == 0 &&
- header.blockSize == 0) {
- ALOGI("[AdbDataLoader] stop signal received. Sending "
- "exit command (remaining bytes: %d).",
- int(remainingData.size()));
-
- sendRequest(mOutFd, EXIT);
- mStopReceiving = true;
- break;
- }
- if (header.fileId < 0 || header.blockSize <= 0 || header.compressionType < 0 ||
- header.blockIdx < 0) {
- ALOGE("[AdbDataLoader] invalid header received. Abort.");
- mStopReceiving = true;
- break;
- }
- const android::dataloader::FileId id = mIdToNodeMap[header.fileId];
- if (!android::incfs::isValidFileId(id)) {
- ALOGE("Unknown data destination for file ID %d. "
- "Ignore.",
- header.fileId);
- continue;
- }
-
- auto& writeFd = writeFds[id];
- if (writeFd < 0) {
- writeFd = this->mIfs->openWrite(id);
- if (writeFd < 0) {
- ALOGE("Failed to open file %d for writing (%d). Aboring.", header.fileId,
- -writeFd);
- break;
- }
- }
-
- const auto inst = IncFsDataBlock{
- .fileFd = writeFd,
- .pageIndex = static_cast<IncFsBlockIndex>(header.blockIdx),
- .compression = static_cast<IncFsCompressionKind>(header.compressionType),
- .kind = INCFS_BLOCK_KIND_DATA,
- .dataSize = static_cast<uint16_t>(header.blockSize),
- .data = (const char*)remainingData.data(),
- };
- instructions.push_back(inst);
- remainingData = remainingData.subspan(header.blockSize);
- }
- writeInstructions(instructions);
- }
- writeInstructions(instructions);
- flushReadLog();
- }
-
- void writeInstructions(std::vector<IncFsDataBlock>& instructions) {
- auto res = this->mIfs->writeBlocks(instructions);
- if (res != instructions.size()) {
- ALOGE("[AdbDataLoader] failed to write data to Incfs (res=%d when "
- "expecting %d)",
- res, int(instructions.size()));
- }
- instructions.clear();
- }
-
- struct MetaPair {
- android::dataloader::RawMetadata meta;
- FileId fileId;
- };
-
- MetaPair* updateMapsForFile(android::dataloader::FileId id) {
- android::dataloader::RawMetadata meta = mIfs->getRawMetadata(id);
- FileId fileId;
- auto res = std::from_chars(meta.data(), meta.data() + meta.size(), fileId);
- if (res.ec != std::errc{} || fileId < 0) {
- ALOGE("[AdbDataLoader] Invalid metadata for fileid=%s (%s)",
- android::incfs::toString(id).c_str(), meta.data());
- return nullptr;
- }
- mIdToNodeMap[fileId] = id;
- auto& metaPair = mNodeToMetaMap[id];
- metaPair.meta = std::move(meta);
- metaPair.fileId = fileId;
- return &metaPair;
- }
-
- android::dataloader::RawMetadata* getMeta(android::dataloader::FileId id) {
- auto it = mNodeToMetaMap.find(id);
- if (it != mNodeToMetaMap.end()) {
- return &it->second.meta;
- }
-
- auto metaPair = updateMapsForFile(id);
- if (!metaPair) {
- return nullptr;
- }
-
- return &metaPair->meta;
- }
-
- FileId* getFileId(android::dataloader::FileId id) {
- auto it = mNodeToMetaMap.find(id);
- if (it != mNodeToMetaMap.end()) {
- return &it->second.fileId;
- }
-
- auto* metaPair = updateMapsForFile(id);
- if (!metaPair) {
- return nullptr;
- }
-
- return &metaPair->fileId;
- }
-
- void traceOrLogRead(const TracedRead& read, bool trace, bool log) {
- if (!read.count) {
- return;
- }
- if (trace) {
- auto* meta = getMeta(read.fileId);
- auto str = android::base::StringPrintf("page_read: index=%lld count=%lld meta=%.*s",
- static_cast<long long>(read.firstBlockIdx),
- static_cast<long long>(read.count),
- meta ? int(meta->size()) : 0,
- meta ? meta->data() : "");
- ATRACE_BEGIN(str.c_str());
- ATRACE_END();
- }
- if (log) {
- mReadLog.reserve(ReadLogBufferSize);
-
- auto fileId = getFileId(read.fileId);
- android::base::StringAppendF(&mReadLog, "%lld:%lld:%lld:%lld\n",
- static_cast<long long>(read.timestampUs),
- static_cast<long long>(fileId ? *fileId : -1),
- static_cast<long long>(read.firstBlockIdx),
- static_cast<long long>(read.count));
-
- if (mReadLog.size() >= mReadLog.capacity() - ReadLogMaxEntrySize) {
- flushReadLog();
- }
- }
- }
-
- void flushReadLog() {
- if (mReadLog.empty() || mReadLogFd == -1) {
- return;
- }
-
- android::base::WriteStringToFd(mReadLog, mReadLogFd);
- mReadLog.clear();
- }
-
-private:
- android::dataloader::FilesystemConnectorPtr mIfs = nullptr;
- android::dataloader::StatusListenerPtr mStatusListener = nullptr;
- android::base::unique_fd mInFd;
- android::base::unique_fd mOutFd;
- android::base::unique_fd mEventFd;
- android::base::unique_fd mReadLogFd;
- std::string mReadLog;
- std::thread mReceiverThread;
- std::mutex mMapsMutex;
- std::unordered_map<android::dataloader::FileId, MetaPair> mNodeToMetaMap GUARDED_BY(mMapsMutex);
- std::unordered_map<FileId, android::dataloader::FileId> mIdToNodeMap GUARDED_BY(mMapsMutex);
- /** Tracks which files have been requested */
- std::unordered_set<FileId> mRequestedFiles;
- std::atomic<bool> mStopReceiving = false;
-};
-
-} // namespace
-
-int JNI_OnLoad(JavaVM* jvm, void* /* reserved */) {
- android::dataloader::DataLoader::initialize(
- [](auto, auto) { return std::make_unique<AdbDataLoader>(); });
- return JNI_VERSION_1_6;
-}
diff --git a/packages/Incremental/NativeAdbDataLoader/src/com/android/incremental/nativeadb/NativeAdbDataLoaderService.java b/packages/Incremental/NativeAdbDataLoader/src/com/android/incremental/nativeadb/NativeAdbDataLoaderService.java
deleted file mode 100644
index c4e41c8..0000000
--- a/packages/Incremental/NativeAdbDataLoader/src/com/android/incremental/nativeadb/NativeAdbDataLoaderService.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package com.android.incremental.nativeadb;
-
-import android.annotation.NonNull;
-import android.content.pm.DataLoaderParams;
-import android.service.dataloader.DataLoaderService;
-
-/** This code is used for testing only. */
-public class NativeAdbDataLoaderService extends DataLoaderService {
- public static final String TAG = "NativeAdbDataLoaderService";
- static {
- System.loadLibrary("nativeadbdataloaderservice_jni");
- }
-
- @Override
- public DataLoader onCreateDataLoader(@NonNull DataLoaderParams dataLoaderParams) {
- return null;
- }
-}
diff --git a/packages/Incremental/NativeAdbDataLoader/res/values/strings.xml b/packages/SettingsLib/SchedulesProvider/res/values/config.xml
similarity index 70%
rename from packages/Incremental/NativeAdbDataLoader/res/values/strings.xml
rename to packages/SettingsLib/SchedulesProvider/res/values/config.xml
index 9921ae6..48f3e3e 100644
--- a/packages/Incremental/NativeAdbDataLoader/res/values/strings.xml
+++ b/packages/SettingsLib/SchedulesProvider/res/values/config.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 The Android Open Source Project
+<!-- Copyright (C) 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Name of the Data Loader Service. [CHAR LIMIT=40] -->
- <string name="app_name">Native Adb Data Loader Service</string>
-</resources>
+<resources>
+ <!-- Package name for the caller of the Schedules provider. -->
+ <string name="config_schedules_provider_caller_package" translatable="false">com.android.settings</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/ScheduleInfo.java b/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/ScheduleInfo.java
index 7d2b8e2..26bcd54 100644
--- a/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/ScheduleInfo.java
+++ b/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/ScheduleInfo.java
@@ -24,9 +24,9 @@
import androidx.annotation.NonNull;
/**
- * This is a schedule data item. It contains the schedule title text, the summary text which
- * displays on the summary of the Settings preference and an {@link Intent}. Intent is able to
- * launch the editing page of the schedule data when user clicks this item (preference).
+ * Schedule data item containing the schedule title text, the summary text which is displayed on the
+ * summary of the Settings preference and an {@link Intent} which Settings will launch when the
+ * user clicks on the preference.
*/
public class ScheduleInfo implements Parcelable {
private static final String TAG = "ScheduleInfo";
@@ -40,7 +40,7 @@
mIntent = builder.mIntent;
}
- protected ScheduleInfo(Parcel in) {
+ private ScheduleInfo(Parcel in) {
mTitle = in.readString();
mSummary = in.readString();
mIntent = in.readParcelable(Intent.class.getClassLoader());
@@ -48,8 +48,6 @@
/**
* Returns the title text.
- *
- * @return The title.
*/
public String getTitle() {
return mTitle;
@@ -57,15 +55,14 @@
/**
* Returns the summary text.
- *
- * @return The summary.
*/
public String getSummary() {
return mSummary;
}
/**
- * Returns an {@link Intent}.
+ * Returns an {@link Intent} which Settings will launch when the user clicks on a schedule
+ * preference.
*/
public Intent getIntent() {
return mIntent;
@@ -107,19 +104,15 @@
@NonNull
@Override
public String toString() {
- return "title : " + mTitle + " summary : " + mSummary + (mIntent == null
- ? " and intent is null." : ".");
+ return "title: " + mTitle + ", summary: " + mSummary + ", intent: " + mIntent;
}
/**
* A simple builder for {@link ScheduleInfo}.
*/
public static class Builder {
- @NonNull
private String mTitle;
- @NonNull
private String mSummary;
- @NonNull
private Intent mIntent;
/**
diff --git a/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/SchedulesProvider.java b/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/SchedulesProvider.java
index a423e47..28d5f07 100644
--- a/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/SchedulesProvider.java
+++ b/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/SchedulesProvider.java
@@ -21,19 +21,18 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.SystemProperties;
+import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.ArrayList;
-import java.util.List;
import java.util.stream.Collectors;
/**
- * This provider is a bridge for client apps to provide the schedule data.
- * Client provider needs to implement their {@link #getScheduleInfoList()} and returns a list of
- * {@link ScheduleInfo}.
+ * A bridge for client apps to provide the schedule data. Client provider needs to implement
+ * {@link #getScheduleInfoList()} returning a list of {@link ScheduleInfo}.
*/
public abstract class SchedulesProvider extends ContentProvider {
public static final String METHOD_GENERATE_SCHEDULE_INFO_LIST = "generateScheduleInfoList";
@@ -46,9 +45,8 @@
}
@Override
- public final Cursor query(
- Uri uri, String[] projection, String selection, String[] selectionArgs,
- String sortOrder) {
+ public final Cursor query(Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder) {
throw new UnsupportedOperationException("Query operation is not supported currently.");
}
@@ -74,18 +72,24 @@
}
/**
- * Return the list of the schedule information.
- *
- * @return a list of the {@link ScheduleInfo}.
+ * Returns the list of the schedule information.
*/
public abstract ArrayList<ScheduleInfo> getScheduleInfoList();
/**
- * Returns a bundle which contains a list of {@link ScheduleInfo} and data types:
- * scheduleInfoList : ArrayList<ScheduleInfo>
+ * Returns a bundle which contains a list of {@link ScheduleInfo}s:
+ *
+ * <ul>
+ * <li>scheduleInfoList: ArrayList<ScheduleInfo>
+ * </ul>
*/
@Override
public Bundle call(@NonNull String method, @Nullable String arg, @Nullable Bundle extras) {
+ if (!TextUtils.equals(getCallingPackage(),
+ getContext().getText(R.string.config_schedules_provider_caller_package))) {
+ return null;
+ }
+
final Bundle bundle = new Bundle();
if (METHOD_GENERATE_SCHEDULE_INFO_LIST.equals(method)) {
final ArrayList<ScheduleInfo> scheduleInfoList = filterInvalidData(
@@ -98,36 +102,40 @@
}
/**
- * To filter the invalid schedule info.
+ * Filters our invalid schedule infos from {@code schedulesInfoList}.
*
- * @param scheduleInfoList The list of the {@link ScheduleInfo}.
- * @return The valid list of the {@link ScheduleInfo}.
+ * @return valid {@link SchedulesInfo}s if {@code schedulesInfoList} is not null. Otherwise,
+ * null.
*/
- private ArrayList<ScheduleInfo> filterInvalidData(ArrayList<ScheduleInfo> scheduleInfoList) {
+ @Nullable
+ private ArrayList<ScheduleInfo> filterInvalidData(
+ @Nullable ArrayList<ScheduleInfo> scheduleInfoList) {
if (scheduleInfoList == null) {
Log.d(TAG, "package : " + getContext().getPackageName() + " has no scheduling data.");
return null;
}
// Dump invalid data in debug mode.
if (SystemProperties.getInt("ro.debuggable", 0) == 1) {
- new Thread(() -> {
- dumpInvalidData(scheduleInfoList);
- }).start();
+ dumpInvalidData(scheduleInfoList);
}
- final List<ScheduleInfo> filteredList = scheduleInfoList
+ return scheduleInfoList
.stream()
- .filter(scheduleInfo -> scheduleInfo.isValid())
- .collect(Collectors.toList());
-
- return new ArrayList<>(filteredList);
+ .filter(ScheduleInfo::isValid)
+ .collect(Collectors.toCollection(ArrayList::new));
}
private void dumpInvalidData(ArrayList<ScheduleInfo> scheduleInfoList) {
- Log.d(TAG, "package : " + getContext().getPackageName()
- + " provided some scheduling data are invalid.");
- scheduleInfoList
+ final boolean hasInvalidData = scheduleInfoList
.stream()
- .filter(scheduleInfo -> !scheduleInfo.isValid())
- .forEach(scheduleInfo -> Log.d(TAG, scheduleInfo.toString()));
+ .anyMatch(scheduleInfo -> !scheduleInfo.isValid());
+
+ if (hasInvalidData) {
+ Log.w(TAG, "package : " + getContext().getPackageName()
+ + " provided some scheduling data that are invalid.");
+ scheduleInfoList
+ .stream()
+ .filter(scheduleInfo -> !scheduleInfo.isValid())
+ .forEach(scheduleInfo -> Log.w(TAG, scheduleInfo.toString()));
+ }
}
}
diff --git a/packages/SettingsLib/res/values/colors.xml b/packages/SettingsLib/res/values/colors.xml
index 5e8779f..88c6185 100644
--- a/packages/SettingsLib/res/values/colors.xml
+++ b/packages/SettingsLib/res/values/colors.xml
@@ -39,4 +39,7 @@
<color name="dark_mode_icon_color_single_tone">#99000000</color>
<color name="light_mode_icon_color_single_tone">#ffffff</color>
+
+ <!-- Yellow 600, used for highlighting "important" conversations in settings & notifications -->
+ <color name="important_conversation">#f9ab00</color>
</resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 6d11461..e8e1d0b 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -892,10 +892,10 @@
<!-- UI debug setting: enable gpu debug layers summary [CHAR LIMIT=50] -->
<string name="enable_gpu_debug_layers_summary">Allow loading GPU debug layers for debug apps</string>
- <!-- UI debug setting: enable verbose vendor logging [CHAR LIMIT=30] -->
+ <!-- UI debug setting: enable verbose vendor logging [CHAR LIMIT=60] -->
<string name="enable_verbose_vendor_logging">Enable verbose vendor logging</string>
- <!-- UI debug setting: enable verbose vendor logging summary [CHAR LIMIT=100] -->
- <string name="enable_verbose_vendor_logging_summary">Allow additional vendor logs to be included in bug reports, may contain private information</string>
+ <!-- UI debug setting: enable verbose vendor logging summary [CHAR LIMIT=NONE] -->
+ <string name="enable_verbose_vendor_logging_summary">Include additional device-specific vendor logs in bug reports, which may contain private information, use more battery, and/or use more storage.</string>
<!-- UI debug setting: scaling factor for window animations [CHAR LIMIT=25] -->
<string name="window_animation_scale_title">Window animation scale</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index f69e4f5..3ae9e1e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -15,9 +15,15 @@
*/
package com.android.settingslib.media;
-import static android.media.MediaRoute2Info.DEVICE_TYPE_BLUETOOTH;
-import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_TV;
-import static android.media.MediaRoute2Info.DEVICE_TYPE_UNKNOWN;
+import static android.media.MediaRoute2Info.TYPE_BLUETOOTH_A2DP;
+import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER;
+import static android.media.MediaRoute2Info.TYPE_GROUP;
+import static android.media.MediaRoute2Info.TYPE_HEARING_AID;
+import static android.media.MediaRoute2Info.TYPE_REMOTE_SPEAKER;
+import static android.media.MediaRoute2Info.TYPE_REMOTE_TV;
+import static android.media.MediaRoute2Info.TYPE_UNKNOWN;
+import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
+import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
import android.app.Notification;
import android.bluetooth.BluetoothAdapter;
@@ -165,6 +171,26 @@
}
/**
+ * Release session to stop playing media on MediaDevice.
+ */
+ boolean releaseSession() {
+ if (TextUtils.isEmpty(mPackageName)) {
+ Log.w(TAG, "releaseSession() package name is null or empty!");
+ return false;
+ }
+
+ final RoutingSessionInfo info = getRoutingSessionInfo();
+ if (info != null) {
+ mRouterManager.getControllerForSession(info).release();
+ return true;
+ }
+
+ Log.w(TAG, "releaseSession() Ignoring release session : " + mPackageName);
+
+ return false;
+ }
+
+ /**
* Get the MediaDevice list that can be added to current media.
*
* @return list of MediaDevice
@@ -298,7 +324,9 @@
private void buildAllRoutes() {
for (MediaRoute2Info route : mRouterManager.getAllRoutes()) {
- addMediaDevice(route);
+ if (route.isSystemRoute()) {
+ addMediaDevice(route);
+ }
}
}
@@ -309,27 +337,29 @@
}
private void addMediaDevice(MediaRoute2Info route) {
- final int deviceType = route.getDeviceType();
+ final int deviceType = route.getType();
MediaDevice mediaDevice = null;
switch (deviceType) {
- case DEVICE_TYPE_UNKNOWN:
+ case TYPE_UNKNOWN:
+ case TYPE_REMOTE_TV:
+ case TYPE_REMOTE_SPEAKER:
+ case TYPE_GROUP:
//TODO(b/148765806): use correct device type once api is ready.
- final String defaultRoute = "DEFAULT_ROUTE";
- if (TextUtils.equals(defaultRoute, route.getOriginalId())) {
- mediaDevice =
- new PhoneMediaDevice(mContext, mRouterManager, route, mPackageName);
- } else {
- mediaDevice = new InfoMediaDevice(mContext, mRouterManager, route,
- mPackageName);
- if (!TextUtils.isEmpty(mPackageName)
- && TextUtils.equals(route.getClientPackageName(), mPackageName)) {
- mCurrentConnectedDevice = mediaDevice;
- }
+ mediaDevice = new InfoMediaDevice(mContext, mRouterManager, route,
+ mPackageName);
+ if (!TextUtils.isEmpty(mPackageName)
+ && TextUtils.equals(route.getClientPackageName(), mPackageName)) {
+ mCurrentConnectedDevice = mediaDevice;
}
break;
- case DEVICE_TYPE_REMOTE_TV:
+ case TYPE_BUILTIN_SPEAKER:
+ case TYPE_WIRED_HEADSET:
+ case TYPE_WIRED_HEADPHONES:
+ mediaDevice =
+ new PhoneMediaDevice(mContext, mRouterManager, route, mPackageName);
break;
- case DEVICE_TYPE_BLUETOOTH:
+ case TYPE_HEARING_AID:
+ case TYPE_BLUETOOTH_A2DP:
final BluetoothDevice device =
BluetoothAdapter.getDefaultAdapter().getRemoteDevice(route.getOriginalId());
final CachedBluetoothDevice cachedDevice =
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 617da6e..c70811f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -282,6 +282,13 @@
}
/**
+ * Release session to stop playing media on MediaDevice.
+ */
+ public boolean releaseSession() {
+ return mInfoMediaManager.releaseSession();
+ }
+
+ /**
* Get the MediaDevice list that has been selected to current media.
*
* @return list of MediaDevice
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/ConversationIconFactory.java b/packages/SettingsLib/src/com/android/settingslib/notification/ConversationIconFactory.java
index 885b7d3..9dc454f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/ConversationIconFactory.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/ConversationIconFactory.java
@@ -15,32 +15,48 @@
*/
package com.android.settingslib.notification;
+import android.annotation.ColorInt;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
-import android.graphics.Bitmap;
import android.graphics.Canvas;
-import android.graphics.drawable.BitmapDrawable;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import android.util.IconDrawableFactory;
+import android.util.Log;
import com.android.launcher3.icons.BaseIconFactory;
-import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.icons.ShadowGenerator;
+import com.android.settingslib.R;
/**
* Factory for creating normalized conversation icons.
* We are not using Launcher's IconFactory because conversation rendering only runs on the UI
- * thread, so there is no need to manage a pool across multiple threads.
+ * thread, so there is no need to manage a pool across multiple threads. Launcher's rendering
+ * also includes shadows, which are only appropriate on top of wallpaper, not embedded in UI.
*/
public class ConversationIconFactory extends BaseIconFactory {
+ // Geometry of the various parts of the design. All values are 1dp on a 48x48dp icon grid.
+ // Space is left around the "head" (main avatar) for
+ // ........
+ // .HHHHHH.
+ // .HHHrrrr
+ // .HHHrBBr
+ // ....rrrr
+
+ private static final float BASE_ICON_SIZE = 48f;
+ private static final float RING_STROKE_WIDTH = 2f;
+ private static final float HEAD_SIZE = BASE_ICON_SIZE - RING_STROKE_WIDTH * 2 - 2; // 40
+ private static final float BADGE_SIZE = HEAD_SIZE * 0.4f; // 16
final LauncherApps mLauncherApps;
final PackageManager mPackageManager;
final IconDrawableFactory mIconDrawableFactory;
+ private int mImportantConversationColor;
public ConversationIconFactory(Context context, LauncherApps la, PackageManager pm,
IconDrawableFactory iconDrawableFactory, int iconSizePx) {
@@ -49,65 +65,156 @@
mLauncherApps = la;
mPackageManager = pm;
mIconDrawableFactory = iconDrawableFactory;
+ mImportantConversationColor = context.getResources().getColor(
+ R.color.important_conversation, null);
}
- private int getBadgeSize() {
- return mContext.getResources().getDimensionPixelSize(
- com.android.launcher3.icons.R.dimen.profile_badge_size);
- }
/**
* Returns the conversation info drawable
*/
- private Drawable getConversationDrawable(ShortcutInfo shortcutInfo) {
+ private Drawable getBaseIconDrawable(ShortcutInfo shortcutInfo) {
return mLauncherApps.getShortcutIconDrawable(shortcutInfo, mFillResIconDpi);
}
/**
- * Get the {@link Drawable} that represents the app icon
+ * Get the {@link Drawable} that represents the app icon, badged with the work profile icon
+ * if appropriate.
*/
- private Drawable getBadgedIcon(String packageName, int userId) {
+ private Drawable getAppBadge(String packageName, int userId) {
+ Drawable badge = null;
try {
final ApplicationInfo appInfo = mPackageManager.getApplicationInfoAsUser(
packageName, PackageManager.GET_META_DATA, userId);
- return mIconDrawableFactory.getBadgedIcon(appInfo, userId);
+ badge = mIconDrawableFactory.getBadgedIcon(appInfo, userId);
} catch (PackageManager.NameNotFoundException e) {
- return mPackageManager.getDefaultActivityIcon();
+ badge = mPackageManager.getDefaultActivityIcon();
}
+ return badge;
}
/**
- * Turns a Drawable into a Bitmap
+ * Returns a {@link Drawable} for the entire conversation. The shortcut icon will be badged
+ * with the launcher icon of the app specified by packageName.
*/
- BitmapInfo toBitmap(Drawable userBadgedAppIcon) {
- Bitmap bitmap = createIconBitmap(
- userBadgedAppIcon, 1f, getBadgeSize());
-
- Canvas c = new Canvas();
- ShadowGenerator shadowGenerator = new ShadowGenerator(getBadgeSize());
- c.setBitmap(bitmap);
- shadowGenerator.recreateIcon(Bitmap.createBitmap(bitmap), c);
- return createIconBitmap(bitmap);
+ public Drawable getConversationDrawable(ShortcutInfo info, String packageName, int uid,
+ boolean important) {
+ return getConversationDrawable(getBaseIconDrawable(info), packageName, uid, important);
}
/**
- * Returns a {@link BitmapInfo} for the entire conversation icon including the badge.
+ * Returns a {@link Drawable} for the entire conversation. The drawable will be badged
+ * with the launcher icon of the app specified by packageName.
*/
- public Bitmap getConversationBitmap(ShortcutInfo info, String packageName, int uid) {
- return getConversationBitmap(getConversationDrawable(info), packageName, uid);
+ public Drawable getConversationDrawable(Drawable baseIcon, String packageName, int uid,
+ boolean important) {
+ return new ConversationIconDrawable(baseIcon,
+ getAppBadge(packageName, UserHandle.getUserId(uid)),
+ mIconBitmapSize,
+ mImportantConversationColor,
+ important);
}
/**
- * Returns a {@link BitmapInfo} for the entire conversation icon including the badge.
+ * Custom Drawable that overlays a badge drawable (e.g. notification small icon or app icon) on
+ * a base icon (conversation/person avatar), plus decorations indicating conversation
+ * importance.
*/
- public Bitmap getConversationBitmap(Drawable baseIcon, String packageName, int uid) {
- int userId = UserHandle.getUserId(uid);
- Drawable badge = getBadgedIcon(packageName, userId);
- BitmapInfo iconInfo = createBadgedIconBitmap(baseIcon,
- UserHandle.of(userId),
- true /* shrinkNonAdaptiveIcons */);
+ public static class ConversationIconDrawable extends Drawable {
+ private Drawable mBaseIcon;
+ private Drawable mBadgeIcon;
+ private int mIconSize;
+ private Paint mRingPaint;
+ private boolean mShowRing;
- badgeWithDrawable(iconInfo.icon,
- new BitmapDrawable(mContext.getResources(), toBitmap(badge).icon));
- return iconInfo.icon;
+ public ConversationIconDrawable(Drawable baseIcon,
+ Drawable badgeIcon,
+ int iconSize,
+ @ColorInt int ringColor,
+ boolean showImportanceRing) {
+ mBaseIcon = baseIcon;
+ mBadgeIcon = badgeIcon;
+ mIconSize = iconSize;
+ mShowRing = showImportanceRing;
+ mRingPaint = new Paint();
+ mRingPaint.setStyle(Paint.Style.STROKE);
+ mRingPaint.setColor(ringColor);
+ }
+
+ /**
+ * Show or hide the importance ring.
+ */
+ public void setImportant(boolean important) {
+ if (important != mShowRing) {
+ mShowRing = important;
+ invalidateSelf();
+ }
+ }
+
+ @Override
+ public int getIntrinsicWidth() {
+ return mIconSize;
+ }
+
+ @Override
+ public int getIntrinsicHeight() {
+ return mIconSize;
+ }
+
+ // Similar to badgeWithDrawable, but relying on the bounds of each underlying drawable
+ @Override
+ public void draw(Canvas canvas) {
+ final Rect bounds = getBounds();
+
+ // scale to our internal 48x48 grid
+ final float scale = bounds.width() / BASE_ICON_SIZE;
+ final int centerX = bounds.centerX();
+ final int centerY = bounds.centerX();
+ final int ringStrokeWidth = (int) (RING_STROKE_WIDTH * scale);
+ final int headSize = (int) (HEAD_SIZE * scale);
+ final int badgeSize = (int) (BADGE_SIZE * scale);
+
+ if (mBaseIcon != null) {
+ mBaseIcon.setBounds(
+ centerX - headSize / 2,
+ centerY - headSize / 2,
+ centerX + headSize / 2,
+ centerY + headSize / 2);
+ mBaseIcon.draw(canvas);
+ } else {
+ Log.w("ConversationIconFactory", "ConversationIconDrawable has null base icon");
+ }
+ if (mBadgeIcon != null) {
+ mBadgeIcon.setBounds(
+ bounds.right - badgeSize - ringStrokeWidth,
+ bounds.bottom - badgeSize - ringStrokeWidth,
+ bounds.right - ringStrokeWidth,
+ bounds.bottom - ringStrokeWidth);
+ mBadgeIcon.draw(canvas);
+ } else {
+ Log.w("ConversationIconFactory", "ConversationIconDrawable has null badge icon");
+ }
+ if (mShowRing) {
+ mRingPaint.setStrokeWidth(ringStrokeWidth);
+ final float radius = badgeSize * 0.5f + ringStrokeWidth * 0.5f; // stroke outside
+ final float cx = bounds.right - badgeSize * 0.5f - ringStrokeWidth;
+ final float cy = bounds.bottom - badgeSize * 0.5f - ringStrokeWidth;
+ canvas.drawCircle(cx, cy, radius, mRingPaint);
+ }
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ // unimplemented
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+ // unimplemented
+ }
+
+ @Override
+ public int getOpacity() {
+ return 0;
+ }
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
index d233649..0e6a60b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
@@ -68,7 +68,8 @@
for (int reason = 0; reason <= getMaxNetworkSelectionDisableReason(); reason++) {
if (networkStatus.getDisableReasonCounter(reason) != 0) {
summary.append(" ")
- .append(NetworkSelectionStatus.getNetworkDisableReasonString(reason))
+ .append(NetworkSelectionStatus
+ .getNetworkSelectionDisableReasonString(reason))
.append("=")
.append(networkStatus.getDisableReasonCounter(reason));
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index 9668629..edb121b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -96,6 +96,7 @@
final MediaRoute2Info info = mock(MediaRoute2Info.class);
when(info.getId()).thenReturn(TEST_ID);
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ when(info.isSystemRoute()).thenReturn(true);
final List<MediaRoute2Info> routes = new ArrayList<>();
routes.add(info);
@@ -166,6 +167,7 @@
final MediaRoute2Info info = mock(MediaRoute2Info.class);
when(info.getId()).thenReturn(TEST_ID);
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ when(info.isSystemRoute()).thenReturn(true);
final List<MediaRoute2Info> routes = new ArrayList<>();
routes.add(info);
@@ -221,6 +223,7 @@
final MediaRoute2Info info = mock(MediaRoute2Info.class);
when(info.getId()).thenReturn(TEST_ID);
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ when(info.isSystemRoute()).thenReturn(true);
final List<MediaRoute2Info> routes = new ArrayList<>();
routes.add(info);
@@ -438,4 +441,23 @@
assertThat(mInfoMediaManager.getSessionVolume()).isEqualTo(-1);
}
+
+ @Test
+ public void releaseSession_packageNameIsNull_returnFalse() {
+ mInfoMediaManager.mPackageName = null;
+
+ assertThat(mInfoMediaManager.releaseSession()).isFalse();
+ }
+
+ @Test
+ public void releaseSession_removeSuccessfully_returnTrue() {
+ final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
+ final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
+ routingSessionInfos.add(info);
+
+ mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
+ when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+
+ assertThat(mInfoMediaManager.releaseSession()).isTrue();
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/schedulesprovider/ScheduleInfoTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/schedulesprovider/ScheduleInfoTest.java
new file mode 100644
index 0000000..5ec89ed
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/schedulesprovider/ScheduleInfoTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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.settingslib.schedulesprovider;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Intent;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class ScheduleInfoTest {
+ private static final String TEST_TITLE = "Night Light";
+ private static final String TEST_SUMMARY = "Night Light summary";
+ private static final String TEST_EMPTY_SUMMARY = "";
+
+ @Test
+ public void builder_usedValidArguments_isValid() {
+ final Intent intent = createTestIntent();
+ final ScheduleInfo info = createTestScheduleInfo(TEST_TITLE, TEST_SUMMARY, intent);
+
+ assertThat(info).isNotNull();
+ assertThat(info.isValid()).isTrue();
+ }
+
+ @Test
+ public void builder_useEmptySummary_isInvalid() {
+ final Intent intent = createTestIntent();
+ final ScheduleInfo info = createTestScheduleInfo(TEST_TITLE, TEST_EMPTY_SUMMARY, intent);
+
+ assertThat(info).isNotNull();
+ assertThat(info.isValid()).isFalse();
+ }
+
+ @Test
+ public void builder_intentIsNull_isInvalid() {
+ final ScheduleInfo info = new ScheduleInfo.Builder()
+ .setTitle(TEST_TITLE)
+ .setSummary(TEST_SUMMARY)
+ .build();
+
+ assertThat(info).isNotNull();
+ assertThat(info.isValid()).isFalse();
+ }
+
+ @Test
+ public void getTitle_setValidTitle_shouldReturnSameCorrectTitle() {
+ final Intent intent = createTestIntent();
+ final ScheduleInfo info = createTestScheduleInfo(TEST_TITLE, TEST_SUMMARY, intent);
+
+ assertThat(info.getTitle()).isEqualTo(TEST_TITLE);
+ }
+
+ @Test
+ public void getSummary_setValidSummary_shouldReturnSameCorrectSummary() {
+ final Intent intent = createTestIntent();
+ final ScheduleInfo info = createTestScheduleInfo(TEST_TITLE, TEST_SUMMARY, intent);
+
+ assertThat(info.getSummary()).isEqualTo(TEST_SUMMARY);
+ }
+
+ @Test
+ public void getIntent_setValidIntent_shouldReturnSameCorrectIntent() {
+ final Intent intent = createTestIntent();
+ final ScheduleInfo info = createTestScheduleInfo(TEST_TITLE, TEST_SUMMARY, intent);
+
+ assertThat(info.getIntent()).isEqualTo(intent);
+ }
+
+ private static Intent createTestIntent() {
+ return new Intent("android.settings.NIGHT_DISPLAY_SETTINGS").addCategory(
+ Intent.CATEGORY_DEFAULT);
+ }
+
+ private static ScheduleInfo createTestScheduleInfo(String title, String summary,
+ Intent intent) {
+ return new ScheduleInfo.Builder()
+ .setTitle(title)
+ .setSummary(summary)
+ .setIntent(intent)
+ .build();
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/schedulesprovider/SchedulesProviderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/schedulesprovider/SchedulesProviderTest.java
new file mode 100644
index 0000000..eb2e8e0
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/schedulesprovider/SchedulesProviderTest.java
@@ -0,0 +1,166 @@
+/*
+ * 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.settingslib.schedulesprovider;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.robolectric.Shadows.shadowOf;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.ArrayList;
+
+@RunWith(RobolectricTestRunner.class)
+public class SchedulesProviderTest {
+ private static final String INVALID_PACKAGE = "com.android.sunny";
+ private static final String VALID_PACKAGE = "com.android.settings";
+ private static final String INVALID_METHOD = "queryTestData";
+ private TestSchedulesProvider mProvider;
+
+ @Before
+ public void setUp() {
+ mProvider = Robolectric.setupContentProvider(TestSchedulesProvider.class);
+ shadowOf(mProvider).setCallingPackage(VALID_PACKAGE);
+ mProvider.setScheduleInfos(TestSchedulesProvider.createOneValidScheduleInfo());
+ }
+
+ @Test
+ public void call_invalidCallingPkg_returnNull() {
+ shadowOf(mProvider).setCallingPackage(INVALID_PACKAGE);
+
+ final Bundle bundle = mProvider.call(SchedulesProvider.METHOD_GENERATE_SCHEDULE_INFO_LIST,
+ null /* arg */, null /* extras */);
+
+ assertThat(bundle).isNull();
+ }
+
+ @Test
+ public void call_invalidMethod_returnBundleWithoutScheduleInfoData() {
+ final Bundle bundle = mProvider.call(INVALID_METHOD, null /* arg */, null /* extras */);
+
+ assertThat(bundle).isNotNull();
+ assertThat(bundle.containsKey(SchedulesProvider.BUNDLE_SCHEDULE_INFO_LIST)).isFalse();
+ }
+
+ @Test
+ public void call_validMethod_returnScheduleInfoData() {
+ final Bundle bundle = mProvider.call(SchedulesProvider.METHOD_GENERATE_SCHEDULE_INFO_LIST,
+ null /* arg */, null /* extras */);
+
+ assertThat(bundle).isNotNull();
+ assertThat(bundle.containsKey(SchedulesProvider.BUNDLE_SCHEDULE_INFO_LIST)).isTrue();
+ final ArrayList<ScheduleInfo> infos = bundle.getParcelableArrayList(
+ SchedulesProvider.BUNDLE_SCHEDULE_INFO_LIST);
+ assertThat(infos).hasSize(1);
+ }
+
+ @Test
+ public void call_addTwoValidData_returnScheduleInfoData() {
+ mProvider.setScheduleInfos(TestSchedulesProvider.createTwoValidScheduleInfos());
+ final Bundle bundle = mProvider.call(SchedulesProvider.METHOD_GENERATE_SCHEDULE_INFO_LIST,
+ null /* arg */, null /* extras */);
+
+ assertThat(bundle).isNotNull();
+ assertThat(bundle.containsKey(SchedulesProvider.BUNDLE_SCHEDULE_INFO_LIST)).isTrue();
+ final ArrayList<ScheduleInfo> infos = bundle.getParcelableArrayList(
+ SchedulesProvider.BUNDLE_SCHEDULE_INFO_LIST);
+ assertThat(infos).hasSize(2);
+ }
+
+ @Test
+ public void call_addTwoValidDataAndOneInvalidData_returnTwoScheduleInfoData() {
+ mProvider.setScheduleInfos(TestSchedulesProvider.createTwoValidAndOneInvalidScheduleInfo());
+ final Bundle bundle = mProvider.call(SchedulesProvider.METHOD_GENERATE_SCHEDULE_INFO_LIST,
+ null /* arg */, null /* extras */);
+
+ assertThat(bundle).isNotNull();
+ assertThat(bundle.containsKey(SchedulesProvider.BUNDLE_SCHEDULE_INFO_LIST)).isTrue();
+ final ArrayList<ScheduleInfo> infos = bundle.getParcelableArrayList(
+ SchedulesProvider.BUNDLE_SCHEDULE_INFO_LIST);
+ assertThat(infos).hasSize(2);
+ }
+
+ private static class TestSchedulesProvider extends SchedulesProvider {
+ private ArrayList<ScheduleInfo> mScheduleInfos = new ArrayList<>();
+
+ @Override
+ public ArrayList<ScheduleInfo> getScheduleInfoList() {
+ return mScheduleInfos;
+ }
+
+ void setScheduleInfos(ArrayList<ScheduleInfo> scheduleInfos) {
+ mScheduleInfos = scheduleInfos;
+ }
+
+ private static ArrayList<ScheduleInfo> createOneValidScheduleInfo() {
+ final ArrayList<ScheduleInfo> scheduleInfos = new ArrayList<>();
+ final Intent intent = new Intent("android.settings.NIGHT_DISPLAY_SETTINGS").addCategory(
+ Intent.CATEGORY_DEFAULT);
+ final ScheduleInfo info = new ScheduleInfo.Builder().setTitle(
+ "Night Light").setSummary("This a sunny test").setIntent(intent).build();
+ scheduleInfos.add(info);
+
+ return scheduleInfos;
+ }
+
+ private static ArrayList<ScheduleInfo> createTwoValidScheduleInfos() {
+ final ArrayList<ScheduleInfo> scheduleInfos = new ArrayList<>();
+ Intent intent = new Intent("android.settings.NIGHT_DISPLAY_SETTINGS").addCategory(
+ Intent.CATEGORY_DEFAULT);
+ ScheduleInfo info = new ScheduleInfo.Builder().setTitle(
+ "Night Light").setSummary("This a sunny test").setIntent(intent).build();
+ scheduleInfos.add(info);
+
+ intent = new Intent("android.settings.DISPLAY_SETTINGS").addCategory(
+ Intent.CATEGORY_DEFAULT);
+ info = new ScheduleInfo.Builder().setTitle("Display").setSummary(
+ "Display summary").setIntent(intent).build();
+ scheduleInfos.add(info);
+
+ return scheduleInfos;
+ }
+
+ private static ArrayList<ScheduleInfo> createTwoValidAndOneInvalidScheduleInfo() {
+ final ArrayList<ScheduleInfo> scheduleInfos = new ArrayList<>();
+ Intent intent = new Intent("android.settings.NIGHT_DISPLAY_SETTINGS").addCategory(
+ Intent.CATEGORY_DEFAULT);
+ ScheduleInfo info = new ScheduleInfo.Builder().setTitle(
+ "Night Light").setSummary("This a sunny test").setIntent(intent).build();
+ scheduleInfos.add(info);
+
+ intent = new Intent("android.settings.DISPLAY_SETTINGS").addCategory(
+ Intent.CATEGORY_DEFAULT);
+ info = new ScheduleInfo.Builder().setTitle("Display").setSummary(
+ "Display summary").setIntent(intent).build();
+ scheduleInfos.add(info);
+
+ intent = new Intent("android.settings.DISPLAY_SETTINGS").addCategory(
+ Intent.CATEGORY_DEFAULT);
+ info = new ScheduleInfo.Builder().setTitle("").setSummary("Display summary").setIntent(
+ intent).build();
+ scheduleInfos.add(info);
+
+ return scheduleInfos;
+ }
+ }
+}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 0f2ee6a..610165a 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -268,6 +268,7 @@
Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD,
Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS,
Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS,
+ Settings.Global.ENHANCED_CONNECTIVITY_ENABLED,
Settings.Global.ENHANCED_4G_MODE_ENABLED,
Settings.Global.EPHEMERAL_COOKIE_MAX_SIZE_BYTES,
Settings.Global.ERROR_LOGCAT_PREFIX,
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index d126ee0..c76b50b 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -615,13 +615,16 @@
+ " bugreport parcel file descriptor is null.");
return;
}
- ParcelFileDescriptor screenshotFd = info.getDefaultScreenshotFd();
- if (screenshotFd == null) {
- Log.e(TAG, "Failed to start bugreport generation as"
- + " screenshot parcel file descriptor is null. Deleting bugreport file");
- FileUtils.closeQuietly(bugreportFd);
- info.bugreportFile.delete();
- return;
+ ParcelFileDescriptor screenshotFd = null;
+ if (isDefaultScreenshotRequired(bugreportType)) {
+ screenshotFd = info.getDefaultScreenshotFd();
+ if (screenshotFd == null) {
+ Log.e(TAG, "Failed to start bugreport generation as"
+ + " screenshot parcel file descriptor is null. Deleting bugreport file");
+ FileUtils.closeQuietly(bugreportFd);
+ info.bugreportFile.delete();
+ return;
+ }
}
mBugreportManager = (BugreportManager) mContext.getSystemService(
@@ -641,10 +644,20 @@
// The binder call didn't go through successfully, so need to close the fds.
// If the calls went through API takes ownership.
FileUtils.closeQuietly(bugreportFd);
- FileUtils.closeQuietly(screenshotFd);
+ if (screenshotFd != null) {
+ FileUtils.closeQuietly(screenshotFd);
+ }
}
}
+ private static boolean isDefaultScreenshotRequired(
+ @BugreportParams.BugreportMode int bugreportType) {
+ // Modify dumpstate#SetOptionsFromMode as well for default system screenshots.
+ // We override dumpstate for interactive bugreports.
+ return bugreportType == BugreportParams.BUGREPORT_MODE_FULL
+ || bugreportType == BugreportParams.BUGREPORT_MODE_WEAR;
+ }
+
private static ParcelFileDescriptor getFd(File file) {
try {
return ParcelFileDescriptor.open(file,
diff --git a/packages/StatementService/AndroidManifest.xml b/packages/StatementService/AndroidManifest.xml
index d79d900..b00c37f 100644
--- a/packages/StatementService/AndroidManifest.xml
+++ b/packages/StatementService/AndroidManifest.xml
@@ -22,6 +22,7 @@
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTENT_FILTER_VERIFICATION_AGENT"/>
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<application
android:label="@string/service_name"
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/NotificationPersonExtractorPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/NotificationPersonExtractorPlugin.java
index 6650c15..f79cd86 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/NotificationPersonExtractorPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/NotificationPersonExtractorPlugin.java
@@ -68,14 +68,14 @@
public final String key;
public final CharSequence name;
public final Drawable avatar;
- public final PendingIntent clickIntent;
+ public final Runnable clickRunnable;
public PersonData(String key, CharSequence name, Drawable avatar,
- PendingIntent clickIntent) {
+ Runnable clickRunnable) {
this.key = key;
this.name = name;
this.avatar = avatar;
- this.clickIntent = clickIntent;
+ this.clickRunnable = clickRunnable;
}
}
}
diff --git a/packages/SystemUI/res-keyguard/drawable/bubble_manage_user_education_bg.xml b/packages/SystemUI/res-keyguard/drawable/bubble_manage_user_education_bg.xml
new file mode 100644
index 0000000..64db25b
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/bubble_manage_user_education_bg.xml
@@ -0,0 +1,21 @@
+<!--
+ ~ 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">
+ <solid android:color="?android:attr/colorAccent"/>
+ <corners
+ android:radius="?android:attr/dialogCornerRadius" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/notification_guts_priority_button_bg_stroke.xml b/packages/SystemUI/res/color/notification_guts_priority_button_bg_stroke.xml
index 7964609..015e9f9 100644
--- a/packages/SystemUI/res/color/notification_guts_priority_button_bg_stroke.xml
+++ b/packages/SystemUI/res/color/notification_guts_priority_button_bg_stroke.xml
@@ -16,6 +16,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true"
- android:color="@color/notification_guts_priority_button_bg_stroke_color_selected" />
+ android:color="?android:attr/colorAccent" />
<item android:color="@color/notification_guts_priority_button_bg_stroke_color" />
</selector>
diff --git a/packages/SystemUI/res/color/notification_guts_priority_contents.xml b/packages/SystemUI/res/color/notification_guts_priority_contents.xml
index 56c43f0..42f0189 100644
--- a/packages/SystemUI/res/color/notification_guts_priority_contents.xml
+++ b/packages/SystemUI/res/color/notification_guts_priority_contents.xml
@@ -16,6 +16,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true"
- android:color="@color/notification_guts_priority_button_content_color_selected" />
+ android:color="?android:attr/colorAccent" />
<item android:color="@color/notification_guts_priority_button_content_color" />
</selector>
diff --git a/packages/SystemUI/res/drawable/bubble_stack_user_education_bg.xml b/packages/SystemUI/res/drawable/bubble_stack_user_education_bg.xml
new file mode 100644
index 0000000..4b9219c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/bubble_stack_user_education_bg.xml
@@ -0,0 +1,22 @@
+<!--
+ ~ 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">
+ <solid android:color="?android:attr/colorAccent"/>
+ <corners
+ android:bottomRightRadius="360dp"
+ android:topRightRadius="360dp" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/notification_guts_bg.xml b/packages/SystemUI/res/drawable/notification_guts_bg.xml
index 1730dce..2fe6c7b 100644
--- a/packages/SystemUI/res/drawable/notification_guts_bg.xml
+++ b/packages/SystemUI/res/drawable/notification_guts_bg.xml
@@ -16,7 +16,7 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="@color/notification_guts_bg_color" />
+ <solid android:color="@color/notification_material_background_color" />
<!--The radius is 1dp smaller than the notification one, to avoid aliasing bugs on the corners -->
<corners android:radius="1dp" />
</shape>
diff --git a/packages/SystemUI/res/layout/app_ops_info.xml b/packages/SystemUI/res/layout/app_ops_info.xml
index 82a0115..bfa252c 100644
--- a/packages/SystemUI/res/layout/app_ops_info.xml
+++ b/packages/SystemUI/res/layout/app_ops_info.xml
@@ -26,7 +26,7 @@
android:orientation="vertical"
android:paddingStart="@*android:dimen/notification_content_margin_start"
android:paddingEnd="@*android:dimen/notification_content_margin_end"
- android:background="@color/notification_guts_bg_color"
+ android:background="@color/notification_material_background_color"
android:theme="@*android:style/Theme.DeviceDefault.Light">
<!-- Package Info -->
diff --git a/packages/SystemUI/res/layout/bubble_stack_user_education.xml b/packages/SystemUI/res/layout/bubble_stack_user_education.xml
new file mode 100644
index 0000000..81b28e6
--- /dev/null
+++ b/packages/SystemUI/res/layout/bubble_stack_user_education.xml
@@ -0,0 +1,48 @@
+<?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.
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/user_education_view"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:paddingTop="48dp"
+ android:paddingBottom="48dp"
+ android:paddingStart="@dimen/bubble_stack_user_education_side_inset"
+ android:paddingEnd="16dp"
+ android:layout_marginEnd="24dp"
+ android:orientation="vertical"
+ android:background="@drawable/bubble_stack_user_education_bg">
+
+ <TextView
+ android:id="@+id/user_education_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingBottom="16dp"
+ android:fontFamily="@*android:string/config_bodyFontFamilyMedium"
+ android:maxLines="1"
+ android:text="@string/bubbles_user_education_title"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Headline"/>
+
+ <TextView
+ android:id="@+id/user_education_description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/bubbles_user_education_description"
+ android:fontFamily="@*android:string/config_bodyFontFamily"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2"/>
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/bubbles_manage_button_education.xml b/packages/SystemUI/res/layout/bubbles_manage_button_education.xml
new file mode 100644
index 0000000..0cabc32
--- /dev/null
+++ b/packages/SystemUI/res/layout/bubbles_manage_button_education.xml
@@ -0,0 +1,46 @@
+<?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.
+ -->
+<com.android.systemui.bubbles.BubbleManageEducationView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:id="@+id/manage_education_view"
+ android:orientation="vertical"
+ android:layout_height="wrap_content"
+ android:layout_width="@dimen/bubbles_manage_education_width">
+
+ <TextView
+ android:id="@+id/user_education_description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="8dp"
+ android:text="@string/bubbles_user_education_manage"
+ android:fontFamily="@*android:string/config_bodyFontFamily"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2"
+ android:background="@drawable/bubble_manage_user_education_bg"
+ />
+
+ <View
+ android:id="@+id/user_education_pointer"
+ android:layout_width="@dimen/bubble_pointer_width"
+ android:layout_height="@dimen/bubble_pointer_height"
+ />
+
+ </LinearLayout>
+</com.android.systemui.bubbles.BubbleManageEducationView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/controls_management.xml b/packages/SystemUI/res/layout/controls_management.xml
index a7379be..6533c18 100644
--- a/packages/SystemUI/res/layout/controls_management.xml
+++ b/packages/SystemUI/res/layout/controls_management.xml
@@ -70,12 +70,14 @@
android:layout_height="match_parent"
android:padding="4dp">
- <TextView
+ <Button
+ android:id="@+id/other_apps"
+ android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="match_parent"
+ android:gravity="center_vertical"
android:text="See other apps"
- android:textAppearance="@style/TextAppearance.Control.Title"
- android:textColor="?android:attr/colorPrimary"
+ style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
@@ -85,6 +87,7 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="Done"
+ style="@*android:style/Widget.DeviceDefault.Button.Colored"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
diff --git a/packages/SystemUI/res/layout/controls_management_favorites.xml b/packages/SystemUI/res/layout/controls_management_favorites.xml
index 62056e6..aab32f4 100644
--- a/packages/SystemUI/res/layout/controls_management_favorites.xml
+++ b/packages/SystemUI/res/layout/controls_management_favorites.xml
@@ -14,97 +14,26 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<androidx.constraintlayout.widget.ConstraintLayout
+<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
- android:id="@+id/error_message"
+ android:id="@+id/status_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/controls_management_list_margin"
- android:text="@string/controls_favorite_load_error"
android:textAppearance="?android:attr/textAppearanceSmall"
- android:visibility="gone"
android:gravity="center_horizontal"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toBottomOf="parent"
- app:layout_constraintBottom_toTopOf="@id/text_favorites"
/>
- <TextView
- android:id="@+id/text_favorites"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/controls_management_list_margin"
- android:text="@string/controls_favorite_header_favorites"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textAllCaps="true"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintBottom_toTopOf="@id/divider1"
- app:layout_constraintTop_toBottomOf="@id/error_message"
- />
-
- <View
- android:id="@+id/divider1"
- android:layout_width="match_parent"
- android:layout_height="@dimen/controls_app_divider_height"
- android:layout_gravity="center_horizontal|top"
- android:background="?android:attr/listDivider"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintBottom_toTopOf="@id/listFavorites"
- app:layout_constraintTop_toBottomOf="@id/text_favorites"
- />
-
- <androidx.recyclerview.widget.RecyclerView
- android:id="@+id/listFavorites"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_marginTop="@dimen/controls_management_list_margin"
- android:nestedScrollingEnabled="false"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintBottom_toTopOf="@id/text_all"
- app:layout_constraintTop_toBottomOf="@id/divider1"/>
-
- <TextView
- android:id="@+id/text_all"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/controls_management_list_margin"
- android:text="@string/controls_favorite_header_all"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textAllCaps="true"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintBottom_toTopOf="@id/divider2"
- app:layout_constraintTop_toBottomOf="@id/listFavorites"
- />
-
- <View
- android:id="@+id/divider2"
- android:layout_width="match_parent"
- android:layout_height="@dimen/controls_app_divider_height"
- android:layout_gravity="center_horizontal|top"
- android:background="?android:attr/listDivider"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintBottom_toTopOf="@id/listAll"
- app:layout_constraintTop_toBottomOf="@id/text_all"
- />
-
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/listAll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="@dimen/controls_management_list_margin"
- android:nestedScrollingEnabled="false"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toBottomOf="@id/divider2"/>
+ android:nestedScrollingEnabled="false"/>
-</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml
index 0b74a11a..a76f961 100644
--- a/packages/SystemUI/res/layout/global_screenshot.xml
+++ b/packages/SystemUI/res/layout/global_screenshot.xml
@@ -32,10 +32,10 @@
android:alpha="0"/>
<HorizontalScrollView
android:id="@+id/global_screenshot_actions_container"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="bottom|center"
- android:elevation="3dp"
+ android:layout_gravity="bottom|left"
+ android:elevation="1dp"
android:fillViewport="true"
android:layout_marginHorizontal="@dimen/screenshot_action_container_margin_horizontal"
android:gravity="center"
@@ -63,7 +63,7 @@
android:id="@+id/global_screenshot_dismiss_button"
android:layout_width="@dimen/screenshot_dismiss_button_tappable_size"
android:layout_height="@dimen/screenshot_dismiss_button_tappable_size"
- android:elevation="9dp"
+ android:elevation="7dp"
android:visibility="gone">
<ImageView
android:layout_width="match_parent"
@@ -76,7 +76,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@android:color/white"
- android:elevation="8dp"
+ android:elevation="@dimen/screenshot_preview_elevation"
android:visibility="gone"/>
<com.android.systemui.screenshot.ScreenshotSelectorView
android:id="@+id/global_screenshot_selector"
diff --git a/packages/SystemUI/res/layout/global_screenshot_action_chip.xml b/packages/SystemUI/res/layout/global_screenshot_action_chip.xml
index 79867a16b..6b94bef 100644
--- a/packages/SystemUI/res/layout/global_screenshot_action_chip.xml
+++ b/packages/SystemUI/res/layout/global_screenshot_action_chip.xml
@@ -19,7 +19,7 @@
android:id="@+id/global_screenshot_action_chip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginHorizontal="@dimen/screenshot_action_chip_margin_horizontal"
+ android:layout_marginRight="@dimen/screenshot_action_chip_margin_right"
android:layout_gravity="center"
android:paddingVertical="@dimen/screenshot_action_chip_padding_vertical"
android:background="@drawable/action_chip_background"
@@ -35,7 +35,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/screenshot_action_chip_padding_end"
+ android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
android:textSize="@dimen/screenshot_action_chip_text_size"
- android:textStyle="bold"
android:textColor="@color/global_screenshot_button_text"/>
</com.android.systemui.screenshot.ScreenshotActionChip>
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index 87de9d4..5d03eee 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -24,8 +24,7 @@
android:clipChildren="false"
android:clipToPadding="true"
android:orientation="vertical"
- android:paddingStart="@*android:dimen/notification_content_margin_start"
- android:background="@color/notification_guts_bg_color">
+ android:paddingStart="@*android:dimen/notification_content_margin_start">
<!-- Package Info -->
<RelativeLayout
diff --git a/packages/SystemUI/res/layout/notification_snooze.xml b/packages/SystemUI/res/layout/notification_snooze.xml
index ffe2eee..c350ed2 100644
--- a/packages/SystemUI/res/layout/notification_snooze.xml
+++ b/packages/SystemUI/res/layout/notification_snooze.xml
@@ -21,7 +21,7 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:clickable="true"
- android:background="@color/notification_guts_bg_color"
+ android:background="@color/notification_material_background_color"
android:theme="@style/Theme.SystemUI">
<RelativeLayout
diff --git a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
index 8878786..46a7cf6 100644
--- a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
+++ b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
@@ -17,6 +17,7 @@
<com.android.systemui.qs.PagedTileLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/qs_pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index 4fae3c5..f8db97d 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -24,7 +24,6 @@
android:layout_width="match_parent"
android:layout_height="@dimen/status_bar_height"
android:id="@+id/status_bar"
- android:background="@drawable/system_bar_background"
android:orientation="vertical"
android:focusable="false"
android:descendantFocusability="afterDescendants"
diff --git a/packages/SystemUI/res/layout/status_bar_notification_footer.xml b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
index 36ba66a..5a1e5c4 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_footer.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
@@ -33,8 +33,8 @@
android:layout_height="wrap_content"
android:layout_gravity="start"
android:focusable="true"
- android:contentDescription="@string/accessibility_manage_notification"
- android:text="@string/manage_notifications_text"
+ android:contentDescription="@string/manage_notifications_history_text"
+ android:text="@string/manage_notifications_history_text"
/>
<com.android.systemui.statusbar.notification.row.FooterViewButton
style="@style/TextAppearance.NotificationSectionHeaderButton"
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index 8fee2cf9..7142929 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -28,5 +28,6 @@
<FrameLayout
android:id="@+id/status_bar_container"
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="wrap_content"
+ android:background="@drawable/system_bar_background" />
</com.android.systemui.statusbar.phone.StatusBarWindowView>
diff --git a/packages/SystemUI/res/layout/system_icons.xml b/packages/SystemUI/res/layout/system_icons.xml
index f3b72bf..6fb5590 100644
--- a/packages/SystemUI/res/layout/system_icons.xml
+++ b/packages/SystemUI/res/layout/system_icons.xml
@@ -26,6 +26,7 @@
android:layout_weight="1"
android:layout_height="match_parent"
android:paddingEnd="@dimen/signal_cluster_battery_padding"
+ android:paddingTop="@dimen/status_bar_padding_top"
android:gravity="center_vertical"
android:orientation="horizontal"/>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 9a66e8b..9b0fe46 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -39,23 +39,16 @@
<!-- The color of the ripples on the untinted notifications -->
<color name="notification_ripple_untinted_color">#30ffffff</color>
- <!-- The "inside" of a notification, reached via longpress -->
- <color name="notification_guts_bg_color">@color/GM2_grey_900</color>
-
<!-- The color of the text inside a notification -->
<color name="notification_primary_text_color">@*android:color/notification_primary_text_color_dark</color>
<color name="notification_guts_link_icon_tint">@color/GM2_grey_500</color>
<color name="notification_guts_sub_text_color">@color/GM2_grey_300</color>
<color name="notification_guts_header_text_color">@color/GM2_grey_200</color>
- <color name="notification_guts_info_button_color">@color/GM2_blue_300</color>
<color name="notification_guts_priority_button_content_color">@color/GM2_grey_500</color>
- <color name="notification_guts_priority_button_content_color_selected">@color/GM2_blue_300</color>
<color name="notification_guts_priority_button_bg_fill_color">@color/transparent</color>
<color name="notification_guts_priority_button_bg_fill_color_selected">@color/GM2_grey_800</color>
<color name="notification_guts_priority_button_bg_stroke_color">@color/GM2_grey_700</color>
- <color name="notification_guts_priority_button_bg_stroke_color_selected">@color/GM2_blue_300</color>
-
<color name="notification_section_header_label_color">@color/GM2_grey_200</color>
<color name="notification_section_clear_all_btn_color">@color/GM2_grey_500</color>
<color name="notification_channel_dialog_separator">@color/GM2_grey_700</color>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 43ceb4e..73e49ce 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -88,21 +88,16 @@
<!-- The color of the text inside a notification -->
<color name="notification_primary_text_color">@*android:color/notification_primary_text_color_light</color>
- <!-- The "inside" of a notification, reached via longpress -->
- <color name="notification_guts_bg_color">@color/GM2_grey_50</color>
<color name="notification_guts_link_icon_tint">@color/GM2_grey_700</color>
<color name="notification_guts_sub_text_color">@color/GM2_grey_700</color>
<color name="notification_guts_header_text_color">@color/GM2_grey_900</color>
<color name="notification_silence_color">#FF32c1de</color>
<color name="notification_alert_color">#FFF87B2B</color>
- <color name="notification_guts_info_button_color">@color/GM2_blue_700</color>
<color name="notification_guts_priority_button_content_color">@color/GM2_grey_700</color>
- <color name="notification_guts_priority_button_content_color_selected">@color/GM2_blue_700</color>
<color name="notification_guts_priority_button_bg_fill_color">@color/transparent</color>
<color name="notification_guts_priority_button_bg_fill_color_selected">#FFFFFF</color>
<color name="notification_guts_priority_button_bg_stroke_color">@color/GM2_grey_300</color>
- <color name="notification_guts_priority_button_bg_stroke_color_selected">@color/GM2_blue_600</color>
<color name="notification_section_header_label_color">@color/GM2_grey_900</color>
<color name="notification_section_clear_all_btn_color">@color/GM2_grey_700</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 8de2df5..27ffcee 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -201,6 +201,13 @@
always-on display) -->
<string name="doze_brightness_sensor_type" translatable="false"></string>
+ <!-- Override value to use for proximity sensor. -->
+ <string name="proximity_sensor_type" translatable="false"></string>
+
+ <!-- If using proximity_sensor_type, specifies a threshold value to distinguish near and
+ far break points. A sensor value less than this is considered "near". -->
+ <item name="proximity_sensor_threshold" translatable="false" format="float" type="dimen"></item>
+
<!-- Doze: pulse parameter - how long does it take to fade in? -->
<integer name="doze_pulse_duration_in">130</integer>
@@ -464,6 +471,9 @@
<!-- Allow dragging the PIP to a location to close it -->
<bool name="config_pipEnableDismissDragToEdge">true</bool>
+ <!-- Allow PIP to resize to a slightly bigger state upon touch/showing the menu -->
+ <bool name="config_pipEnableResizeForMenu">true</bool>
+
<!-- SystemUI Plugins that can be loaded on user builds. -->
<string-array name="config_pluginWhitelist" translatable="false">
<item>com.android.systemui</item>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 9caaa9f..aefe4a2 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -305,7 +305,7 @@
<dimen name="global_screenshot_legacy_bg_padding">20dp</dimen>
<dimen name="global_screenshot_bg_padding">20dp</dimen>
<dimen name="global_screenshot_x_scale">80dp</dimen>
- <dimen name="screenshot_preview_elevation">8dp</dimen>
+ <dimen name="screenshot_preview_elevation">6dp</dimen>
<dimen name="screenshot_offset_y">48dp</dimen>
<dimen name="screenshot_offset_x">16dp</dimen>
<dimen name="screenshot_dismiss_button_tappable_size">48dp</dimen>
@@ -314,13 +314,13 @@
<dimen name="screenshot_action_container_corner_radius">10dp</dimen>
<dimen name="screenshot_action_container_padding_vertical">10dp</dimen>
<dimen name="screenshot_action_container_margin_horizontal">8dp</dimen>
- <dimen name="screenshot_action_container_padding_left">100dp</dimen>
+ <dimen name="screenshot_action_container_padding_left">96dp</dimen>
<dimen name="screenshot_action_container_padding_right">8dp</dimen>
<!-- Radius of the chip background on global screenshot actions -->
<dimen name="screenshot_button_corner_radius">20dp</dimen>
- <dimen name="screenshot_action_chip_margin_horizontal">4dp</dimen>
- <dimen name="screenshot_action_chip_padding_vertical">10dp</dimen>
- <dimen name="screenshot_action_chip_icon_size">20dp</dimen>
+ <dimen name="screenshot_action_chip_margin_right">8dp</dimen>
+ <dimen name="screenshot_action_chip_padding_vertical">7dp</dimen>
+ <dimen name="screenshot_action_chip_icon_size">18dp</dimen>
<dimen name="screenshot_action_chip_padding_start">8dp</dimen>
<!-- Padding between icon and text -->
<dimen name="screenshot_action_chip_padding_middle">8dp</dimen>
@@ -1159,7 +1159,7 @@
<!-- Extra padding around the dismiss target for bubbles -->
<dimen name="bubble_dismiss_slop">16dp</dimen>
<!-- Height of button allowing users to adjust settings for bubbles. -->
- <dimen name="bubble_settings_size">48dp</dimen>
+ <dimen name="bubble_manage_button_height">48dp</dimen>
<!-- How far, horizontally, to animate the expanded view over when animating in/out. -->
<dimen name="bubble_expanded_animate_x_distance">100dp</dimen>
<!-- How far, vertically, to animate the expanded view over when animating in/out. -->
@@ -1175,16 +1175,22 @@
<!-- How far offscreen the bubble stack rests. Cuts off padding and part of icon bitmap. -->
<dimen name="bubble_stack_offscreen">9dp</dimen>
<!-- How far down the screen the stack starts. -->
- <dimen name="bubble_stack_starting_offset_y">96dp</dimen>
+ <dimen name="bubble_stack_starting_offset_y">120dp</dimen>
<!-- Space between the pointer triangle and the bubble expanded view -->
<dimen name="bubble_pointer_margin">8dp</dimen>
- <!-- Height of the permission prompt shown with bubbles -->
- <dimen name="bubble_permission_height">120dp</dimen>
<!-- Padding applied to the bubble dismiss target. Touches in this padding cause the bubbles to
snap to the dismiss target. -->
<dimen name="bubble_dismiss_target_padding_x">40dp</dimen>
<dimen name="bubble_dismiss_target_padding_y">20dp</dimen>
+ <!-- Bubbles user education views -->
+ <dimen name="bubbles_manage_education_width">160dp</dimen>
+ <!-- The inset from the top bound of the manage button to place the user education. -->
+ <dimen name="bubbles_manage_education_top_inset">10dp</dimen>
+ <!-- Size of padding for the user education cling, this should at minimum be larger than
+ individual_bubble_size + some padding. -->
+ <dimen name="bubble_stack_user_education_side_inset">72dp</dimen>
+
<!-- Size of the RAT type for CellularTile -->
<dimen name="celltile_rat_type_size">10sp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 4aafec8..496ab43 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2586,6 +2586,12 @@
<string name="bubble_accessibility_action_move_bottom_right">Move bottom right</string>
<!-- Text used for the bubble dismiss area. Bubbles dragged to, or flung towards, this area will go away. [CHAR LIMIT=20] -->
<string name="bubble_dismiss_text">Dismiss</string>
+ <!-- Title text for the bubbles feature education cling shown when a bubble is on screen for the first time. [CHAR LIMIT=60]-->
+ <string name="bubbles_user_education_title">Keep chats up front</string>
+ <!-- Descriptive text for the bubble feature education cling shown when a bubble is on screen for the first time. [CHAR LIMIT=NONE] -->
+ <string name="bubbles_user_education_description">New chats from <xliff:g id="app_name" example="YouTube">%1$s</xliff:g> will appear as bubbles. Tap a bubble to open it. Drag to move it.\n\nTap the bubble</string>
+ <!-- Text for the bubble "manage" button feature education cling shown when a bubble is on screen for the first time. [CHAR LIMIT=80]-->
+ <string name="bubbles_user_education_manage">Tap Manage to turn off bubbles from this app</string>
<!-- Notification content text when the system navigation mode changes as a result of changing the default launcher [CHAR LIMIT=NONE] -->
<string name="notification_content_system_nav_changed">System navigation updated. To make changes, go to Settings.</string>
@@ -2614,8 +2620,8 @@
<string name="controls_providers_subtitle">Choose an app from which to add controls</string>
<!-- Number of favorites for controls management screen [CHAR LIMIT=NONE]-->
<plurals name="controls_number_of_favorites">
- <item quantity="one"><xliff:g id="number" example="1">%s</xliff:g> current favorite.</item>
- <item quantity="other"><xliff:g id="number" example="3">%s</xliff:g> current favorites.</item>
+ <item quantity="one"><xliff:g id="number" example="1">%s</xliff:g> control added.</item>
+ <item quantity="other"><xliff:g id="number" example="3">%s</xliff:g> controls added.</item>
</plurals>
<!-- Controls management controls screen default title [CHAR LIMIT=30] -->
@@ -2626,6 +2632,10 @@
<string name="controls_favorite_header_favorites">Favorites</string>
<!-- Controls management controls screen all header [CHAR LIMIT=50] -->
<string name="controls_favorite_header_all">All</string>
- <!-- Controls management controls screen error on load message [CHAR LIMIT=50] -->
+ <!-- Controls management controls screen error on load message [CHAR LIMIT=60] -->
<string name="controls_favorite_load_error">The list of all controls could not be loaded.</string>
+ <!-- Controls management controls screen header for Other zone [CHAR LIMIT=60] -->
+ <string name="controls_favorite_other_zone_header">Other</string>
+
+
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 36c4526..d9b1452 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -514,7 +514,7 @@
<style name="TextAppearance.NotificationInfo.Button">
<item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
<item name="android:textSize">14sp</item>
- <item name="android:textColor">@color/notification_guts_info_button_color</item>
+ <item name="android:textColor">?android:attr/colorAccent</item>
<item name="android:background">@drawable/btn_borderless_rect</item>
<item name="android:gravity">center_vertical</item>
<item name="android:focusable">true</item>
@@ -550,7 +550,7 @@
<style name="TextAppearance.NotificationImportanceButton">
<item name="android:textSize">@dimen/notification_importance_button_text</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
- <item name="android:textColor">@color/notification_guts_priority_contents</item>
+ <item name="android:textColor">?android:attr/colorAccent</item>
<item name="android:gravity">center</item>
</style>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index a130092..49e3e57 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -370,8 +370,7 @@
Rect initialBounds) {
try {
return ActivityTaskManager.getService().setTaskWindowingModeSplitScreenPrimary(taskId,
- createMode, true /* onTop */, false /* animate */, initialBounds,
- true /* showRecents */);
+ true /* onTop */);
} catch (RemoteException e) {
return false;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
index 6cd6118..bbb83c7 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
@@ -61,14 +61,6 @@
}
}
- public void setSplitScreenMinimized(boolean minimized) {
- try {
- mAnimationController.setSplitScreenMinimized(minimized);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to set minimize dock", e);
- }
- }
-
public void hideCurrentInputMethod() {
try {
mAnimationController.hideCurrentInputMethod();
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index e28b1e2..d64bf77 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -191,18 +191,6 @@
}
/**
- * Registers a docked stack listener with the system.
- */
- public void registerDockedStackListener(DockedStackListenerCompat listener) {
- try {
- WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(
- listener.mListener);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to register docked stack listener");
- }
- }
-
- /**
* Adds a pinned stack listener, which will receive updates from the window manager service
* along with any other pinned stack listeners that were added via this method.
*/
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index ba8a1a9..3cf07d1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -19,7 +19,9 @@
import static android.view.ViewRootImpl.sNewInsetsMode;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.systemBars;
+
import static com.android.systemui.DejankUtils.whitelistIpcs;
+
import static java.lang.Integer.max;
import android.app.Activity;
@@ -28,7 +30,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.res.ColorStateList;
-import android.graphics.Rect;
import android.metrics.LogMaker;
import android.os.Handler;
import android.os.Looper;
@@ -511,6 +512,8 @@
boolean finish = false;
boolean strongAuth = false;
int eventSubtype = -1;
+ mCurrentSecuritySelection = whitelistIpcs(() ->
+ mSecurityModel.getSecurityMode(targetUserId));
if (mUpdateMonitor.getUserHasTrust(targetUserId)) {
finish = true;
eventSubtype = BOUNCER_DISMISS_EXTENDED_ACCESS;
@@ -518,13 +521,8 @@
finish = true;
eventSubtype = BOUNCER_DISMISS_BIOMETRIC;
} else if (SecurityMode.None == mCurrentSecuritySelection) {
- SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
- if (SecurityMode.None == securityMode) {
- finish = true; // no security required
- eventSubtype = BOUNCER_DISMISS_NONE_SECURITY;
- } else {
- showSecurityScreen(securityMode); // switch to the alternate security view
- }
+ finish = true; // no security required
+ eventSubtype = BOUNCER_DISMISS_NONE_SECURITY;
} else if (authenticated) {
switch (mCurrentSecuritySelection) {
case Pattern:
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
index 924d16d..f01fa81 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
@@ -71,7 +71,8 @@
public void onEntryRemoved(
NotificationEntry entry,
NotificationVisibility visibility,
- boolean removedByUser) {
+ boolean removedByUser,
+ int reason) {
removeNotification(entry.getSbn());
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 4473b01..dbcdead 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -20,7 +20,9 @@
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.os.Handler;
import android.os.HandlerThread;
+import android.os.SystemClock;
import android.os.Trace;
import android.service.wallpaper.WallpaperService;
import android.util.Log;
@@ -94,18 +96,28 @@
private EglHelper mEglHelper;
private StatusBarStateController mController;
private final Runnable mFinishRenderingTask = this::finishRendering;
- private final boolean mNeedTransition;
private boolean mShouldStopTransition;
- @VisibleForTesting
- final boolean mIsHighEndGfx;
- private final boolean mDisplayNeedsBlanking;
private final DisplayInfo mDisplayInfo = new DisplayInfo();
private final Object mMonitor = new Object();
+ @VisibleForTesting
+ boolean mIsHighEndGfx;
+ private boolean mDisplayNeedsBlanking;
+ private boolean mNeedTransition;
private boolean mNeedRedraw;
// This variable can only be accessed in synchronized block.
private boolean mWaitingForRendering;
GLEngine(Context context, DozeParameters dozeParameters) {
+ init(dozeParameters);
+ }
+
+ @VisibleForTesting
+ GLEngine(DozeParameters dozeParameters, Handler handler) {
+ super(SystemClock::elapsedRealtime, handler);
+ init(dozeParameters);
+ }
+
+ private void init(DozeParameters dozeParameters) {
mIsHighEndGfx = ActivityManager.isHighEndGfx();
mDisplayNeedsBlanking = dozeParameters.getDisplayNeedsBlanking();
mNeedTransition = mIsHighEndGfx && !mDisplayNeedsBlanking;
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index 10ae343..5e6589f 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -57,7 +57,9 @@
Key.SEEN_RINGER_GUIDANCE_COUNT,
Key.QS_HAS_TURNED_OFF_MOBILE_DATA,
Key.TOUCHED_RINGER_TOGGLE,
- Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP
+ Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP,
+ Key.HAS_SEEN_BUBBLES_EDUCATION,
+ Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION
})
public @interface Key {
@Deprecated
@@ -103,6 +105,8 @@
String QS_HAS_TURNED_OFF_MOBILE_DATA = "QsHasTurnedOffMobileData";
String TOUCHED_RINGER_TOGGLE = "TouchedRingerToggle";
String HAS_SEEN_ODI_CAPTIONS_TOOLTIP = "HasSeenODICaptionsTooltip";
+ String HAS_SEEN_BUBBLES_EDUCATION = "HasSeenBubblesOnboarding";
+ String HAS_SEEN_BUBBLES_MANAGE_EDUCATION = "HasSeenBubblesManageOnboarding";
}
public static boolean getBoolean(Context context, @Key String key, boolean defaultValue) {
@@ -164,7 +168,7 @@
get(context).unregisterOnSharedPreferenceChangeListener(listener);
}
- private static SharedPreferences get(Context context) {
+ public static SharedPreferences get(Context context) {
return context.getSharedPreferences(context.getPackageName(), Context.MODE_PRIVATE);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 6ce6353..0f896c4 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -99,7 +99,8 @@
private static final boolean DEBUG_COLOR = DEBUG_SCREENSHOT_ROUNDED_CORNERS;
private DisplayManager mDisplayManager;
- private boolean mIsRegistered;
+ @VisibleForTesting
+ protected boolean mIsRegistered;
private final BroadcastDispatcher mBroadcastDispatcher;
private final Handler mMainHandler;
private final TunerService mTunerService;
@@ -168,7 +169,6 @@
mDisplayManager = mContext.getSystemService(DisplayManager.class);
updateRoundedCornerRadii();
setupDecorations();
-
mDisplayListener = new DisplayManager.DisplayListener() {
@Override
public void onDisplayAdded(int displayId) {
@@ -230,7 +230,10 @@
removeAllOverlays();
}
- if (hasOverlays() && !mIsRegistered) {
+ if (hasOverlays()) {
+ if (mIsRegistered) {
+ return;
+ }
DisplayMetrics metrics = new DisplayMetrics();
mDisplayManager.getDisplay(DEFAULT_DISPLAY).getMetrics(metrics);
mDensity = metrics.density;
@@ -271,7 +274,8 @@
return mContext.getDisplay().getCutout();
}
- private boolean hasOverlays() {
+ @VisibleForTesting
+ boolean hasOverlays() {
if (mOverlays == null) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index a220dac..48457f6 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -425,7 +425,8 @@
public void onEntryRemoved(
NotificationEntry entry,
@android.annotation.Nullable NotificationVisibility visibility,
- boolean removedByUser) {
+ boolean removedByUser,
+ int reason) {
BubbleController.this.onEntryRemoved(entry);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java
index 3190662..e800011 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDebugConfig.java
@@ -16,6 +16,9 @@
package com.android.systemui.bubbles;
+import android.content.Context;
+import android.provider.Settings;
+
import java.util.List;
/**
@@ -41,6 +44,20 @@
static final boolean DEBUG_BUBBLE_EXPANDED_VIEW = false;
static final boolean DEBUG_EXPERIMENTS = true;
static final boolean DEBUG_OVERFLOW = false;
+ static final boolean DEBUG_USER_EDUCATION = false;
+
+ private static final boolean FORCE_SHOW_USER_EDUCATION = false;
+ private static final String FORCE_SHOW_USER_EDUCATION_SETTING =
+ "force_show_bubbles_user_education";
+
+ /**
+ * @return whether we should force show user education for bubbles. Used for debugging & demos.
+ */
+ static boolean forceShowUserEducation(Context context) {
+ boolean forceShow = Settings.Secure.getInt(context.getContentResolver(),
+ FORCE_SHOW_USER_EDUCATION_SETTING, 0) != 0;
+ return FORCE_SHOW_USER_EDUCATION || forceShow;
+ }
static String formatBubblesString(List<Bubble> bubbles, Bubble selected) {
StringBuilder sb = new StringBuilder();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index e3983c5..a6f759f 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -243,7 +243,7 @@
mPointerView.setVisibility(INVISIBLE);
mSettingsIconHeight = getContext().getResources().getDimensionPixelSize(
- R.dimen.bubble_settings_size);
+ R.dimen.bubble_manage_button_height);
mSettingsIcon = findViewById(R.id.settings_button);
mSettingsIcon.setOnClickListener(this);
@@ -531,6 +531,16 @@
}
/**
+ * Position of the manage button displayed in the expanded view. Used for placing user
+ * education about the manage button.
+ */
+ public Rect getManageButtonLocationOnScreen() {
+ mTempLoc = mSettingsIcon.getLocationOnScreen();
+ return new Rect(mTempLoc[0], mTempLoc[1], mTempLoc[0] + mSettingsIcon.getWidth(),
+ mTempLoc[1] + mSettingsIcon.getHeight());
+ }
+
+ /**
* Removes and releases an ActivityView if one was previously created for this bubble.
*/
public void cleanUpExpandedState() {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
index 20b3386..2873811 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
@@ -73,9 +73,6 @@
private static final String WHITELISTED_AUTO_BUBBLE_APPS = "whitelisted_auto_bubble_apps";
- private static final String ALLOW_BUBBLE_OVERFLOW = "allow_bubble_overflow";
- private static final boolean ALLOW_BUBBLE_OVERFLOW_DEFAULT = false;
-
/**
* When true, if a notification has the information necessary to bubble (i.e. valid
* contentIntent and an icon or image), then a {@link android.app.Notification.BubbleMetadata}
@@ -131,16 +128,6 @@
}
/**
- * When true, show a menu when a bubble is long-pressed, which will allow the user to take
- * actions on that bubble.
- */
- static boolean allowBubbleOverflow(Context context) {
- return Settings.Secure.getInt(context.getContentResolver(),
- ALLOW_BUBBLE_OVERFLOW,
- ALLOW_BUBBLE_OVERFLOW_DEFAULT ? 1 : 0) != 0;
- }
-
- /**
* If {@link #allowAnyNotifToBubble(Context)} is true, this method creates and adds
* {@link android.app.Notification.BubbleMetadata} to the notification entry as long as
* the notification has necessary info for BubbleMetadata.
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleManageEducationView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleManageEducationView.java
new file mode 100644
index 0000000..f4d6432
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleManageEducationView.java
@@ -0,0 +1,109 @@
+/*
+ * 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.bubbles;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.drawable.ShapeDrawable;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.internal.util.ContrastColorUtil;
+import com.android.systemui.R;
+import com.android.systemui.recents.TriangleShape;
+
+/**
+ * Educational view to highlight the manage button that allows a user to configure the settings
+ * for the bubble. Shown only the first time a user expands a bubble.
+ */
+public class BubbleManageEducationView extends LinearLayout {
+
+ private View mPointerView;
+ private View mManageView;
+
+ public BubbleManageEducationView(Context context) {
+ this(context, null);
+ }
+
+ public BubbleManageEducationView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public BubbleManageEducationView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public BubbleManageEducationView(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ mManageView = findViewById(R.id.manage_education_view);
+
+ final TypedArray ta = mContext.obtainStyledAttributes(
+ new int[] {android.R.attr.colorAccent,
+ android.R.attr.textColorPrimaryInverse});
+ final int bgColor = ta.getColor(0, Color.BLACK);
+ int textColor = ta.getColor(1, Color.WHITE);
+ ta.recycle();
+
+ textColor = ContrastColorUtil.ensureTextContrast(textColor, bgColor, true);
+ ((TextView) findViewById(R.id.user_education_description)).setTextColor(textColor);
+
+ final Resources res = getResources();
+ final int pointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width);
+ final int pointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
+
+ ShapeDrawable triangleShape =
+ new ShapeDrawable(TriangleShape.create(
+ pointerWidth, pointerHeight, false /* isPointingUp */));
+ triangleShape.setTint(bgColor);
+
+ mPointerView = findViewById(R.id.user_education_pointer);
+ mPointerView.setBackground(triangleShape);
+ }
+
+ /**
+ * Specifies the x value this pointer should point to.
+ */
+ public void setPointerPosition(int x) {
+ mPointerView.setTranslationX(x - (mPointerView.getWidth() / 2));
+ }
+
+ /**
+ * Specifies the position for the manage view.
+ */
+ public void setManageViewPosition(int x, int y) {
+ mManageView.setTranslationX(x);
+ mManageView.setTranslationY(y);
+ }
+
+ /**
+ * @return the height of the view that shows the educational text and pointer.
+ */
+ public int getManageViewHeight() {
+ return mManageView.getHeight();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
index a0e7449..313bb42 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
@@ -62,11 +62,12 @@
R.dimen.bubble_overflow_icon_bitmap_size);
}
- public void setUpOverflow(ViewGroup parentViewGroup) {
+ void setUpOverflow(ViewGroup parentViewGroup, BubbleStackView stackView) {
mOverflowExpandedView = (BubbleExpandedView) mInflater.inflate(
R.layout.bubble_expanded_view, parentViewGroup /* root */,
false /* attachToRoot */);
mOverflowExpandedView.setOverflow(true);
+ mOverflowExpandedView.setStackView(stackView);
updateIcon(mContext, parentViewGroup);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 072c20c..6647069 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -19,9 +19,13 @@
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_EDUCATION;
+import static com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION;
import static com.android.systemui.bubbles.BadgedImageView.DOT_STATE_DEFAULT;
import static com.android.systemui.bubbles.BadgedImageView.DOT_STATE_SUPPRESSED_FOR_FLYOUT;
import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_STACK_VIEW;
+import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_USER_EDUCATION;
import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
@@ -33,6 +37,8 @@
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
@@ -57,6 +63,7 @@
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.FrameLayout;
+import android.widget.TextView;
import androidx.annotation.MainThread;
import androidx.annotation.Nullable;
@@ -66,7 +73,9 @@
import androidx.dynamicanimation.animation.SpringForce;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ContrastColorUtil;
import com.android.internal.widget.ViewClippingUtil;
+import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.bubbles.animation.ExpandedAnimationController;
import com.android.systemui.bubbles.animation.PhysicsAnimationLayout;
@@ -88,6 +97,10 @@
public class BubbleStackView extends FrameLayout {
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleStackView" : TAG_BUBBLES;
+ /** Animation durations for bubble stack user education views. **/
+ private static final int ANIMATE_STACK_USER_EDUCATION_DURATION = 200;
+ private static final int ANIMATE_STACK_USER_EDUCATION_DURATION_SHORT = 40;
+
/** How far the flyout needs to be dragged before it's dismissed regardless of velocity. */
static final float FLYOUT_DRAG_PERCENT_DISMISS = 0.25f;
@@ -171,6 +184,12 @@
* previous one animates out.
*/
private Runnable mAfterFlyoutHidden;
+ /**
+ * Set when the flyout is tapped, so that we can expand the bubble associated with the flyout
+ * once it collapses.
+ */
+ @Nullable
+ private Bubble mBubbleToExpandAfterFlyoutCollapse = null;
/** Layout change listener that moves the stack to the nearest valid position on rotation. */
private OnLayoutChangeListener mOrientationChangedListener;
@@ -319,6 +338,14 @@
private BubbleOverflow mBubbleOverflow;
+ private boolean mShouldShowUserEducation;
+ private boolean mAnimatingEducationAway;
+ private View mUserEducationView;
+
+ private boolean mShouldShowManageEducation;
+ private BubbleManageEducationView mManageEducationView;
+ private boolean mAnimatingManageEducationAway;
+
public BubbleStackView(Context context, BubbleData data,
@Nullable SurfaceSynchronizer synchronizer,
FloatingContentCoordinator floatingContentCoordinator) {
@@ -361,6 +388,8 @@
mDisplaySize, mExpandedViewPadding, res.getConfiguration().orientation);
mSurfaceSynchronizer = synchronizer != null ? synchronizer : DEFAULT_SURFACE_SYNCHRONIZER;
+ setUpUserEducation();
+
mBubbleContainer = new PhysicsAnimationLayout(context);
mBubbleContainer.setActiveController(mStackAnimationController);
mBubbleContainer.setElevation(elevation);
@@ -500,10 +529,50 @@
});
}
- void showExpandedViewContents(int displayId) {
- if (mExpandedBubble != null
- && mExpandedBubble.getExpandedView().getVirtualDisplayId() == displayId) {
- mExpandedBubble.setContentVisibility(true);
+ private void setUpUserEducation() {
+ if (mUserEducationView != null) {
+ removeView(mUserEducationView);
+ }
+ mShouldShowUserEducation = shouldShowBubblesEducation();
+ if (DEBUG_USER_EDUCATION) {
+ Log.d(TAG, "shouldShowUserEducation: " + mShouldShowUserEducation);
+ }
+ if (mShouldShowUserEducation) {
+ mUserEducationView = mInflater.inflate(R.layout.bubble_stack_user_education, this,
+ false /* attachToRoot */);
+ mUserEducationView.setVisibility(GONE);
+
+ final TypedArray ta = mContext.obtainStyledAttributes(
+ new int[] {android.R.attr.colorAccent,
+ android.R.attr.textColorPrimaryInverse});
+ final int bgColor = ta.getColor(0, Color.BLACK);
+ int textColor = ta.getColor(1, Color.WHITE);
+ ta.recycle();
+ textColor = ContrastColorUtil.ensureTextContrast(textColor, bgColor, true);
+
+ TextView title = mUserEducationView.findViewById(R.id.user_education_title);
+ TextView description = mUserEducationView.findViewById(R.id.user_education_description);
+ title.setTextColor(textColor);
+ description.setTextColor(textColor);
+
+ addView(mUserEducationView);
+ }
+
+ if (mManageEducationView != null) {
+ removeView(mManageEducationView);
+ }
+ mShouldShowManageEducation = shouldShowManageEducation();
+ if (DEBUG_USER_EDUCATION) {
+ Log.d(TAG, "shouldShowManageEducation: " + mShouldShowManageEducation);
+ }
+ if (mShouldShowManageEducation) {
+ mManageEducationView = (BubbleManageEducationView)
+ mInflater.inflate(R.layout.bubbles_manage_button_education, this,
+ false /* attachToRoot */);
+ mManageEducationView.setVisibility(GONE);
+ mManageEducationView.setElevation(mBubbleElevation);
+
+ addView(mManageEducationView);
}
}
@@ -520,13 +589,10 @@
}
private void setUpOverflow() {
- if (!BubbleExperimentConfig.allowBubbleOverflow(mContext)) {
- return;
- }
int overflowBtnIndex = 0;
if (mBubbleOverflow == null) {
- mBubbleOverflow = new BubbleOverflow(mContext);
- mBubbleOverflow.setUpOverflow(this);
+ mBubbleOverflow = new BubbleOverflow(getContext());
+ mBubbleOverflow.setUpOverflow(mBubbleContainer, this);
} else {
mBubbleContainer.removeView(mBubbleOverflow.getBtn());
mBubbleOverflow.updateIcon(mContext, this);
@@ -542,6 +608,7 @@
public void onThemeChanged() {
setUpFlyout();
setUpOverflow();
+ setUpUserEducation();
}
/** Respond to the phone being rotated by repositioning the stack and hiding any flyouts. */
@@ -733,9 +800,8 @@
@Nullable
Bubble getExpandedBubble() {
if (mExpandedBubble == null
- || (BubbleExperimentConfig.allowBubbleOverflow(mContext)
- && mExpandedBubble.getIconView() == mBubbleOverflow.getBtn()
- && mExpandedBubble.getKey() == BubbleOverflow.KEY)) {
+ || (mExpandedBubble.getIconView() == mBubbleOverflow.getBtn()
+ && BubbleOverflow.KEY.equals(mExpandedBubble.getKey()))) {
return null;
}
return (Bubble) mExpandedBubble;
@@ -747,6 +813,12 @@
Log.d(TAG, "addBubble: " + bubble);
}
+ if (getBubbleCount() == 0 && mShouldShowUserEducation) {
+ // Override the default stack position if we're showing user education.
+ mStackAnimationController.setStackPosition(
+ mStackAnimationController.getDefaultStartPosition());
+ }
+
if (getBubbleCount() == 0) {
mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide();
}
@@ -785,9 +857,6 @@
}
private void updateOverflowBtnVisibility(boolean apply) {
- if (!BubbleExperimentConfig.allowBubbleOverflow(mContext)) {
- return;
- }
if (mIsExpanded) {
if (DEBUG_BUBBLE_STACK_VIEW) {
Log.d(TAG, "Show overflow button.");
@@ -888,6 +957,109 @@
}
/**
+ * If necessary, shows the user education view for the bubble stack. This appears the first
+ * time a user taps on a bubble.
+ *
+ * @return true if user education was shown, false otherwise.
+ */
+ private boolean maybeShowStackUserEducation() {
+ if (mShouldShowUserEducation && mUserEducationView.getVisibility() != VISIBLE) {
+ Bubble b = mBubbleData.getSelectedBubble();
+ TextView description = mUserEducationView.findViewById(R.id.user_education_description);
+ description.setText(mContext.getString(
+ R.string.bubbles_user_education_description, b.getAppName()));
+
+ mUserEducationView.setAlpha(0);
+ mUserEducationView.setVisibility(VISIBLE);
+ // Post so we have height of mUserEducationView
+ mUserEducationView.post(() -> {
+ final int viewHeight = mUserEducationView.getHeight();
+ PointF stackPosition = mStackAnimationController.getDefaultStartPosition();
+ final float translationY = stackPosition.y + (mBubbleSize / 2) - (viewHeight / 2);
+ mUserEducationView.setTranslationY(translationY);
+ mUserEducationView.animate()
+ .setDuration(ANIMATE_STACK_USER_EDUCATION_DURATION)
+ .setInterpolator(FAST_OUT_SLOW_IN)
+ .alpha(1);
+ });
+ Prefs.putBoolean(getContext(), HAS_SEEN_BUBBLES_EDUCATION, true);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * If necessary, hides the user education view for the bubble stack.
+ *
+ * @param fromExpansion if true this indicates the hide is happening due to the bubble being
+ * expanded, false if due to a touch outside of the bubble stack.
+ */
+ void hideStackUserEducation(boolean fromExpansion) {
+ if (mShouldShowUserEducation
+ && mUserEducationView.getVisibility() == VISIBLE
+ && !mAnimatingEducationAway) {
+ mAnimatingEducationAway = true;
+ mUserEducationView.animate()
+ .alpha(0)
+ .setDuration(fromExpansion
+ ? ANIMATE_STACK_USER_EDUCATION_DURATION_SHORT
+ : ANIMATE_STACK_USER_EDUCATION_DURATION)
+ .withEndAction(() -> {
+ mAnimatingEducationAway = false;
+ mShouldShowUserEducation = shouldShowBubblesEducation();
+ mUserEducationView.setVisibility(GONE);
+ });
+ }
+ }
+
+ /**
+ * If necessary, toggles the user education view for the manage button. This is shown when the
+ * bubble stack is expanded for the first time.
+ *
+ * @param show whether the user education view should show or not.
+ */
+ void maybeShowManageEducation(boolean show) {
+ if (mManageEducationView == null) {
+ return;
+ }
+ if (show
+ && mShouldShowManageEducation
+ && mManageEducationView.getVisibility() != VISIBLE
+ && mIsExpanded) {
+ mManageEducationView.setAlpha(0);
+ mManageEducationView.setVisibility(VISIBLE);
+ mManageEducationView.post(() -> {
+ final Rect position =
+ mExpandedBubble.getExpandedView().getManageButtonLocationOnScreen();
+ final int viewHeight = mManageEducationView.getManageViewHeight();
+ final int inset = getResources().getDimensionPixelSize(
+ R.dimen.bubbles_manage_education_top_inset);
+ mManageEducationView.bringToFront();
+ mManageEducationView.setManageViewPosition(position.left,
+ position.top - viewHeight + inset);
+ mManageEducationView.setPointerPosition(position.centerX() - position.left);
+ mManageEducationView.animate()
+ .setDuration(ANIMATE_STACK_USER_EDUCATION_DURATION)
+ .setInterpolator(FAST_OUT_SLOW_IN).alpha(1);
+ });
+ Prefs.putBoolean(getContext(), HAS_SEEN_BUBBLES_MANAGE_EDUCATION, true);
+ } else if (!show
+ && mManageEducationView.getVisibility() == VISIBLE
+ && !mAnimatingManageEducationAway) {
+ mManageEducationView.animate()
+ .alpha(0)
+ .setDuration(mIsExpansionAnimating
+ ? ANIMATE_STACK_USER_EDUCATION_DURATION_SHORT
+ : ANIMATE_STACK_USER_EDUCATION_DURATION)
+ .withEndAction(() -> {
+ mAnimatingManageEducationAway = false;
+ mShouldShowManageEducation = shouldShowManageEducation();
+ mManageEducationView.setVisibility(GONE);
+ });
+ }
+ }
+
+ /**
* Dismiss the stack of bubbles.
*
* @deprecated
@@ -911,8 +1083,7 @@
float y = event.getRawY();
if (mIsExpanded) {
if (isIntersecting(mBubbleContainer, x, y)) {
- if (BubbleExperimentConfig.allowBubbleOverflow(mContext)
- && isIntersecting(mBubbleOverflow.getBtn(), x, y)) {
+ if (isIntersecting(mBubbleOverflow.getBtn(), x, y)) {
return mBubbleOverflow.getBtn();
}
// Could be tapping or dragging a bubble while expanded
@@ -931,7 +1102,17 @@
return null;
} else if (mFlyout.getVisibility() == VISIBLE && isIntersecting(mFlyout, x, y)) {
return mFlyout;
+ } else if (mUserEducationView != null && mUserEducationView.getVisibility() == VISIBLE) {
+ View bubbleChild = mBubbleContainer.getChildAt(0);
+ if (isIntersecting(bubbleChild, x, y)) {
+ return this;
+ } else if (isIntersecting(mUserEducationView, x, y)) {
+ return mUserEducationView;
+ } else {
+ return null;
+ }
}
+
// If it wasn't an individual bubble in the expanded state, or the flyout, it's the stack.
return this;
}
@@ -941,22 +1122,6 @@
}
/**
- * Collapses the stack of bubbles.
- * <p>
- * Must be called from the main thread.
- *
- * @deprecated use {@link #setExpanded(boolean)} and {@link #setSelectedBubble(Bubble)}
- */
- @Deprecated
- @MainThread
- void collapseStack() {
- if (DEBUG_BUBBLE_STACK_VIEW) {
- Log.d(TAG, "collapseStack()");
- }
- mBubbleData.setExpanded(false);
- }
-
- /**
* @deprecated use {@link #setExpanded(boolean)} and {@link #setSelectedBubble(Bubble)}
*/
@Deprecated
@@ -965,25 +1130,16 @@
if (DEBUG_BUBBLE_STACK_VIEW) {
Log.d(TAG, "collapseStack(endRunnable)");
}
- collapseStack();
+ mBubbleData.setExpanded(false);
// TODO - use the runnable at end of animation
endRunnable.run();
}
- /**
- * Expands the stack of bubbles.
- * <p>
- * Must be called from the main thread.
- *
- * @deprecated use {@link #setExpanded(boolean)} and {@link #setSelectedBubble(Bubble)}
- */
- @Deprecated
- @MainThread
- void expandStack() {
- if (DEBUG_BUBBLE_STACK_VIEW) {
- Log.d(TAG, "expandStack()");
+ void showExpandedViewContents(int displayId) {
+ if (mExpandedBubble != null
+ && mExpandedBubble.getExpandedView().getVirtualDisplayId() == displayId) {
+ mExpandedBubble.setContentVisibility(true);
}
- mBubbleData.setExpanded(true);
}
private void beforeExpandedViewAnimation() {
@@ -1003,11 +1159,12 @@
mIsExpanded = false;
final BubbleViewProvider previouslySelected = mExpandedBubble;
beforeExpandedViewAnimation();
+ maybeShowManageEducation(false);
if (DEBUG_BUBBLE_STACK_VIEW) {
Log.d(TAG, "animateCollapse");
- Log.d(TAG, BubbleDebugConfig.formatBubblesString(this.getBubblesOnScreen(),
- this.getExpandedBubble()));
+ Log.d(TAG, BubbleDebugConfig.formatBubblesString(getBubblesOnScreen(),
+ getExpandedBubble()));
}
updateOverflowBtnVisibility(/* apply */ false);
mBubbleContainer.cancelAllAnimations();
@@ -1029,6 +1186,7 @@
private void animateExpansion() {
mIsExpanded = true;
+ hideStackUserEducation(true /* fromExpansion */);
beforeExpandedViewAnimation();
mBubbleContainer.setActiveController(mExpandedAnimationController);
@@ -1036,6 +1194,7 @@
mExpandedAnimationController.expandFromStack(() -> {
updatePointerPosition();
afterExpandedViewAnimation();
+ maybeShowManageEducation(true);
} /* after */);
mExpandedViewContainer.setTranslationX(getCollapsedX());
@@ -1082,11 +1241,19 @@
}
}
+ /** Called when the collapsed stack is tapped on. */
+ void onStackTapped() {
+ if (!maybeShowStackUserEducation()) {
+ mBubbleData.setExpanded(true);
+ }
+ }
+
/** Called when a drag operation on an individual bubble has started. */
public void onBubbleDragStart(View bubble) {
if (DEBUG_BUBBLE_STACK_VIEW) {
Log.d(TAG, "onBubbleDragStart: bubble=" + bubble);
}
+ maybeShowManageEducation(false);
mExpandedAnimationController.prepareForBubbleDrag(bubble);
}
@@ -1137,6 +1304,7 @@
return;
}
+ hideStackUserEducation(false /* fromExpansion */);
springInDismissTarget();
mStackAnimationController.moveStackFromTouch(x, y);
}
@@ -1199,14 +1367,13 @@
mFlyout.setTranslationX(mFlyout.getRestingTranslationX() + overscrollTranslation);
}
- /**
- * Set when the flyout is tapped, so that we can expand the bubble associated with the flyout
- * once it collapses.
- */
- @Nullable private Bubble mBubbleToExpandAfterFlyoutCollapse = null;
-
void onFlyoutTapped() {
- mBubbleToExpandAfterFlyoutCollapse = mBubbleData.getSelectedBubble();
+ if (maybeShowStackUserEducation()) {
+ // If we're showing user education, don't open the bubble show the education first
+ mBubbleToExpandAfterFlyoutCollapse = null;
+ } else {
+ mBubbleToExpandAfterFlyoutCollapse = mBubbleData.getSelectedBubble();
+ }
mFlyout.removeCallbacks(mHideFlyout);
mHideFlyout.run();
@@ -1229,6 +1396,8 @@
mFlyout.removeCallbacks(mHideFlyout);
animateFlyoutCollapsed(shouldDismiss, velX);
+
+ maybeShowStackUserEducation();
}
/**
@@ -1440,6 +1609,7 @@
if (flyoutMessage == null
|| flyoutMessage.message == null
|| !bubble.showFlyout()
+ || (mUserEducationView != null && mUserEducationView.getVisibility() == VISIBLE)
|| isExpanded()
|| mIsExpansionAnimating
|| mIsGestureInProgress
@@ -1525,7 +1695,12 @@
@Override
public void getBoundsOnScreen(Rect outRect) {
- // If the bubble menu is open, the entire screen should capture touch events.
+ if (mUserEducationView != null && mUserEducationView.getVisibility() == VISIBLE) {
+ // When user education shows then capture all touches
+ outRect.set(0, 0, getWidth(), getHeight());
+ return;
+ }
+
if (!mIsExpanded) {
if (getBubbleCount() > 0) {
mBubbleContainer.getChildAt(0).getBoundsOnScreen(outRect);
@@ -1645,11 +1820,8 @@
* @return the number of bubbles in the stack view.
*/
public int getBubbleCount() {
- if (BubbleExperimentConfig.allowBubbleOverflow(mContext)) {
- // Subtract 1 for the overflow button which is always in the bubble container.
- return mBubbleContainer.getChildCount() - 1;
- }
- return mBubbleContainer.getChildCount();
+ // Subtract 1 for the overflow button that is always in the bubble container.
+ return mBubbleContainer.getChildCount() - 1;
}
/**
@@ -1715,6 +1887,18 @@
return mExpandedBubble.getExpandedView().performBackPressIfNeeded();
}
+ /** Whether the educational view should appear for bubbles. **/
+ private boolean shouldShowBubblesEducation() {
+ return BubbleDebugConfig.forceShowUserEducation(getContext())
+ || !Prefs.getBoolean(getContext(), HAS_SEEN_BUBBLES_EDUCATION, false);
+ }
+
+ /** Whether the educational view should appear for the expanded view "manage" button. **/
+ private boolean shouldShowManageEducation() {
+ return BubbleDebugConfig.forceShowUserEducation(getContext())
+ || !Prefs.getBoolean(getContext(), HAS_SEEN_BUBBLES_MANAGE_EDUCATION, false);
+ }
+
/** For debugging only */
List<Bubble> getBubblesOnScreen() {
List<Bubble> bubbles = new ArrayList<>();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
index 5e3e747..46d1e0d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
@@ -24,7 +24,6 @@
import android.view.ViewConfiguration;
import com.android.systemui.Dependency;
-import com.android.systemui.R;
/**
* Handles interpreting touches on a {@link BubbleStackView}. This includes expanding, collapsing,
@@ -92,6 +91,7 @@
// anything, collapse the stack.
if (action == MotionEvent.ACTION_OUTSIDE || mTouchedView == null) {
mBubbleData.setExpanded(false);
+ mStack.hideStackUserEducation(false /* fromExpansion */);
resetForNextGesture();
return false;
}
@@ -102,6 +102,7 @@
// Not touching anything touchable, but we shouldn't collapse (e.g. touching edge
// of expanded view).
+ mStack.maybeShowManageEducation(false);
resetForNextGesture();
return false;
}
@@ -217,9 +218,8 @@
}
} else if (mTouchedView == mStack.getExpandedBubbleView()) {
mBubbleData.setExpanded(false);
- } else if (isStack || isFlyout) {
- // Toggle expansion
- mBubbleData.setExpanded(!mBubbleData.isExpanded());
+ } else if (isStack) {
+ mStack.onStackTapped();
} else {
final String key = ((BadgedImageView) mTouchedView).getKey();
if (key == BubbleOverflow.KEY) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
index 245d4af..f22c8fa 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
@@ -879,9 +879,10 @@
}
/** Moves the stack to a position instantly, with no animation. */
- private void setStackPosition(PointF pos) {
+ public void setStackPosition(PointF pos) {
Log.d(TAG, String.format("Setting position to (%f, %f).", pos.x, pos.y));
mStackPosition.set(pos.x, pos.y);
+ mRestingStackPosition = mStackPosition;
// If we're not the active controller, we don't want to physically move the bubble views.
if (isActiveController()) {
@@ -902,10 +903,10 @@
}
}
- /** Returns the default stack position, which is on the top right. */
- private PointF getDefaultStartPosition() {
+ /** Returns the default stack position, which is on the top left. */
+ public PointF getDefaultStartPosition() {
return new PointF(
- getAllowableStackPositionRegion().right,
+ getAllowableStackPositionRegion().left,
getAllowableStackPositionRegion().top + mStackStartingVerticalOffset);
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
index f719cc6..37c7a2e 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
@@ -16,6 +16,7 @@
package com.android.systemui.classifier;
+import android.app.ActivityManager;
import android.content.Context;
import android.database.ContentObserver;
import android.hardware.Sensor;
@@ -296,6 +297,10 @@
mHandler.postDelayed(mPendingWtf, 1000);
}
}
+ if (ActivityManager.isRunningInUserTestHarness()) {
+ // This is a test device running UiAutomator code.
+ return false;
+ }
if (mAccessibilityManager.isTouchExplorationEnabled()) {
// Touch exploration triggers false positives in the classifier and
// already sufficiently prevents false unlocks.
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
index 0ac1c12..79b691b 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
@@ -39,6 +39,7 @@
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.FalsingPlugin;
import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.sensors.ProximitySensor;
@@ -69,6 +70,7 @@
private final DockManager mDockManager;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private Executor mUiBgExecutor;
+ private final StatusBarStateController mStatusBarStateController;
@Inject
FalsingManagerProxy(Context context, PluginManager pluginManager, @Main Executor executor,
@@ -76,12 +78,14 @@
DeviceConfigProxy deviceConfig, DockManager dockManager,
KeyguardUpdateMonitor keyguardUpdateMonitor,
DumpManager dumpManager,
- @UiBackground Executor uiBgExecutor) {
+ @UiBackground Executor uiBgExecutor,
+ StatusBarStateController statusBarStateController) {
mDisplayMetrics = displayMetrics;
mProximitySensor = proximitySensor;
mDockManager = dockManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mUiBgExecutor = uiBgExecutor;
+ mStatusBarStateController = statusBarStateController;
mProximitySensor.setTag(PROXIMITY_SENSOR_TAG);
mProximitySensor.setSensorDelay(SensorManager.SENSOR_DELAY_GAME);
mDeviceConfig = deviceConfig;
@@ -143,7 +147,8 @@
mKeyguardUpdateMonitor,
mProximitySensor,
mDeviceConfig,
- mDockManager
+ mDockManager,
+ mStatusBarStateController
);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
index a084ae6..caab187 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
@@ -19,6 +19,7 @@
import static com.android.systemui.classifier.FalsingManagerImpl.FALSING_REMAIN_LOCKED;
import static com.android.systemui.classifier.FalsingManagerImpl.FALSING_SUCCESS;
+import android.app.ActivityManager;
import android.hardware.biometrics.BiometricSourceType;
import android.net.Uri;
import android.os.Build;
@@ -32,6 +33,8 @@
import com.android.systemui.classifier.Classifier;
import com.android.systemui.dock.DockManager;
import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.sensors.ProximitySensor;
@@ -59,6 +62,7 @@
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final ProximitySensor mProximitySensor;
private final DockManager mDockManager;
+ private final StatusBarStateController mStatusBarStateController;
private boolean mSessionStarted;
private MetricsLogger mMetricsLogger;
private int mIsFalseTouchCalls;
@@ -88,15 +92,29 @@
};
private boolean mPreviousResult = false;
+ private StatusBarStateController.StateListener mStatusBarStateListener =
+ new StatusBarStateController.StateListener() {
+ @Override
+ public void onStateChanged(int newState) {
+ logDebug("StatusBarState=" + StatusBarState.toShortString(newState));
+ mState = newState;
+ updateSessionActive();
+ }
+ };
+ private int mState;
+
public BrightLineFalsingManager(FalsingDataProvider falsingDataProvider,
KeyguardUpdateMonitor keyguardUpdateMonitor, ProximitySensor proximitySensor,
DeviceConfigProxy deviceConfigProxy,
- DockManager dockManager) {
+ DockManager dockManager, StatusBarStateController statusBarStateController) {
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mDataProvider = falsingDataProvider;
mProximitySensor = proximitySensor;
mDockManager = dockManager;
+ mStatusBarStateController = statusBarStateController;
mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
+ mStatusBarStateController.addCallback(mStatusBarStateListener);
+ mState = mStatusBarStateController.getState();
mMetricsLogger = new MetricsLogger();
mClassifiers = new ArrayList<>();
@@ -116,13 +134,12 @@
mProximitySensor.register(mSensorEventListener);
}
-
private void unregisterSensors() {
mProximitySensor.unregister(mSensorEventListener);
}
private void sessionStart() {
- if (!mSessionStarted && !mShowingAod && mScreenOn) {
+ if (!mSessionStarted && shouldSessionBeActive()) {
logDebug("Starting Session");
mSessionStarted = true;
mJustUnlockedWithFace = false;
@@ -145,6 +162,19 @@
}
}
+
+ private void updateSessionActive() {
+ if (shouldSessionBeActive()) {
+ sessionStart();
+ } else {
+ sessionEnd();
+ }
+ }
+
+ private boolean shouldSessionBeActive() {
+ return mScreenOn && (mState == StatusBarState.KEYGUARD) && !mShowingAod;
+ }
+
private void updateInteractionType(@Classifier.InteractionType int type) {
logDebug("InteractionType: " + type);
mDataProvider.setInteractionType(type);
@@ -161,8 +191,8 @@
return mPreviousResult;
}
- mPreviousResult = !mJustUnlockedWithFace && !mDockManager.isDocked()
- && mClassifiers.stream().anyMatch(falsingClassifier -> {
+ mPreviousResult = !ActivityManager.isRunningInUserTestHarness() && !mJustUnlockedWithFace
+ && !mDockManager.isDocked() && mClassifiers.stream().anyMatch(falsingClassifier -> {
boolean result = falsingClassifier.isFalseTouch();
if (result) {
logInfo(String.format(
@@ -232,11 +262,7 @@
@Override
public void setShowingAod(boolean showingAod) {
mShowingAod = showingAod;
- if (showingAod) {
- sessionEnd();
- } else {
- sessionStart();
- }
+ updateSessionActive();
}
@Override
@@ -343,13 +369,13 @@
@Override
public void onScreenTurningOn() {
mScreenOn = true;
- sessionStart();
+ updateSessionActive();
}
@Override
public void onScreenOff() {
mScreenOn = false;
- sessionEnd();
+ updateSessionActive();
}
@@ -421,6 +447,7 @@
public void cleanup() {
unregisterSensors();
mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
+ mStatusBarStateController.removeCallback(mStatusBarStateListener);
}
static void logDebug(String msg) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
index 6ff1bbc..a67f6bd 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
@@ -16,6 +16,7 @@
package com.android.systemui.controls.controller
+import android.app.ActivityManager
import android.content.ComponentName
import android.content.Context
import android.os.IBinder
@@ -50,7 +51,7 @@
private val refreshing = AtomicBoolean(false)
- private var currentUser = context.user
+ private var currentUser = UserHandle.of(ActivityManager.getCurrentUser())
override val currentUserId: Int
get() = currentUser.identifier
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index bb665fe..3b06ebe 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -16,6 +16,7 @@
package com.android.systemui.controls.controller
+import android.app.ActivityManager
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.ComponentName
@@ -78,16 +79,16 @@
private var userChanging: Boolean = true
- private val contentResolver: ContentResolver
- get() = context.contentResolver
- override var available = Settings.Secure.getInt(
- contentResolver, CONTROLS_AVAILABLE, DEFAULT_ENABLED) != 0
- private set
-
- private var currentUser = context.user
+ private var currentUser = UserHandle.of(ActivityManager.getCurrentUser())
override val currentUserId
get() = currentUser.identifier
+ private val contentResolver: ContentResolver
+ get() = context.contentResolver
+ override var available = Settings.Secure.getIntForUser(
+ contentResolver, CONTROLS_AVAILABLE, DEFAULT_ENABLED, currentUserId) != 0
+ private set
+
private val persistenceWrapper = optionalWrapper.orElseGet {
ControlsFavoritePersistenceWrapper(
Environment.buildPath(
@@ -106,7 +107,7 @@
userContext.filesDir, ControlsFavoritePersistenceWrapper.FILE_NAME)
persistenceWrapper.changeFile(fileName)
available = Settings.Secure.getIntForUser(contentResolver, CONTROLS_AVAILABLE,
- /* default */ DEFAULT_ENABLED, newUser.identifier) != 0
+ DEFAULT_ENABLED, newUser.identifier) != 0
synchronized(currentFavorites) {
currentFavorites.clear()
}
@@ -142,7 +143,7 @@
return
}
available = Settings.Secure.getIntForUser(contentResolver, CONTROLS_AVAILABLE,
- /* default */ DEFAULT_ENABLED, currentUserId) != 0
+ DEFAULT_ENABLED, currentUserId) != 0
synchronized(currentFavorites) {
currentFavorites.clear()
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt
new file mode 100644
index 0000000..c053517
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.systemui.controls.management
+
+import android.text.TextUtils
+import android.util.ArrayMap
+import com.android.systemui.controls.ControlStatus
+import com.android.systemui.controls.controller.ControlInfo
+
+/**
+ * This model is used to show all controls separated by zones.
+ *
+ * The model will sort the controls and zones in the following manner:
+ * * The zones will be sorted in a first seen basis
+ * * The controls in each zone will be sorted in a first seen basis.
+ *
+ * @property controls List of all controls as returned by loading
+ * @property initialFavoriteIds sorted ids of favorite controls
+ * @property noZoneString text to use as header for all controls that have blank or `null` zone.
+ */
+class AllModel(
+ private val controls: List<ControlStatus>,
+ initialFavoriteIds: List<String>,
+ private val emptyZoneString: CharSequence
+) : ControlsModel {
+
+ override val favorites: List<ControlInfo.Builder>
+ get() = favoriteIds.mapNotNull { id ->
+ val control = controls.firstOrNull { it.control.controlId == id }?.control
+ control?.let {
+ ControlInfo.Builder().apply {
+ controlId = it.controlId
+ controlTitle = it.title
+ deviceType = it.deviceType
+ }
+ }
+ }
+
+ private val favoriteIds = initialFavoriteIds.toMutableList()
+
+ override val elements: List<ElementWrapper> = createWrappers(controls)
+
+ override fun changeFavoriteStatus(controlId: String, favorite: Boolean) {
+ if (favorite) {
+ favoriteIds.add(controlId)
+ } else {
+ favoriteIds.remove(controlId)
+ }
+ }
+
+ private fun createWrappers(list: List<ControlStatus>): List<ElementWrapper> {
+ val map = list.groupByTo(OrderedMap(ArrayMap<CharSequence, MutableList<ControlStatus>>())) {
+ it.control.zone ?: ""
+ }
+ val output = mutableListOf<ElementWrapper>()
+ var emptyZoneValues: Sequence<ControlWrapper>? = null
+ for (zoneName in map.orderedKeys) {
+ val values = map.getValue(zoneName).asSequence().map { ControlWrapper(it) }
+ if (TextUtils.isEmpty(zoneName)) {
+ emptyZoneValues = values
+ } else {
+ output.add(ZoneNameWrapper(zoneName))
+ output.addAll(values)
+ }
+ }
+ // Add controls with empty zone at the end
+ if (emptyZoneValues != null) {
+ if (map.size != 1) {
+ output.add(ZoneNameWrapper(emptyZoneString))
+ }
+ output.addAll(emptyZoneValues)
+ }
+ return output
+ }
+
+ private class OrderedMap<K, V>(private val map: MutableMap<K, V>) : MutableMap<K, V> by map {
+
+ val orderedKeys = mutableListOf<K>()
+
+ override fun put(key: K, value: V): V? {
+ if (key !in map) {
+ orderedKeys.add(key)
+ }
+ return map.put(key, value)
+ }
+
+ override fun clear() {
+ orderedKeys.clear()
+ map.clear()
+ }
+
+ override fun remove(key: K): V? {
+ val removed = map.remove(key)
+ if (removed != null) {
+ orderedKeys.remove(key)
+ }
+ return removed
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
index ac5e089..25ebc65 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
@@ -116,6 +116,10 @@
fun renderFavoritesForComponent(component: ComponentName): String {
val qty = favoriteFunction(component)
- return resources.getQuantityString(R.plurals.controls_number_of_favorites, qty, qty)
+ if (qty != 0) {
+ return resources.getQuantityString(R.plurals.controls_number_of_favorites, qty, qty)
+ } else {
+ return ""
+ }
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
index d3cabe6..0870a4d 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
@@ -42,8 +42,7 @@
* @param onlyFavorites set to true to only display favorites instead of all controls
*/
class ControlAdapter(
- private val layoutInflater: LayoutInflater,
- private val onlyFavorites: Boolean = false
+ private val layoutInflater: LayoutInflater
) : RecyclerView.Adapter<Holder>() {
companion object {
@@ -57,22 +56,21 @@
}
}
- var modelList: List<ElementWrapper> = emptyList()
- private var favoritesModel: FavoriteModel? = null
+ private var model: ControlsModel? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
return when (viewType) {
TYPE_CONTROL -> {
ControlHolder(
- layoutInflater.inflate(R.layout.controls_base_item, parent, false).apply {
- layoutParams.apply {
- width = ViewGroup.LayoutParams.MATCH_PARENT
- }
- elevation = 15f
- },
- { id, favorite ->
- favoritesModel?.changeFavoriteStatus(id, favorite)
- })
+ layoutInflater.inflate(R.layout.controls_base_item, parent, false).apply {
+ layoutParams.apply {
+ width = ViewGroup.LayoutParams.MATCH_PARENT
+ }
+ elevation = 15f
+ }
+ ) { id, favorite ->
+ model?.changeFavoriteStatus(id, favorite)
+ }
}
TYPE_ZONE -> {
ZoneHolder(layoutInflater.inflate(R.layout.controls_zone_header, parent, false))
@@ -81,27 +79,26 @@
}
}
- fun changeFavoritesModel(favoritesModel: FavoriteModel) {
- this.favoritesModel = favoritesModel
- if (onlyFavorites) {
- modelList = favoritesModel.favorites
- } else {
- modelList = favoritesModel.all
- }
+ fun changeModel(model: ControlsModel) {
+ this.model = model
notifyDataSetChanged()
}
- override fun getItemCount() = modelList.size
+ override fun getItemCount() = model?.elements?.size ?: 0
override fun onBindViewHolder(holder: Holder, index: Int) {
- holder.bindData(modelList[index])
+ model?.let {
+ holder.bindData(it.elements[index])
+ }
}
override fun getItemViewType(position: Int): Int {
- return when (modelList[position]) {
- is ZoneNameWrapper -> TYPE_ZONE
- is ControlWrapper -> TYPE_CONTROL
- }
+ model?.let {
+ return when (it.elements.get(position)) {
+ is ZoneNameWrapper -> TYPE_ZONE
+ is ControlWrapper -> TYPE_CONTROL
+ }
+ } ?: throw IllegalStateException("Getting item type for null model")
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
index af4a977..2c01449 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
@@ -26,11 +26,9 @@
import android.widget.Button
import android.widget.TextView
import androidx.recyclerview.widget.GridLayoutManager
-import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import com.android.systemui.R
import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.controls.controller.ControlInfo
import com.android.systemui.controls.controller.ControlsControllerImpl
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.settings.CurrentUserTracker
@@ -51,33 +49,10 @@
private lateinit var recyclerViewAll: RecyclerView
private lateinit var adapterAll: ControlAdapter
- private lateinit var recyclerViewFavorites: RecyclerView
- private lateinit var adapterFavorites: ControlAdapter
- private lateinit var errorText: TextView
+ private lateinit var statusText: TextView
+ private var model: ControlsModel? = null
private var component: ComponentName? = null
- private var currentModel: FavoriteModel? = null
- private var itemTouchHelperCallback = object : ItemTouchHelper.SimpleCallback(
- /* dragDirs */ ItemTouchHelper.UP
- or ItemTouchHelper.DOWN
- or ItemTouchHelper.LEFT
- or ItemTouchHelper.RIGHT,
- /* swipeDirs */0
- ) {
- override fun onMove(
- recyclerView: RecyclerView,
- viewHolder: RecyclerView.ViewHolder,
- target: RecyclerView.ViewHolder
- ): Boolean {
- return currentModel?.onMoveItem(
- viewHolder.layoutPosition, target.layoutPosition) != null
- }
-
- override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {}
-
- override fun isItemViewSwipeEnabled() = false
- }
-
private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) {
private val startingUser = controller.currentUserId
@@ -89,6 +64,10 @@
}
}
+ override fun onBackPressed() {
+ finish()
+ }
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.controls_management)
@@ -99,21 +78,27 @@
val app = intent.getCharSequenceExtra(EXTRA_APP)
component = intent.getParcelableExtra<ComponentName>(Intent.EXTRA_COMPONENT_NAME)
- errorText = requireViewById(R.id.error_message)
+ statusText = requireViewById(R.id.status_message)
- setUpRecyclerViews()
+ setUpRecyclerView()
requireViewById<TextView>(R.id.title).text = app?.let { it }
?: resources.getText(R.string.controls_favorite_default_title)
requireViewById<TextView>(R.id.subtitle).text =
resources.getText(R.string.controls_favorite_subtitle)
+ requireViewById<Button>(R.id.other_apps).apply {
+ visibility = View.VISIBLE
+ setOnClickListener {
+ this@ControlsFavoritingActivity.onBackPressed()
+ }
+ }
+
requireViewById<Button>(R.id.done).setOnClickListener {
if (component == null) return@setOnClickListener
- val favoritesForStorage = currentModel?.favorites?.map {
- with(it.controlStatus.control) {
- ControlInfo(component!!, controlId, title, deviceType)
- }
+ val favoritesForStorage = model?.favorites?.map {
+ it.componentName = component!!
+ it.build()
}
if (favoritesForStorage != null) {
controller.replaceFavoritesForComponent(component!!, favoritesForStorage)
@@ -122,20 +107,22 @@
}
component?.let {
+ statusText.text = resources.getText(com.android.internal.R.string.loading)
controller.loadForComponent(it, Consumer { data ->
val allControls = data.allControls
val favoriteKeys = data.favoritesIds
val error = data.errorOnLoad
executor.execute {
- val favoriteModel = FavoriteModel(
- allControls,
- favoriteKeys,
- allAdapter = adapterAll,
- favoritesAdapter = adapterFavorites)
- adapterAll.changeFavoritesModel(favoriteModel)
- adapterFavorites.changeFavoritesModel(favoriteModel)
- currentModel = favoriteModel
- errorText.visibility = if (error) View.VISIBLE else View.GONE
+ val emptyZoneString = resources.getText(
+ R.string.controls_favorite_other_zone_header)
+ val model = AllModel(allControls, favoriteKeys, emptyZoneString)
+ adapterAll.changeModel(model)
+ this.model = model
+ if (error) {
+ statusText.text = resources.getText(R.string.controls_favorite_load_error)
+ } else {
+ statusText.visibility = View.GONE
+ }
}
})
}
@@ -143,7 +130,7 @@
currentUserTracker.startTracking()
}
- private fun setUpRecyclerViews() {
+ private fun setUpRecyclerView() {
val margin = resources.getDimensionPixelSize(R.dimen.controls_card_margin)
val itemDecorator = MarginItemDecorator(margin, margin)
val layoutInflater = LayoutInflater.from(applicationContext)
@@ -156,14 +143,6 @@
}
addItemDecoration(itemDecorator)
}
-
- adapterFavorites = ControlAdapter(layoutInflater, true)
- recyclerViewFavorites = requireViewById<RecyclerView>(R.id.listFavorites).apply {
- layoutManager = GridLayoutManager(applicationContext, 2)
- adapter = adapterFavorites
- addItemDecoration(itemDecorator)
- }
- ItemTouchHelper(itemTouchHelperCallback).attachToRecyclerView(recyclerViewFavorites)
}
override fun onDestroy() {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
index 882382c..53f3019 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
@@ -16,6 +16,7 @@
package com.android.systemui.controls.management
+import android.app.ActivityManager
import android.content.ComponentName
import android.content.Context
import android.content.pm.ServiceInfo
@@ -72,7 +73,7 @@
private var availableServices = emptyList<ServiceInfo>()
- override var currentUserId = context.userId
+ override var currentUserId = ActivityManager.getCurrentUser()
private set
private val serviceListingCallback = ServiceListing.Callback {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt
new file mode 100644
index 0000000..a995a2e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.systemui.controls.management
+
+import com.android.systemui.controls.ControlStatus
+import com.android.systemui.controls.controller.ControlInfo
+
+/**
+ * Model for using with [ControlAdapter].
+ *
+ * Implementations of this interface provide different views of the controls to show.
+ */
+interface ControlsModel {
+
+ /**
+ * List of favorites (builders) in order.
+ *
+ * This should be obtained prior to storing the favorites using
+ * [ControlsController.replaceFavoritesForComponent].
+ */
+ val favorites: List<ControlInfo.Builder>
+
+ /**
+ * List of all the elements to display by the corresponding [RecyclerView].
+ */
+ val elements: List<ElementWrapper>
+
+ /**
+ * Change the favorite status of a particular control.
+ */
+ fun changeFavoriteStatus(controlId: String, favorite: Boolean) {}
+
+ /**
+ * Move an item (in elements) from one position to another.
+ */
+ fun onMoveItem(from: Int, to: Int) {}
+}
+
+/**
+ * Wrapper classes for the different types of elements shown in the [RecyclerView]s in
+ * [ControlAdapter].
+ */
+sealed class ElementWrapper
+data class ZoneNameWrapper(val zoneName: CharSequence) : ElementWrapper()
+data class ControlWrapper(val controlStatus: ControlStatus) : ElementWrapper()
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
index ad4bdef..098caf6 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
@@ -21,6 +21,7 @@
import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewStub
+import android.widget.Button
import android.widget.TextView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
@@ -86,6 +87,10 @@
requireViewById<TextView>(R.id.subtitle).text =
resources.getText(R.string.controls_providers_subtitle)
+ requireViewById<Button>(R.id.done).setOnClickListener {
+ this@ControlsProviderSelectorActivity.finishAffinity()
+ }
+
currentUserTracker.startTracking()
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/FavoriteModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/FavoriteModel.kt
index 6bade0a..5c51e3d 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/FavoriteModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/FavoriteModel.kt
@@ -142,12 +142,4 @@
else if (p0 != null && p1 == null) return 1
return p0.toString().compareTo(p1.toString())
}
-}
-
-/**
- * Wrapper classes for the different types of elements shown in the [RecyclerView]s in
- * [ControlsFavoritingActivity].
- */
-sealed class ElementWrapper
-data class ZoneNameWrapper(val zoneName: CharSequence) : ElementWrapper()
-data class ControlWrapper(val controlStatus: ControlStatus) : ElementWrapper()
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index 7c0033c..6c502d2 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -20,6 +20,7 @@
import android.app.INotificationManager;
import android.content.Context;
+import android.content.SharedPreferences;
import android.hardware.display.AmbientDisplayConfiguration;
import android.hardware.display.NightDisplayListener;
import android.os.Handler;
@@ -27,6 +28,7 @@
import android.os.ServiceManager;
import android.util.DisplayMetrics;
import android.view.Choreographer;
+import android.view.IWindowManager;
import android.view.LayoutInflater;
import android.view.WindowManager;
@@ -34,6 +36,7 @@
import com.android.internal.util.NotificationMessagingUtil;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.ViewMediatorCallback;
+import com.android.systemui.Prefs;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.AlwaysOnDisplayPolicy;
@@ -45,6 +48,7 @@
import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NavigationBarController;
+import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DataSaverController;
@@ -79,6 +83,13 @@
/** */
@Provides
+ @Main
+ public SharedPreferences provideSharePreferences(Context context) {
+ return Prefs.get(context);
+ }
+
+ /** */
+ @Provides
public AmbientDisplayConfiguration provideAmbientDispalyConfiguration(Context context) {
return new AmbientDisplayConfiguration(context);
}
@@ -157,6 +168,14 @@
return new ConfigurationControllerImpl(context);
}
+ /** */
+ @Singleton
+ @Provides
+ public AutoHideController provideAutoHideController(Context context,
+ @Main Handler mainHandler, IWindowManager iWindowManager) {
+ return new AutoHideController(context, mainHandler, iWindowManager);
+ }
+
@Singleton
@Provides
public ActivityManagerWrapper provideActivityManagerWrapper() {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
index 3aa14a3..1ec979c 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
@@ -30,6 +30,8 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.IPackageManager;
+import android.content.pm.LauncherApps;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.hardware.SensorPrivacyManager;
import android.media.AudioManager;
@@ -167,6 +169,12 @@
return LatencyTracker.getInstance(context);
}
+ @Singleton
+ @Provides
+ static LauncherApps provideLauncherApps(Context context) {
+ return context.getSystemService(LauncherApps.class);
+ }
+
@SuppressLint("MissingPermission")
@Singleton
@Provides
@@ -184,6 +192,12 @@
@Singleton
@Provides
+ static PackageManager providePackageManager(Context context) {
+ return context.getPackageManager();
+ }
+
+ @Singleton
+ @Provides
static PackageManagerWrapper providePackageManagerWrapper() {
return PackageManagerWrapper.getInstance();
}
@@ -209,6 +223,7 @@
@Provides
@Singleton
+ @Nullable
static TelecomManager provideTelecomManager(Context context) {
return context.getSystemService(TelecomManager.class);
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 5da02dc..24f505d 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -206,7 +206,7 @@
ConfigurationController configurationController, ActivityStarter activityStarter,
KeyguardStateController keyguardStateController, UserManager userManager,
TrustManager trustManager, IActivityManager iActivityManager,
- TelecomManager telecomManager, MetricsLogger metricsLogger,
+ @Nullable TelecomManager telecomManager, MetricsLogger metricsLogger,
BlurUtils blurUtils, SysuiColorExtractor colorExtractor,
IStatusBarService statusBarService,
NotificationShadeWindowController notificationShadeWindowController,
@@ -568,13 +568,16 @@
@Override
public void onPress() {
mMetricsLogger.action(MetricsEvent.ACTION_EMERGENCY_DIALER_FROM_POWER_MENU);
- Intent intent = mTelecomManager.createLaunchEmergencyDialerIntent(null /* number */);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
- | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- intent.putExtra(EmergencyDialerConstants.EXTRA_ENTRY_TYPE,
- EmergencyDialerConstants.ENTRY_TYPE_POWER_MENU);
- mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ if (mTelecomManager != null) {
+ Intent intent = mTelecomManager.createLaunchEmergencyDialerIntent(
+ null /* number */);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+ | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ intent.putExtra(EmergencyDialerConstants.EXTRA_ENTRY_TYPE,
+ EmergencyDialerConstants.ENTRY_TYPE_POWER_MENU);
+ mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index d688f0a..c129035 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -96,6 +96,7 @@
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.InjectionInflationController;
import java.io.FileDescriptor;
@@ -378,6 +379,17 @@
private IKeyguardDrawnCallback mDrawnCallback;
private CharSequence mCustomMessage;
+ private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
+ new DeviceConfig.OnPropertiesChangedListener() {
+ @Override
+ public void onPropertiesChanged(DeviceConfig.Properties properties) {
+ if (properties.getKeyset().contains(NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN)) {
+ mShowHomeOverLockscreen = properties.getBoolean(
+ NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN, true /* defaultValue */);
+ }
+ }
+ };
+
KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() {
@Override
@@ -692,6 +704,8 @@
}
};
+ private DeviceConfigProxy mDeviceConfig;
+
/**
* Injected constructor. See {@link KeyguardModule}.
*/
@@ -705,7 +719,8 @@
DismissCallbackRegistry dismissCallbackRegistry,
KeyguardUpdateMonitor keyguardUpdateMonitor, DumpManager dumpManager,
@UiBackground Executor uiBgExecutor, PowerManager powerManager,
- TrustManager trustManager) {
+ TrustManager trustManager,
+ DeviceConfigProxy deviceConfig) {
super(context);
mFalsingManager = falsingManager;
mLockPatternUtils = lockPatternUtils;
@@ -718,20 +733,15 @@
mPM = powerManager;
mTrustManager = trustManager;
dumpManager.registerDumpable(getClass().getName(), this);
- mShowHomeOverLockscreen = DeviceConfig.getBoolean(
+ mDeviceConfig = deviceConfig;
+ mShowHomeOverLockscreen = mDeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_SYSTEMUI,
NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN,
/* defaultValue = */ true);
- DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post,
- new DeviceConfig.OnPropertiesChangedListener() {
- @Override
- public void onPropertiesChanged(DeviceConfig.Properties properties) {
- if (properties.getKeyset().contains(NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN)) {
- mShowHomeOverLockscreen = properties.getBoolean(
- NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN, true /* defaultValue */);
- }
- }
- });
+ mDeviceConfig.addOnPropertiesChangedListener(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ mHandler::post,
+ mOnPropertiesChangedListener);
}
public void userActivity() {
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 d7af360..367f464 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -31,6 +31,7 @@
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.util.DeviceConfigProxy;
import java.util.concurrent.Executor;
@@ -62,7 +63,8 @@
DumpManager dumpManager,
PowerManager powerManager,
TrustManager trustManager,
- @UiBackground Executor uiBgExecutor) {
+ @UiBackground Executor uiBgExecutor,
+ DeviceConfigProxy deviceConfig) {
return new KeyguardViewMediator(
context,
falsingManager,
@@ -75,6 +77,7 @@
dumpManager,
uiBgExecutor,
powerManager,
- trustManager);
+ trustManager,
+ deviceConfig);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
index b5fd406..1fc1fe4 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
@@ -99,6 +99,10 @@
return mCurrentAnimator;
}
+ PipTransitionAnimator getCurrentAnimator() {
+ return mCurrentAnimator;
+ }
+
private PipTransitionAnimator setupPipTransitionAnimator(PipTransitionAnimator animator) {
animator.setInterpolator(mFastOutSlowInInterpolator);
animator.setFloatValues(FRACTION_START, FRACTION_END);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
index 8c3ccaa..1ae3d4f 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
@@ -336,9 +336,17 @@
// Save the snap fraction and adjust the size based on the new aspect ratio.
final float snapFraction = mSnapAlgorithm.getSnapFraction(stackBounds,
getMovementBounds(stackBounds));
- final int minEdgeSize = useCurrentMinEdgeSize ? mCurrentMinSize : mDefaultMinSize;
- final Size size = mSnapAlgorithm.getSizeForAspectRatio(
- new Size(stackBounds.width(), stackBounds.height()), aspectRatio, minEdgeSize);
+ final int minEdgeSize;
+ final Size size;
+ if (useCurrentMinEdgeSize) {
+ minEdgeSize = mCurrentMinSize;
+ size = mSnapAlgorithm.getSizeForAspectRatio(
+ new Size(stackBounds.width(), stackBounds.height()), aspectRatio, minEdgeSize);
+ } else {
+ minEdgeSize = mDefaultMinSize;
+ size = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio, minEdgeSize,
+ mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
+ }
final int left = (int) (stackBounds.centerX() - size.getWidth() / 2f);
final int top = (int) (stackBounds.centerY() - size.getHeight() / 2f);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index 1555153..665146e 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -166,7 +166,22 @@
* Updates the display dimension with given {@link DisplayInfo}
*/
public void onDisplayInfoChanged(DisplayInfo displayInfo) {
- mDisplayBounds.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
+ final Rect newDisplayBounds = new Rect(0, 0,
+ displayInfo.logicalWidth, displayInfo.logicalHeight);
+ if (!mDisplayBounds.equals(newDisplayBounds)) {
+ // Updates the exiting PiP animation in case the screen rotation changes in the middle.
+ // It's a legit case that PiP window is in portrait mode on home screen and
+ // the application requests landscape onces back to fullscreen mode.
+ final PipAnimationController.PipTransitionAnimator animator =
+ mPipAnimationController.getCurrentAnimator();
+ if (animator != null
+ && animator.getAnimationType() == ANIM_TYPE_BOUNDS
+ && animator.getDestinationBounds().equals(mDisplayBounds)) {
+ animator.updateEndValue(newDisplayBounds);
+ animator.setDestinationBounds(newDisplayBounds);
+ }
+ }
+ mDisplayBounds.set(newDisplayBounds);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index e98dec0..8ada3c3 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -48,6 +48,7 @@
import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.WindowManagerWrapper;
+import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.FloatingContentCoordinator;
import com.android.systemui.wm.DisplayChangeController;
import com.android.systemui.wm.DisplayController;
@@ -68,7 +69,6 @@
private IActivityManager mActivityManager;
private Handler mHandler = new Handler();
- private final PinnedStackListener mPinnedStackListener = new PipManagerPinnedStackListener();
private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
private final Rect mTmpInsetBounds = new Rect();
private final Rect mTmpNormalBounds = new Rect();
@@ -204,12 +204,14 @@
@Inject
public PipManager(Context context, BroadcastDispatcher broadcastDispatcher,
DisplayController displayController,
- FloatingContentCoordinator floatingContentCoordinator) {
+ FloatingContentCoordinator floatingContentCoordinator,
+ DeviceConfigProxy deviceConfig) {
mContext = context;
mActivityManager = ActivityManager.getService();
try {
- WindowManagerWrapper.getInstance().addPinnedStackListener(mPinnedStackListener);
+ WindowManagerWrapper.getInstance().addPinnedStackListener(
+ new PipManagerPinnedStackListener());
} catch (RemoteException e) {
Log.e(TAG, "Failed to register pinned stack listener", e);
}
@@ -225,7 +227,7 @@
mInputConsumerController);
mTouchHandler = new PipTouchHandler(context, mActivityManager, activityTaskManager,
mMenuController, mInputConsumerController, mPipBoundsHandler, mPipTaskOrganizer,
- floatingContentCoordinator);
+ floatingContentCoordinator, deviceConfig);
mAppOpsListener = new PipAppOpsListener(context, mActivityManager,
mTouchHandler.getMotionHelper());
displayController.addDisplayChangingController(mRotationController);
@@ -339,12 +341,15 @@
private void updateMovementBounds(Rect animatingBounds, boolean fromImeAdjustment,
boolean fromShelfAdjustment) {
- // Populate inset / normal bounds and DisplayInfo from mPipBoundsHandler first.
+ // Populate inset / normal bounds and DisplayInfo from mPipBoundsHandler before
+ // passing to mTouchHandler, mTouchHandler would rely on the bounds calculated by
+ // mPipBoundsHandler with up-to-dated information
mPipBoundsHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds,
animatingBounds, mTmpDisplayInfo);
mTouchHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds,
animatingBounds, fromImeAdjustment, fromShelfAdjustment,
mTmpDisplayInfo.rotation);
+ mPipTaskOrganizer.onDisplayInfoChanged(mTmpDisplayInfo);
}
public void dump(PrintWriter pw) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index 91f539c..980d18b 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -411,11 +411,9 @@
}
private void adjustAndAnimatePipOffset(Rect originalBounds, int offset, int duration) {
- if (offset == 0) {
- return;
- }
SomeArgs args = SomeArgs.obtain();
args.arg1 = originalBounds;
+ // offset would be zero if triggered from screen rotation.
args.argi1 = offset;
args.argi2 = duration;
mHandler.sendMessage(mHandler.obtainMessage(MSG_OFFSET_ANIMATE, args));
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
index 9fb6234..389793e 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
@@ -41,6 +41,7 @@
import com.android.internal.policy.TaskResizingAlgorithm;
import com.android.systemui.R;
import com.android.systemui.pip.PipBoundsHandler;
+import com.android.systemui.util.DeviceConfigProxy;
import java.util.concurrent.Executor;
@@ -77,7 +78,8 @@
private int mCtrlType;
public PipResizeGestureHandler(Context context, PipBoundsHandler pipBoundsHandler,
- PipTouchHandler pipTouchHandler, PipMotionHelper motionHelper) {
+ PipTouchHandler pipTouchHandler, PipMotionHelper motionHelper,
+ DeviceConfigProxy deviceConfig) {
final Resources res = context.getResources();
context.getDisplay().getMetrics(mDisplayMetrics);
mDisplayId = context.getDisplayId();
@@ -93,7 +95,7 @@
DeviceConfig.NAMESPACE_SYSTEMUI,
PIP_USER_RESIZE,
/* defaultValue = */ false);
- DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, mMainExecutor,
+ deviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, mMainExecutor,
new DeviceConfig.OnPropertiesChangedListener() {
@Override
public void onPropertiesChanged(DeviceConfig.Properties properties) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 79a25b2..3f73d01 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -48,6 +48,7 @@
import com.android.systemui.pip.PipTaskOrganizer;
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.statusbar.FlingAnimationUtils;
+import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.FloatingContentCoordinator;
import java.io.PrintWriter;
@@ -67,6 +68,8 @@
// Allow dragging the PIP to a location to close it
private final boolean mEnableDismissDragToEdge;
+ // Allow PIP to resize to a slightly bigger state upon touch
+ private final boolean mEnableResize;
private final Context mContext;
private final IActivityManager mActivityManager;
private final PipBoundsHandler mPipBoundsHandler;
@@ -162,7 +165,8 @@
InputConsumerController inputConsumerController,
PipBoundsHandler pipBoundsHandler,
PipTaskOrganizer pipTaskOrganizer,
- FloatingContentCoordinator floatingContentCoordinator) {
+ FloatingContentCoordinator floatingContentCoordinator,
+ DeviceConfigProxy deviceConfig) {
// Initialize the Pip input consumer
mContext = context;
mActivityManager = activityManager;
@@ -177,7 +181,8 @@
mMotionHelper = new PipMotionHelper(mContext, activityTaskManager, pipTaskOrganizer,
mMenuController, mSnapAlgorithm, mFlingAnimationUtils, floatingContentCoordinator);
mPipResizeGestureHandler =
- new PipResizeGestureHandler(context, pipBoundsHandler, this, mMotionHelper);
+ new PipResizeGestureHandler(context, pipBoundsHandler, this, mMotionHelper,
+ deviceConfig);
mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler,
() -> mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
mMovementBounds, true /* allowMenuTimeout */, willResizeMenu()));
@@ -188,6 +193,7 @@
mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset);
mEnableDismissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge);
+ mEnableResize = res.getBoolean(R.bool.config_pipEnableResizeForMenu);
// Register the listener for input consumer touch events
inputConsumerController.setInputListener(this::handleTouchEvent);
@@ -263,6 +269,10 @@
public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds, Rect curBounds,
boolean fromImeAdjustment, boolean fromShelfAdjustment, int displayRotation) {
final int bottomOffset = mIsImeShowing ? mImeHeight : 0;
+ final boolean fromDisplayRotationChanged = (mDisplayRotation != displayRotation);
+ if (fromDisplayRotationChanged) {
+ mTouchState.reset();
+ }
// Re-calculate the expanded bounds
mNormalBounds = normalBounds;
@@ -290,14 +300,14 @@
// If this is from an IME or shelf adjustment, then we should move the PiP so that it is not
// occluded by the IME or shelf.
- if (fromImeAdjustment || fromShelfAdjustment) {
+ if (fromImeAdjustment || fromShelfAdjustment || fromDisplayRotationChanged) {
if (mTouchState.isUserInteracting()) {
// Defer the update of the current movement bounds until after the user finishes
// touching the screen
} else {
final float offsetBufferPx = BOTTOM_OFFSET_BUFFER_DP
* mContext.getResources().getDisplayMetrics().density;
- final Rect toMovementBounds = mMenuState == MENU_STATE_FULL
+ final Rect toMovementBounds = mMenuState == MENU_STATE_FULL && willResizeMenu()
? new Rect(expandedMovementBounds)
: new Rect(normalMovementBounds);
final int prevBottom = mMovementBounds.bottom - mMovementBoundsExtraOffsets;
@@ -691,11 +701,12 @@
};
/**
- * Updates the current movement bounds based on whether the menu is currently visible.
+ * Updates the current movement bounds based on whether the menu is currently visible and
+ * resized.
*/
private void updateMovementBounds(int menuState) {
boolean isMenuExpanded = menuState == MENU_STATE_FULL;
- mMovementBounds = isMenuExpanded
+ mMovementBounds = isMenuExpanded && willResizeMenu()
? mExpandedMovementBounds
: mNormalMovementBounds;
mPipBoundsHandler.setMinEdgeSize(
@@ -715,8 +726,11 @@
* @return whether the menu will resize as a part of showing the full menu.
*/
private boolean willResizeMenu() {
- return mExpandedBounds.width() != mNormalBounds.width() ||
- mExpandedBounds.height() != mNormalBounds.height();
+ if (!mEnableResize) {
+ return false;
+ }
+ return mExpandedBounds.width() != mNormalBounds.width()
+ || mExpandedBounds.height() != mNormalBounds.height();
}
public void dump(PrintWriter pw, String prefix) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 938f9db..17ac5e5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -28,6 +28,7 @@
import android.provider.Settings.Secure;
import android.service.quicksettings.Tile;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.Log;
import com.android.systemui.Dumpable;
@@ -61,6 +62,7 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Optional;
+import java.util.Set;
import java.util.function.Predicate;
import javax.inject.Inject;
@@ -91,6 +93,7 @@
private final ArrayList<QSFactory> mQsFactories = new ArrayList<>();
private int mCurrentUser;
private final Optional<StatusBar> mStatusBarOptional;
+ private Context mUserContext;
@Inject
public QSTileHost(Context context,
@@ -107,6 +110,7 @@
QSLogger qsLogger) {
mIconController = iconController;
mContext = context;
+ mUserContext = context;
mTunerService = tunerService;
mPluginManager = pluginManager;
mDumpManager = dumpManager;
@@ -207,6 +211,9 @@
return mContext;
}
+ public Context getUserContext() {
+ return mUserContext;
+ }
public TileServices getTileServices() {
return mServices;
@@ -227,6 +234,9 @@
}
final List<String> tileSpecs = loadTileSpecs(mContext, newValue);
int currentUser = ActivityManager.getCurrentUser();
+ if (currentUser != mCurrentUser) {
+ mUserContext = mContext.createContextAsUser(UserHandle.of(currentUser), 0);
+ }
if (tileSpecs.equals(mTileSpecs) && currentUser == mCurrentUser) return;
mTiles.entrySet().stream().filter(tile -> !tileSpecs.contains(tile.getKey())).forEach(
tile -> {
@@ -253,6 +263,13 @@
mQSLogger.logTileDestroyed(tileSpec, "Tile not available");
}
} else {
+ // This means that the tile is a CustomTile AND the user is different, so let's
+ // destroy it
+ if (tile != null) {
+ tile.destroy();
+ Log.d(TAG, "Destroying tile for wrong user: " + tileSpec);
+ mQSLogger.logTileDestroyed(tileSpec, "Tile for wrong user");
+ }
Log.d(TAG, "Creating tile: " + tileSpec);
try {
tile = createTile(tileSpec);
@@ -273,7 +290,7 @@
}
}
mCurrentUser = currentUser;
- List<String> currentSpecs = new ArrayList(mTileSpecs);
+ List<String> currentSpecs = new ArrayList<>(mTileSpecs);
mTileSpecs.clear();
mTileSpecs.addAll(tileSpecs);
mTiles.clear();
@@ -300,7 +317,7 @@
}
public void addTile(String spec) {
- changeTileSpecs(tileSpecs-> tileSpecs.add(spec));
+ changeTileSpecs(tileSpecs-> !tileSpecs.contains(spec) && tileSpecs.add(spec));
}
private void changeTileSpecs(Predicate<List<String>> changeFunction) {
@@ -314,9 +331,12 @@
}
public void addTile(ComponentName tile) {
- List<String> newSpecs = new ArrayList<>(mTileSpecs);
- newSpecs.add(0, CustomTile.toSpec(tile));
- changeTiles(mTileSpecs, newSpecs);
+ String spec = CustomTile.toSpec(tile);
+ if (!mTileSpecs.contains(spec)) {
+ List<String> newSpecs = new ArrayList<>(mTileSpecs);
+ newSpecs.add(0, spec);
+ changeTiles(mTileSpecs, newSpecs);
+ }
}
public void removeTile(ComponentName tile) {
@@ -326,10 +346,10 @@
}
public void changeTiles(List<String> previousTiles, List<String> newTiles) {
- final int NP = previousTiles.size();
- final int NA = newTiles.size();
+ final List<String> copy = new ArrayList<>(previousTiles);
+ final int NP = copy.size();
for (int i = 0; i < NP; i++) {
- String tileSpec = previousTiles.get(i);
+ String tileSpec = copy.get(i);
if (!tileSpec.startsWith(CustomTile.PREFIX)) continue;
if (!newTiles.contains(tileSpec)) {
ComponentName component = CustomTile.getComponentFromSpec(tileSpec);
@@ -380,16 +400,26 @@
}
final ArrayList<String> tiles = new ArrayList<String>();
boolean addedDefault = false;
+ Set<String> addedSpecs = new ArraySet<>();
for (String tile : tileList.split(",")) {
tile = tile.trim();
if (tile.isEmpty()) continue;
if (tile.equals("default")) {
if (!addedDefault) {
- tiles.addAll(getDefaultSpecs(context));
+ List<String> defaultSpecs = getDefaultSpecs(context);
+ for (String spec : defaultSpecs) {
+ if (!addedSpecs.contains(spec)) {
+ tiles.add(spec);
+ addedSpecs.add(spec);
+ }
+ }
addedDefault = true;
}
} else {
- tiles.add(tile);
+ if (!addedSpecs.contains(tile)) {
+ tiles.add(tile);
+ addedSpecs.add(tile);
+ }
}
}
return tiles;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 21a424c..3b27fb7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -20,6 +20,7 @@
import android.app.ActivityManager;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -72,15 +73,19 @@
private android.graphics.drawable.Icon mDefaultIcon;
private CharSequence mDefaultLabel;
+ private final Context mUserContext;
+
private boolean mListening;
private boolean mIsTokenGranted;
private boolean mIsShowingDialog;
- private CustomTile(QSTileHost host, String action) {
+ private CustomTile(QSTileHost host, String action, Context userContext) {
super(host);
mWindowManager = WindowManagerGlobal.getWindowManagerService();
mComponent = ComponentName.unflattenFromString(action);
mTile = new Tile();
+ mUserContext = userContext;
+ mUser = mUserContext.getUserId();
updateDefaultTileAndIcon();
mServiceManager = host.getTileServices().getTileWrapper(this);
if (mServiceManager.isToggleableTile()) {
@@ -90,7 +95,6 @@
mService = mServiceManager.getTileService();
mServiceManager.setTileChangeListener(this);
- mUser = ActivityManager.getCurrentUser();
}
@Override
@@ -100,7 +104,7 @@
private void updateDefaultTileAndIcon() {
try {
- PackageManager pm = mContext.getPackageManager();
+ PackageManager pm = mUserContext.getPackageManager();
int flags = PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.MATCH_DIRECT_BOOT_AWARE;
if (isSystemApp(pm)) {
flags |= PackageManager.MATCH_DISABLED_COMPONENTS;
@@ -318,15 +322,16 @@
state.state = tileState;
Drawable drawable;
try {
- drawable = mTile.getIcon().loadDrawable(mContext);
+ drawable = mTile.getIcon().loadDrawable(mUserContext);
} catch (Exception e) {
Log.w(TAG, "Invalid icon, forcing into unavailable state");
state.state = Tile.STATE_UNAVAILABLE;
- drawable = mDefaultIcon.loadDrawable(mContext);
+ drawable = mDefaultIcon.loadDrawable(mUserContext);
}
final Drawable drawableF = drawable;
state.iconSupplier = () -> {
+ if (drawableF == null) return null;
Drawable.ConstantState cs = drawableF.getConstantState();
if (cs != null) {
return new DrawableIcon(cs.newDrawable());
@@ -387,7 +392,7 @@
return ComponentName.unflattenFromString(action);
}
- public static CustomTile create(QSTileHost host, String spec) {
+ public static CustomTile create(QSTileHost host, String spec, Context userContext) {
if (spec == null || !spec.startsWith(PREFIX) || !spec.endsWith(")")) {
throw new IllegalArgumentException("Bad custom tile spec: " + spec);
}
@@ -395,6 +400,6 @@
if (action.isEmpty()) {
throw new IllegalArgumentException("Empty custom tile spec action");
}
- return new CustomTile(host, action);
+ return new CustomTile(host, action, userContext);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
index 17b0251..aa51771 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -279,6 +279,7 @@
if (mPackageReceiverRegistered.get() || mUserReceiverRegistered.get()) {
stopPackageListening();
}
+ mChangeListener = null;
}
private void handleDeath() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 554672d..1b8717b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -178,7 +178,9 @@
}
// Custom tiles
- if (tileSpec.startsWith(CustomTile.PREFIX)) return CustomTile.create(mHost, tileSpec);
+ if (tileSpec.startsWith(CustomTile.PREFIX)) {
+ return CustomTile.create(mHost, tileSpec, mHost.getUserContext());
+ }
// Debug tiles.
if (Build.IS_DEBUGGABLE) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index ebf45a6..9f7b84a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -53,6 +53,7 @@
import com.android.systemui.R;
import com.android.systemui.SysUIToast;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
@@ -79,6 +80,7 @@
private final ZenModeController mController;
private final DndDetailAdapter mDetailAdapter;
private final ActivityStarter mActivityStarter;
+ private final SharedPreferences mSharedPreferences;
private final BroadcastDispatcher mBroadcastDispatcher;
private boolean mListening;
@@ -87,10 +89,12 @@
@Inject
public DndTile(QSHost host, ZenModeController zenModeController,
- ActivityStarter activityStarter, BroadcastDispatcher broadcastDispatcher) {
+ ActivityStarter activityStarter, BroadcastDispatcher broadcastDispatcher,
+ @Main SharedPreferences sharedPreferences) {
super(host);
mController = zenModeController;
mActivityStarter = activityStarter;
+ mSharedPreferences = sharedPreferences;
mDetailAdapter = new DndDetailAdapter();
mBroadcastDispatcher = broadcastDispatcher;
broadcastDispatcher.registerReceiver(mReceiver, new IntentFilter(ACTION_SET_VISIBLE));
@@ -111,16 +115,16 @@
Prefs.putBoolean(context, Prefs.Key.DND_TILE_VISIBLE, visible);
}
- public static boolean isVisible(Context context) {
- return Prefs.getBoolean(context, Prefs.Key.DND_TILE_VISIBLE, false /* defaultValue */);
+ public static boolean isVisible(SharedPreferences prefs) {
+ return prefs.getBoolean(Prefs.Key.DND_TILE_VISIBLE, false /* defaultValue */);
}
public static void setCombinedIcon(Context context, boolean combined) {
Prefs.putBoolean(context, Prefs.Key.DND_TILE_COMBINED_ICON, combined);
}
- public static boolean isCombinedIcon(Context context) {
- return Prefs.getBoolean(context, Prefs.Key.DND_TILE_COMBINED_ICON,
+ public static boolean isCombinedIcon(SharedPreferences sharedPreferences) {
+ return sharedPreferences.getBoolean(Prefs.Key.DND_TILE_COMBINED_ICON,
false /* defaultValue */);
}
@@ -301,7 +305,7 @@
@Override
public boolean isAvailable() {
- return isVisible(mContext);
+ return isVisible(mSharedPreferences);
}
private final OnSharedPreferenceChangeListener mPrefListener
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index 2557226..5dcb4e3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -16,9 +16,9 @@
package com.android.systemui.qs.tiles;
-import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.widget.Switch;
@@ -81,11 +81,11 @@
}
public static boolean isCurrentOrientationLockPortrait(RotationLockController controller,
- Context context) {
+ Resources resources) {
int lockOrientation = controller.getRotationLockOrientation();
if (lockOrientation == Configuration.ORIENTATION_UNDEFINED) {
// Freely rotating device; use current rotation
- return context.getResources().getConfiguration().orientation
+ return resources.getConfiguration().orientation
!= Configuration.ORIENTATION_LANDSCAPE;
} else {
return lockOrientation != Configuration.ORIENTATION_LANDSCAPE;
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index 626f298..390ac09 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -63,7 +63,7 @@
/**
* A service which records the device screen and optionally microphone input.
*/
-public class RecordingService extends Service {
+public class RecordingService extends Service implements MediaRecorder.OnInfoListener {
public static final int REQUEST_CODE = 2;
private static final int NOTIFICATION_ID = 1;
@@ -85,6 +85,8 @@
private static final int VIDEO_FRAME_RATE = 30;
private static final int AUDIO_BIT_RATE = 16;
private static final int AUDIO_SAMPLE_RATE = 44100;
+ private static final int MAX_DURATION_MS = 60 * 60 * 1000;
+ private static final long MAX_FILESIZE_BYTES = 5000000000L;
private final RecordingController mController;
private MediaProjection mMediaProjection;
@@ -250,6 +252,8 @@
mMediaRecorder.setVideoSize(screenWidth, screenHeight);
mMediaRecorder.setVideoFrameRate(VIDEO_FRAME_RATE);
mMediaRecorder.setVideoEncodingBitRate(VIDEO_BIT_RATE);
+ mMediaRecorder.setMaxDuration(MAX_DURATION_MS);
+ mMediaRecorder.setMaxFileSize(MAX_FILESIZE_BYTES);
// Set up audio
if (mUseAudio) {
@@ -274,6 +278,7 @@
null,
null);
+ mMediaRecorder.setOnInfoListener(this);
mMediaRecorder.start();
mController.updateState(true);
} catch (IOException e) {
@@ -454,4 +459,10 @@
return new Intent(context, RecordingService.class).setAction(ACTION_DELETE)
.putExtra(EXTRA_PATH, path);
}
+
+ @Override
+ public void onInfo(MediaRecorder mr, int what, int extra) {
+ Log.d(TAG, "Media recorder info: " + what);
+ onStartCommand(getStopIntent(this), 0, 0);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index f06cd54..7c770f4 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -514,12 +514,17 @@
1, cornerScale, mFastOutSlowIn.getInterpolation(t / scalePct));
mScreenshotView.setScaleX(scale);
mScreenshotView.setScaleY(scale);
+ } else {
+ mScreenshotView.setScaleX(cornerScale);
+ mScreenshotView.setScaleY(cornerScale);
}
if (t < xPositionPct) {
float xCenter = MathUtils.lerp(startPos.x, finalPos.x,
mFastOutSlowIn.getInterpolation(t / xPositionPct));
mScreenshotView.setX(xCenter - width * mScreenshotView.getScaleX() / 2f);
+ } else {
+ mScreenshotView.setX(finalPos.x - width * mScreenshotView.getScaleX() / 2f);
}
float yCenter = MathUtils.lerp(startPos.y, finalPos.y,
mFastOutSlowIn.getInterpolation(t));
@@ -544,6 +549,10 @@
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
+ mScreenshotView.setScaleX(cornerScale);
+ mScreenshotView.setScaleY(cornerScale);
+ mScreenshotView.setX(finalPos.x - width * cornerScale / 2f);
+ mScreenshotView.setY(finalPos.y - height * cornerScale / 2f);
Rect bounds = new Rect();
mScreenshotView.getBoundsOnScreen(bounds);
mDismissButton.setX(bounds.right - mDismissButtonSize / 2f);
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index 56cdff4..27b799b 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -401,6 +401,7 @@
return;
}
mMinimized = minimized;
+ WindowManagerProxy.applyPrimaryFocusable(mSplits, !mMinimized);
mView.setMinimizedDockStack(minimized, getAnimDuration(), mHomeStackResizable);
updateTouchable();
});
@@ -504,6 +505,7 @@
final boolean wasMinimized = mMinimized;
mMinimized = true;
setHomeStackResizable(mSplits.mSecondary.isResizable());
+ WindowManagerProxy.applyPrimaryFocusable(mSplits, false /* focusable */);
if (!inSplitMode()) {
// Wasn't in split-mode yet, so enter now.
if (DEBUG) {
@@ -521,6 +523,9 @@
}
void ensureNormalSplit() {
+ if (mMinimized) {
+ WindowManagerProxy.applyPrimaryFocusable(mSplits, true /* focusable */);
+ }
if (!inSplitMode()) {
// Wasn't in split-mode, so enter now.
if (DEBUG) {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
index 7685733..167c33a 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -292,10 +292,23 @@
for (int i = freeHomeAndRecents.size() - 1; i >= 0; --i) {
wct.setBounds(freeHomeAndRecents.get(i).token, null);
}
+ // Reset focusable to true
+ wct.setFocusable(tiles.mPrimary.token, true /* focusable */);
ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(wct,
null /* organizer */);
} catch (RemoteException e) {
Log.w(TAG, "Failed to remove stack: " + e);
}
}
+
+ static void applyPrimaryFocusable(SplitScreenTaskOrganizer splits, boolean focusable) {
+ try {
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setFocusable(splits.mPrimary.token, focusable);
+ ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(wct,
+ null /* organizer */);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error setting focusability: " + e);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AutoHideUiElement.java b/packages/SystemUI/src/com/android/systemui/statusbar/AutoHideUiElement.java
new file mode 100644
index 0000000..af04135
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AutoHideUiElement.java
@@ -0,0 +1,53 @@
+/*
+ * 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;
+
+/**
+ * Common interface for a UI element controlled by
+ * {@link com.android.systemui.statusbar.phone.AutoHideController}. These UI elements automatically
+ * hidden by {@link com.android.systemui.statusbar.phone.AutoHideController} when in some transient
+ * state.
+ */
+public interface AutoHideUiElement {
+
+ /**
+ * Ensures that the {@link AutoHideUiElement} reflects the current expected state. This
+ * method will be posted as a {@link Runnable} in the main thread.
+ */
+ void synchronizeState();
+
+ /**
+ * The {@link com.android.systemui.statusbar.phone.AutoHideController} is responsible for
+ * automatically hiding ui elements that are only shown transiently. This method determines
+ * whether a manual touch should also hide the ui elements that are temporarily visible.
+ *
+ * Note that all {@link AutoHideUiElement} instances should return true for a manual touch to
+ * trigger {@link #hide()} on the ui elements.
+ */
+ default boolean shouldHideOnTouch() {
+ return true;
+ }
+
+ /** Returns true if the {@link AutoHideUiElement} is visible. */
+ boolean isVisible();
+
+ /**
+ * Called to hide the {@link AutoHideUiElement} through the
+ * {@link com.android.systemui.statusbar.phone.AutoHideController}.
+ */
+ void hide();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
index a8188b3..ebac4b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
@@ -149,7 +149,6 @@
AutoHideController autoHideController = isOnDefaultDisplay
? Dependency.get(AutoHideController.class)
: new AutoHideController(context, mHandler,
- Dependency.get(NotificationRemoteInputManager.class),
Dependency.get(IWindowManager.class));
navBar.setAutoHideController(autoHideController);
navBar.restoreAppearanceAndTransientState();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index b43fe73..047edd2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -20,6 +20,7 @@
import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON;
import static com.android.systemui.statusbar.phone.StatusBar.DEBUG;
+import android.annotation.NonNull;
import android.annotation.SuppressLint;
import android.app.NotificationManager;
import android.content.ComponentName;
@@ -161,6 +162,15 @@
}
}
+ public final void unsnoozeNotification(@NonNull String key) {
+ if (!isBound()) return;
+ try {
+ getNotificationInterface().unsnoozeNotificationFromSystemListener(mWrapper, key);
+ } catch (android.os.RemoteException ex) {
+ Log.v(TAG, "Unable to contact notification manager", ex);
+ }
+ }
+
public void registerAsSystemService() {
try {
registerAsSystemService(mContext,
@@ -195,7 +205,8 @@
new ArrayList<>(),
false,
false,
- false
+ false,
+ null
);
}
return ranking;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index f8db922..3d7beea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -65,6 +65,7 @@
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.DeviceConfigProxy;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -183,7 +184,8 @@
NotificationEntryManager notificationEntryManager,
MediaArtworkProcessor mediaArtworkProcessor,
KeyguardBypassController keyguardBypassController,
- @Main Executor mainExecutor) {
+ @Main Executor mainExecutor,
+ DeviceConfigProxy deviceConfig) {
mContext = context;
mMediaArtworkProcessor = mediaArtworkProcessor;
mKeyguardBypassController = keyguardBypassController;
@@ -212,7 +214,8 @@
public void onEntryRemoved(
NotificationEntry entry,
NotificationVisibility visibility,
- boolean removedByUser) {
+ boolean removedByUser,
+ int reason) {
onNotificationRemoved(entry.getKey());
}
});
@@ -221,7 +224,7 @@
DeviceConfig.getProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.COMPACT_MEDIA_SEEKBAR_ENABLED));
- DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
+ deviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
mContext.getMainExecutor(),
mPropertiesChangedListener);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index ebc2fa6..bf28040 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -292,7 +292,8 @@
public void onEntryRemoved(
@Nullable NotificationEntry entry,
NotificationVisibility visibility,
- boolean removedByUser) {
+ boolean removedByUser,
+ int reason) {
// We're removing the notification, the smart controller can forget about it.
mSmartReplyController.stopSending(entry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 7e70c20..fe2f1f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -30,6 +30,7 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.dagger.StatusBarModule;
+import com.android.systemui.statusbar.notification.DynamicChildBindController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
@@ -59,11 +60,12 @@
private final Handler mHandler;
- //TODO: change this top <Entry, List<Entry>>?
- private final HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>>
- mTmpChildOrderMap = new HashMap<>();
+ /** Re-usable map of notifications to their sorted children.*/
+ private final HashMap<NotificationEntry, List<NotificationEntry>> mTmpChildOrderMap =
+ new HashMap<>();
// Dependencies:
+ private final DynamicChildBindController mDynamicChildBindController;
protected final NotificationLockscreenUserManager mLockscreenUserManager;
protected final NotificationGroupManager mGroupManager;
protected final VisualStabilityManager mVisualStabilityManager;
@@ -105,7 +107,8 @@
KeyguardBypassController bypassController,
BubbleController bubbleController,
DynamicPrivacyController privacyController,
- ForegroundServiceSectionController fgsSectionController) {
+ ForegroundServiceSectionController fgsSectionController,
+ DynamicChildBindController dynamicChildBindController) {
mContext = context;
mHandler = mainHandler;
mLockscreenUserManager = notificationLockscreenUserManager;
@@ -121,6 +124,7 @@
mBubbleController = bubbleController;
mDynamicPrivacyController = privacyController;
privacyController.addListener(this);
+ mDynamicChildBindController = dynamicChildBindController;
}
public void setUpWithPresenter(NotificationPresenter presenter,
@@ -175,13 +179,12 @@
ent.getRow().setNeedsRedaction(needsRedaction);
if (mGroupManager.isChildInGroupWithSummary(ent.getSbn())) {
NotificationEntry summary = mGroupManager.getGroupSummary(ent.getSbn());
- List<ExpandableNotificationRow> orderedChildren =
- mTmpChildOrderMap.get(summary.getRow());
+ List<NotificationEntry> orderedChildren = mTmpChildOrderMap.get(summary);
if (orderedChildren == null) {
orderedChildren = new ArrayList<>();
- mTmpChildOrderMap.put(summary.getRow(), orderedChildren);
+ mTmpChildOrderMap.put(summary, orderedChildren);
}
- orderedChildren.add(ent.getRow());
+ orderedChildren.add(ent);
} else {
toShow.add(ent.getRow());
}
@@ -260,6 +263,7 @@
}
+ mDynamicChildBindController.updateChildContentViews(mTmpChildOrderMap);
mVisualStabilityManager.onReorderingFinished();
// clear the map again for the next usage
mTmpChildOrderMap.clear();
@@ -274,6 +278,7 @@
private void addNotificationChildrenAndSort() {
// Let's now add all notification children which are missing
boolean orderChanged = false;
+ ArrayList<ExpandableNotificationRow> orderedRows = new ArrayList<>();
for (int i = 0; i < mListContainer.getContainerChildCount(); i++) {
View view = mListContainer.getContainerChildAt(i);
if (!(view instanceof ExpandableNotificationRow)) {
@@ -283,11 +288,11 @@
ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
List<ExpandableNotificationRow> children = parent.getNotificationChildren();
- List<ExpandableNotificationRow> orderedChildren = mTmpChildOrderMap.get(parent);
+ List<NotificationEntry> orderedChildren = mTmpChildOrderMap.get(parent.getEntry());
for (int childIndex = 0; orderedChildren != null && childIndex < orderedChildren.size();
childIndex++) {
- ExpandableNotificationRow childView = orderedChildren.get(childIndex);
+ ExpandableNotificationRow childView = orderedChildren.get(childIndex).getRow();
if (children == null || !children.contains(childView)) {
if (childView.getParent() != null) {
Log.wtf(TAG, "trying to add a notification child that already has " +
@@ -300,11 +305,13 @@
parent.addChildNotification(childView, childIndex);
mListContainer.notifyGroupChildAdded(childView);
}
+ orderedRows.add(childView);
}
// Finally after removing and adding has been performed we can apply the order.
- orderChanged |= parent.applyChildOrder(orderedChildren, mVisualStabilityManager,
+ orderChanged |= parent.applyChildOrder(orderedRows, mVisualStabilityManager,
mEntryManager);
+ orderedRows.clear();
}
if (orderChanged) {
mListContainer.generateChildOrderChangedEvent();
@@ -323,13 +330,13 @@
ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
List<ExpandableNotificationRow> children = parent.getNotificationChildren();
- List<ExpandableNotificationRow> orderedChildren = mTmpChildOrderMap.get(parent);
+ List<NotificationEntry> orderedChildren = mTmpChildOrderMap.get(parent.getEntry());
if (children != null) {
toRemove.clear();
for (ExpandableNotificationRow childRow : children) {
if ((orderedChildren == null
- || !orderedChildren.contains(childRow))
+ || !orderedChildren.contains(childRow.getEntry()))
&& !childRow.keepInParent()) {
toRemove.add(childRow);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index cd5bb77..4c99a90 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -32,6 +32,7 @@
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
import com.android.systemui.statusbar.SmartReplyController;
+import com.android.systemui.statusbar.notification.DynamicChildBindController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
@@ -42,6 +43,7 @@
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.tracing.ProtoTracer;
+import com.android.systemui.util.DeviceConfigProxy;
import java.util.concurrent.Executor;
@@ -91,7 +93,8 @@
NotificationEntryManager notificationEntryManager,
MediaArtworkProcessor mediaArtworkProcessor,
KeyguardBypassController keyguardBypassController,
- @Main Executor mainExecutor) {
+ @Main Executor mainExecutor,
+ DeviceConfigProxy deviceConfigProxy) {
return new NotificationMediaManager(
context,
statusBarLazy,
@@ -99,7 +102,8 @@
notificationEntryManager,
mediaArtworkProcessor,
keyguardBypassController,
- mainExecutor);
+ mainExecutor,
+ deviceConfigProxy);
}
/** */
@@ -135,7 +139,8 @@
KeyguardBypassController bypassController,
BubbleController bubbleController,
DynamicPrivacyController privacyController,
- ForegroundServiceSectionController fgsSectionController) {
+ ForegroundServiceSectionController fgsSectionController,
+ DynamicChildBindController dynamicChildBindController) {
return new NotificationViewHierarchyManager(
context,
mainHandler,
@@ -147,7 +152,8 @@
bypassController,
bubbleController,
privacyController,
- fgsSectionController);
+ fgsSectionController,
+ dynamicChildBindController);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicChildBindController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicChildBindController.java
new file mode 100644
index 0000000..059d6ff
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicChildBindController.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.systemui.statusbar.notification;
+
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_CONTRACTED;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_EXPANDED;
+import static com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED;
+
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.RowContentBindParams;
+import com.android.systemui.statusbar.notification.row.RowContentBindStage;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.inject.Inject;
+
+/**
+ * Controller that binds/unbinds views content views on notification group children.
+ *
+ * We currently only show a limited number of notification children even if more exist, so we
+ * can save memory by freeing content views when they're not visible and binding them again when
+ * they get close to being visible.
+ *
+ * Eventually, when {@link NotifPipeline} takes over as the new notification pipeline, we'll have
+ * more control over which notifications even make it to inflation in the first place and be able
+ * to enforce this at an earlier stage at the level of the {@link ExpandableNotificationRow}, but
+ * for now, we're just doing it at the level of content views.
+ */
+public class DynamicChildBindController {
+ private final RowContentBindStage mStage;
+ private final int mChildBindCutoff;
+
+ @Inject
+ public DynamicChildBindController(RowContentBindStage stage) {
+ this(stage, CHILD_BIND_CUTOFF);
+ }
+
+ /**
+ * @param childBindCutoff the cutoff where we no longer bother having content views bound
+ */
+ DynamicChildBindController(
+ RowContentBindStage stage,
+ int childBindCutoff) {
+ mStage = stage;
+ mChildBindCutoff = childBindCutoff;
+ }
+
+ /**
+ * Update the child content views, unbinding content views on children that won't be visible
+ * and binding content views on children that will be visible eventually.
+ *
+ * @param groupNotifs map of notification summaries to their children
+ */
+ public void updateChildContentViews(
+ Map<NotificationEntry, List<NotificationEntry>> groupNotifs) {
+ for (NotificationEntry entry : groupNotifs.keySet()) {
+ List<NotificationEntry> children = groupNotifs.get(entry);
+ for (int j = 0; j < children.size(); j++) {
+ NotificationEntry childEntry = children.get(j);
+ if (j >= mChildBindCutoff) {
+ if (hasChildContent(childEntry)) {
+ freeChildContent(childEntry);
+ }
+ } else {
+ if (!hasChildContent(childEntry)) {
+ bindChildContent(childEntry);
+ }
+ }
+ }
+ }
+ }
+
+ private boolean hasChildContent(NotificationEntry entry) {
+ ExpandableNotificationRow row = entry.getRow();
+ return row.getPrivateLayout().getContractedChild() != null
+ || row.getPrivateLayout().getExpandedChild() != null;
+ }
+
+ private void freeChildContent(NotificationEntry entry) {
+ RowContentBindParams params = mStage.getStageParams(entry);
+ params.freeContentViews(FLAG_CONTENT_VIEW_CONTRACTED);
+ params.freeContentViews(FLAG_CONTENT_VIEW_EXPANDED);
+ mStage.requestRebind(entry, null);
+ }
+
+ private void bindChildContent(NotificationEntry entry) {
+ RowContentBindParams params = mStage.getStageParams(entry);
+ params.requireContentViews(FLAG_CONTENT_VIEW_CONTRACTED);
+ params.requireContentViews(FLAG_CONTENT_VIEW_EXPANDED);
+ mStage.requestRebind(entry, null);
+ }
+
+ /**
+ * How big the buffer of extra views we keep around to be ready to show when we do need to
+ * dynamically inflate.
+ */
+ private static final int EXTRA_VIEW_BUFFER_COUNT = 1;
+
+ private static final int CHILD_BIND_CUTOFF =
+ NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED + EXTRA_VIEW_BUFFER_COUNT;
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
index 07cf9d9..df21f0b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
@@ -77,7 +77,8 @@
public void onEntryRemoved(
NotificationEntry entry,
NotificationVisibility visibility,
- boolean removedByUser) {
+ boolean removedByUser,
+ int reason) {
stopAlerting(entry.getKey());
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
index 25253a1..2beceb2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
@@ -92,7 +92,8 @@
default void onEntryRemoved(
NotificationEntry entry,
@Nullable NotificationVisibility visibility,
- boolean removedByUser) {
+ boolean removedByUser,
+ int reason) {
}
/**
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 5ebd368..7f0479c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -477,7 +477,7 @@
mLogger.logNotifRemoved(entry.getKey(), removedByUser);
for (NotificationEntryListener listener : mNotificationEntryListeners) {
- listener.onEntryRemoved(entry, visibility, removedByUser);
+ listener.onEntryRemoved(entry, visibility, removedByUser, reason);
}
for (NotifCollectionListener listener : mNotifCollectionListeners) {
// NEM doesn't have a good knowledge of reasons so defaulting to unknown.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java
index f2765db..c9c6f28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java
@@ -59,7 +59,8 @@
public void onEntryRemoved(
NotificationEntry entry,
NotificationVisibility visibility,
- boolean removedByUser) {
+ boolean removedByUser,
+ int reason) {
mListContainer.cleanUpViewStateForEntry(entry);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 25a832d..3e9d8a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -153,6 +153,7 @@
public String remoteInputMimeType;
public Uri remoteInputUri;
private Notification.BubbleMetadata mBubbleMetadata;
+ private ShortcutInfo mShortcutInfo;
/**
* If {@link android.app.RemoteInput#getEditChoicesBeforeSending} is enabled, and the user is
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 becb758..6e161c9 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
@@ -213,7 +213,8 @@
public void onEntryRemoved(
NotificationEntry entry,
NotificationVisibility visibility,
- boolean removedByUser) {
+ boolean removedByUser,
+ int reason) {
if (removedByUser && visibility != null) {
logNotificationClear(entry.getKey(), entry.getSbn(), visibility);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHub.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHub.kt
index 2a02c19..3007198 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHub.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHub.kt
@@ -47,7 +47,7 @@
val key: PersonKey,
val name: CharSequence,
val avatar: Drawable,
- val clickIntent: PendingIntent,
+ val clickRunnable: Runnable,
val userId: Int
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt
index 721f32a..360bf96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt
@@ -17,20 +17,30 @@
package com.android.systemui.statusbar.notification.people
import android.app.Notification
+import android.content.Context
+import android.content.pm.LauncherApps
+import android.content.pm.PackageManager
+import android.content.pm.ShortcutInfo
import android.content.pm.UserInfo
+import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.os.UserManager
+import android.service.notification.NotificationListenerService
+import android.service.notification.NotificationListenerService.REASON_SNOOZED
import android.service.notification.StatusBarNotification
+import android.util.IconDrawableFactory
import android.util.SparseArray
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import com.android.internal.statusbar.NotificationVisibility
import com.android.internal.widget.MessagingGroup
+import com.android.settingslib.notification.ConversationIconFactory
import com.android.systemui.R
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.NotificationPersonExtractorPlugin
+import com.android.systemui.statusbar.NotificationListener
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.notification.NotificationEntryListener
import com.android.systemui.statusbar.notification.NotificationEntryManager
@@ -69,7 +79,7 @@
override fun extractPerson(sbn: StatusBarNotification) =
plugin?.extractPerson(sbn)?.run {
- PersonModel(key, name, avatar, clickIntent, sbn.user.identifier)
+ PersonModel(key, name, avatar, clickRunnable, sbn.user.identifier)
}
override fun extractPersonKey(sbn: StatusBarNotification) = plugin?.extractPersonKey(sbn)
@@ -80,17 +90,26 @@
@Singleton
class PeopleHubDataSourceImpl @Inject constructor(
- private val notificationEntryManager: NotificationEntryManager,
- private val extractor: NotificationPersonExtractor,
- private val userManager: UserManager,
- @Background private val bgExecutor: Executor,
- @Main private val mainExecutor: Executor,
- private val notifLockscreenUserMgr: NotificationLockscreenUserManager
-) : DataSource<PeopleHubModel> {
+ private val notificationEntryManager: NotificationEntryManager,
+ private val extractor: NotificationPersonExtractor,
+ private val userManager: UserManager,
+ private val launcherApps: LauncherApps,
+ private val packageManager: PackageManager,
+ private val c: Context,
+ private val notificationListener: NotificationListener,
+ @Background private val bgExecutor: Executor,
+ @Main private val mainExecutor: Executor,
+ private val notifLockscreenUserMgr: NotificationLockscreenUserManager,
+ private val peopleNotificationIdentifier: PeopleNotificationIdentifier
+ ) : DataSource<PeopleHubModel> {
private var userChangeSubscription: Subscription? = null
private val dataListeners = mutableListOf<DataListener<PeopleHubModel>>()
private val peopleHubManagerForUser = SparseArray<PeopleHubManager>()
+ val context: Context = c.applicationContext
+ val iconFactory = ConversationIconFactory(context, launcherApps, packageManager,
+ IconDrawableFactory.newInstance(context), context.resources.getDimensionPixelSize(
+ R.dimen.notification_guts_conversation_icon_size))
private val notificationEntryListener = object : NotificationEntryListener {
override fun onEntryInflated(entry: NotificationEntry) = addVisibleEntry(entry)
@@ -102,18 +121,23 @@
override fun onEntryRemoved(
entry: NotificationEntry,
visibility: NotificationVisibility?,
- removedByUser: Boolean
- ) = removeVisibleEntry(entry)
+ removedByUser: Boolean,
+ reason: Int
+ ) = removeVisibleEntry(entry, reason)
}
- private fun removeVisibleEntry(entry: NotificationEntry) {
+ private fun removeVisibleEntry(entry: NotificationEntry, reason: Int) {
(extractor.extractPersonKey(entry.sbn) ?: entry.extractPersonKey())?.let { key ->
val userId = entry.sbn.user.identifier
bgExecutor.execute {
val parentId = userManager.getProfileParent(userId)?.id ?: userId
mainExecutor.execute {
- if (peopleHubManagerForUser[parentId]?.removeActivePerson(key) == true) {
- updateUi()
+ if (reason == REASON_SNOOZED) {
+ if (peopleHubManagerForUser[parentId]?.migrateActivePerson(key) == true) {
+ updateUi()
+ }
+ } else {
+ peopleHubManagerForUser[parentId]?.removeActivePerson(key)
}
}
}
@@ -121,7 +145,7 @@
}
private fun addVisibleEntry(entry: NotificationEntry) {
- (extractor.extractPerson(entry.sbn) ?: entry.extractPerson())?.let { personModel ->
+ entry.extractPerson()?.let { personModel ->
val userId = entry.sbn.user.identifier
bgExecutor.execute {
val parentId = userManager.getProfileParent(userId)?.id ?: userId
@@ -180,6 +204,34 @@
listener.onDataChanged(model)
}
}
+
+ private fun NotificationEntry.extractPerson(): PersonModel? {
+ if (!peopleNotificationIdentifier.isPeopleNotification(sbn, ranking)) {
+ return null
+ }
+ val clickRunnable = Runnable { notificationListener.unsnoozeNotification(key) }
+ val extras = sbn.notification.extras
+ val name = ranking.shortcutInfo?.shortLabel
+ ?: extras.getString(Notification.EXTRA_CONVERSATION_TITLE)
+ ?: extras.getString(Notification.EXTRA_TITLE)
+ ?: return null
+ val drawable = ranking.shortcutInfo?.getIcon(iconFactory, sbn, ranking)
+ ?: iconFactory.getConversationDrawable(extractAvatarFromRow(this), sbn.packageName,
+ sbn.uid, ranking.channel.isImportantConversation)
+
+ return PersonModel(key, name, drawable, clickRunnable, sbn.user.identifier)
+ }
+
+ private fun ShortcutInfo.getIcon(iconFactory: ConversationIconFactory,
+ sbn: StatusBarNotification,
+ ranking: NotificationListenerService.Ranking): Drawable? {
+ return iconFactory.getConversationDrawable(ranking.shortcutInfo, sbn.packageName, sbn.uid,
+ ranking.channel.isImportantConversation)
+ }
+
+ private fun NotificationEntry.extractPersonKey(): PersonKey? =
+ // TODO migrate to shortcut id when snoozing is conversation wide
+ if (peopleNotificationIdentifier.isPeopleNotification(sbn, ranking)) key else null
}
private fun NotificationLockscreenUserManager.registerListener(
@@ -201,7 +253,7 @@
// People that were once "active" and have been dismissed, and so can be displayed in the hub
private val inactivePeople = ArrayDeque<PersonModel>(MAX_STORED_INACTIVE_PEOPLE)
- fun removeActivePerson(key: PersonKey): Boolean {
+ fun migrateActivePerson(key: PersonKey): Boolean {
activePeople.remove(key)?.let { data ->
if (inactivePeople.size >= MAX_STORED_INACTIVE_PEOPLE) {
inactivePeople.removeLast()
@@ -212,6 +264,10 @@
return false
}
+ fun removeActivePerson(key: PersonKey) {
+ activePeople.remove(key)
+ }
+
fun addActivePerson(person: PersonModel): Boolean {
activePeople[person.key] = person
return inactivePeople.removeIf { it.key == person.key }
@@ -229,19 +285,6 @@
private fun ViewGroup.childrenWithId(id: Int): Sequence<View> = children.filter { it.id == id }
-private fun NotificationEntry.extractPerson(): PersonModel? {
- if (!isMessagingNotification()) {
- return null
- }
- val clickIntent = sbn.notification.contentIntent ?: return null
- val extras = sbn.notification.extras
- val name = extras.getString(Notification.EXTRA_CONVERSATION_TITLE)
- ?: extras.getString(Notification.EXTRA_TITLE)
- ?: return null
- val drawable = extractAvatarFromRow(this) ?: return null
- return PersonModel(key, name, drawable, clickIntent, sbn.user.identifier)
-}
-
fun extractAvatarFromRow(entry: NotificationEntry): Drawable? =
entry.row
?.childrenWithId(R.id.expanded)
@@ -261,8 +304,3 @@
}
?.firstOrNull()
-private fun NotificationEntry.extractPersonKey(): PersonKey? =
- if (isMessagingNotification()) key else null
-
-private fun NotificationEntry.isMessagingNotification() =
- sbn.notification.notificationStyle == Notification.MessagingStyle::class.java
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt
index a58c42b..62d3612 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt
@@ -102,30 +102,19 @@
@Singleton
class PeopleHubViewModelFactoryDataSourceImpl @Inject constructor(
private val activityStarter: ActivityStarter,
- private val dataSource: DataSource<@JvmSuppressWildcards PeopleHubModel>,
- private val settingChangeSource: DataSource<@JvmSuppressWildcards Boolean>
+ private val dataSource: DataSource<@JvmSuppressWildcards PeopleHubModel>
) : DataSource<PeopleHubViewModelFactory> {
override fun registerListener(listener: DataListener<PeopleHubViewModelFactory>): Subscription {
- var stripEnabled = false
var model: PeopleHubModel? = null
fun updateListener() {
// don't invoke listener until we've received our first model
model?.let { model ->
- val factory =
- if (stripEnabled) PeopleHubViewModelFactoryImpl(model, activityStarter)
- else EmptyViewModelFactory
+ val factory = PeopleHubViewModelFactoryImpl(model, activityStarter)
listener.onDataChanged(factory)
}
}
-
- val settingSub = settingChangeSource.registerListener(object : DataListener<Boolean> {
- override fun onDataChanged(data: Boolean) {
- stripEnabled = data
- updateListener()
- }
- })
val dataSub = dataSource.registerListener(object : DataListener<PeopleHubModel> {
override fun onDataChanged(data: PeopleHubModel) {
model = data
@@ -134,7 +123,6 @@
})
return object : Subscription {
override fun unsubscribe() {
- settingSub.unsubscribe()
dataSub.unsubscribe()
}
}
@@ -155,11 +143,7 @@
override fun createWithAssociatedClickView(view: View): PeopleHubViewModel {
val personViewModels = model.people.asSequence().map { personModel ->
val onClick = {
- activityStarter.startPendingIntentDismissingKeyguard(
- personModel.clickIntent,
- null,
- view
- )
+ personModel.clickRunnable.run()
}
PersonViewModel(personModel.name, personModel.avatar, onClick)
}
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 97755fc..6d3f126 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
@@ -18,7 +18,10 @@
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED;
+import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_EXPANDED;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC;
@@ -467,6 +470,14 @@
}
};
switch (inflationFlag) {
+ case FLAG_CONTENT_VIEW_CONTRACTED:
+ getPrivateLayout().performWhenContentInactive(VISIBLE_TYPE_CONTRACTED,
+ freeViewRunnable);
+ break;
+ case FLAG_CONTENT_VIEW_EXPANDED:
+ getPrivateLayout().performWhenContentInactive(VISIBLE_TYPE_EXPANDED,
+ freeViewRunnable);
+ break;
case FLAG_CONTENT_VIEW_HEADS_UP:
getPrivateLayout().performWhenContentInactive(VISIBLE_TYPE_HEADSUP,
freeViewRunnable);
@@ -807,7 +818,7 @@
// TODO: Move inflation logic out of this call
if (mIsChildInGroup != isChildInGroup) {
mIsChildInGroup = isChildInGroup;
- if (mIsLowPriority) {
+ if (!isRemoved() && mIsLowPriority) {
RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
params.setUseLowPriority(mIsLowPriority);
mRowContentBindStage.requestRebind(mEntry, null /* callback */);
@@ -1576,13 +1587,15 @@
// TODO: Move inflation logic out of this call and remove this method
if (mNeedsRedaction != needsRedaction) {
mNeedsRedaction = needsRedaction;
- RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
- if (needsRedaction) {
- params.requireContentViews(FLAG_CONTENT_VIEW_PUBLIC);
- } else {
- params.freeContentViews(FLAG_CONTENT_VIEW_PUBLIC);
+ if (!isRemoved()) {
+ RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
+ if (needsRedaction) {
+ params.requireContentViews(FLAG_CONTENT_VIEW_PUBLIC);
+ } else {
+ params.freeContentViews(FLAG_CONTENT_VIEW_PUBLIC);
+ }
+ mRowContentBindStage.requestRebind(mEntry, null /* callback */);
}
- mRowContentBindStage.requestRebind(mEntry, null /* callback */);
}
}
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 e3ca283..6dd4ff9 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
@@ -18,6 +18,7 @@
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED;
+import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_EXPANDED;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
import android.annotation.NonNull;
@@ -191,6 +192,18 @@
private void freeNotificationView(NotificationEntry entry, ExpandableNotificationRow row,
@InflationFlag int inflateFlag) {
switch (inflateFlag) {
+ case FLAG_CONTENT_VIEW_CONTRACTED:
+ if (row.getPrivateLayout().isContentViewInactive(VISIBLE_TYPE_CONTRACTED)) {
+ row.getPrivateLayout().setContractedChild(null);
+ mRemoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED);
+ }
+ break;
+ case FLAG_CONTENT_VIEW_EXPANDED:
+ if (row.getPrivateLayout().isContentViewInactive(VISIBLE_TYPE_EXPANDED)) {
+ row.getPrivateLayout().setExpandedChild(null);
+ mRemoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED);
+ }
+ break;
case FLAG_CONTENT_VIEW_HEADS_UP:
if (row.getPrivateLayout().isContentViewInactive(VISIBLE_TYPE_HEADSUP)) {
row.getPrivateLayout().setHeadsUpChild(null);
@@ -204,8 +217,6 @@
mRemoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC);
}
break;
- case FLAG_CONTENT_VIEW_CONTRACTED:
- case FLAG_CONTENT_VIEW_EXPANDED:
default:
break;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index d1b9a87..27fd1b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -71,7 +71,13 @@
public static final int VISIBLE_TYPE_EXPANDED = 1;
public static final int VISIBLE_TYPE_HEADSUP = 2;
private static final int VISIBLE_TYPE_SINGLELINE = 3;
- public static final int UNDEFINED = -1;
+ /**
+ * Used when there is no content on the view such as when we're a public layout but don't
+ * need to show.
+ */
+ private static final int VISIBLE_TYPE_NONE = -1;
+
+ private static final int UNDEFINED = -1;
private final Rect mClipBounds = new Rect();
@@ -99,7 +105,7 @@
private HybridGroupManager mHybridGroupManager;
private int mClipTopAmount;
private int mContentHeight;
- private int mVisibleType = VISIBLE_TYPE_CONTRACTED;
+ private int mVisibleType = VISIBLE_TYPE_NONE;
private boolean mAnimate;
private boolean mIsHeadsUp;
private boolean mLegacy;
@@ -141,7 +147,7 @@
/** The visible type at the start of a touch driven transformation */
private int mTransformationStartVisibleType;
/** The visible type at the start of an animation driven transformation */
- private int mAnimationStartVisibleType = UNDEFINED;
+ private int mAnimationStartVisibleType = VISIBLE_TYPE_NONE;
private boolean mUserExpanding;
private int mSingleLineWidthIndention;
private boolean mForceSelectNextLayout = true;
@@ -386,7 +392,7 @@
mContractedChild = null;
mContractedWrapper = null;
if (mTransformationStartVisibleType == VISIBLE_TYPE_CONTRACTED) {
- mTransformationStartVisibleType = UNDEFINED;
+ mTransformationStartVisibleType = VISIBLE_TYPE_NONE;
}
return;
}
@@ -434,7 +440,7 @@
mExpandedChild = null;
mExpandedWrapper = null;
if (mTransformationStartVisibleType == VISIBLE_TYPE_EXPANDED) {
- mTransformationStartVisibleType = UNDEFINED;
+ mTransformationStartVisibleType = VISIBLE_TYPE_NONE;
}
if (mVisibleType == VISIBLE_TYPE_EXPANDED) {
selectLayout(false /* animate */, true /* force */);
@@ -472,7 +478,7 @@
mHeadsUpChild = null;
mHeadsUpWrapper = null;
if (mTransformationStartVisibleType == VISIBLE_TYPE_HEADSUP) {
- mTransformationStartVisibleType = UNDEFINED;
+ mTransformationStartVisibleType = VISIBLE_TYPE_NONE;
}
if (mVisibleType == VISIBLE_TYPE_HEADSUP) {
selectLayout(false /* animate */, true /* force */);
@@ -597,7 +603,7 @@
}
// Size change of the expanded version
- if ((mVisibleType == VISIBLE_TYPE_EXPANDED) && mContentHeightAtAnimationStart >= 0
+ if ((mVisibleType == VISIBLE_TYPE_EXPANDED) && mContentHeightAtAnimationStart != UNDEFINED
&& mExpandedChild != null) {
return Math.min(mContentHeightAtAnimationStart, getViewHeight(VISIBLE_TYPE_EXPANDED));
}
@@ -607,10 +613,12 @@
hint = getViewHeight(VISIBLE_TYPE_HEADSUP);
} else if (mExpandedChild != null) {
hint = getViewHeight(VISIBLE_TYPE_EXPANDED);
- } else {
+ } else if (mContractedChild != null) {
hint = getViewHeight(VISIBLE_TYPE_CONTRACTED)
+ mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.notification_action_list_height);
+ } else {
+ hint = getMinHeight();
}
if (mExpandedChild != null && isVisibleOrTransitioning(VISIBLE_TYPE_EXPANDED)) {
@@ -646,7 +654,7 @@
if (mForceSelectNextLayout) {
forceUpdateVisibilities();
}
- if (mTransformationStartVisibleType != UNDEFINED
+ if (mTransformationStartVisibleType != VISIBLE_TYPE_NONE
&& mVisibleType != mTransformationStartVisibleType
&& getViewForVisibleType(mTransformationStartVisibleType) != null) {
final TransformableView shownView = getTransformableViewForVisibleType(mVisibleType);
@@ -823,7 +831,7 @@
fireExpandedVisibleListenerIfVisible();
// forceUpdateVisibilities cancels outstanding animations without updating the
// mAnimationStartVisibleType. Do so here instead.
- mAnimationStartVisibleType = UNDEFINED;
+ mAnimationStartVisibleType = VISIBLE_TYPE_NONE;
}
private void fireExpandedVisibleListenerIfVisible() {
@@ -898,7 +906,7 @@
fireExpandedVisibleListenerIfVisible();
// updateViewVisibilities cancels outstanding animations without updating the
// mAnimationStartVisibleType. Do so here instead.
- mAnimationStartVisibleType = UNDEFINED;
+ mAnimationStartVisibleType = VISIBLE_TYPE_NONE;
}
private void updateViewVisibility(int visibleType, int type, View view,
@@ -924,7 +932,7 @@
if (hiddenView != getTransformableViewForVisibleType(mVisibleType)) {
hiddenView.setVisible(false);
}
- mAnimationStartVisibleType = UNDEFINED;
+ mAnimationStartVisibleType = VISIBLE_TYPE_NONE;
}
});
fireExpandedVisibleListenerIfVisible();
@@ -1041,8 +1049,10 @@
&& (!mIsChildInGroup || isGroupExpanded()
|| !mContainingNotification.isExpanded(true /* allowOnKeyguard */)))) {
return VISIBLE_TYPE_CONTRACTED;
- } else {
+ } else if (!noExpandedChild) {
return VISIBLE_TYPE_EXPANDED;
+ } else {
+ return VISIBLE_TYPE_NONE;
}
}
}
@@ -1423,7 +1433,8 @@
if (mExpandedChild != null && mExpandedChild.getHeight() != 0) {
if ((!mIsHeadsUp && !mHeadsUpAnimatingAway)
|| mHeadsUpChild == null || !mContainingNotification.canShowHeadsUp()) {
- if (mExpandedChild.getHeight() <= mContractedChild.getHeight()) {
+ if (mContractedChild == null
+ || mExpandedChild.getHeight() <= mContractedChild.getHeight()) {
expandable = false;
}
} else if (mExpandedChild.getHeight() <= mHeadsUpChild.getHeight()) {
@@ -1514,7 +1525,7 @@
if (userExpanding) {
mTransformationStartVisibleType = mVisibleType;
} else {
- mTransformationStartVisibleType = UNDEFINED;
+ mTransformationStartVisibleType = VISIBLE_TYPE_NONE;
mVisibleType = calculateVisibleType();
updateViewVisibilities(mVisibleType);
updateBackgroundColor(false);
@@ -1558,6 +1569,7 @@
}
public void setContentHeightAnimating(boolean animating) {
+ //TODO: It's odd that this does nothing when animating is true
if (!animating) {
mContentHeightAtAnimationStart = UNDEFINED;
}
@@ -1565,7 +1577,7 @@
@VisibleForTesting
boolean isAnimatingVisibleType() {
- return mAnimationStartVisibleType != UNDEFINED;
+ return mAnimationStartVisibleType != VISIBLE_TYPE_NONE;
}
public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
@@ -1758,17 +1770,25 @@
}
public int getExpandHeight() {
- int viewType = VISIBLE_TYPE_EXPANDED;
- if (mExpandedChild == null) {
+ int viewType;
+ if (mExpandedChild != null) {
+ viewType = VISIBLE_TYPE_EXPANDED;
+ } else if (mContractedChild != null) {
viewType = VISIBLE_TYPE_CONTRACTED;
+ } else {
+ return getMinHeight();
}
return getViewHeight(viewType) + getExtraRemoteInputHeight(mExpandedRemoteInput);
}
public int getHeadsUpHeight(boolean forceNoHeader) {
- int viewType = VISIBLE_TYPE_HEADSUP;
- if (mHeadsUpChild == null) {
+ int viewType;
+ if (mHeadsUpChild != null) {
+ viewType = VISIBLE_TYPE_HEADSUP;
+ } else if (mContractedChild != null) {
viewType = VISIBLE_TYPE_CONTRACTED;
+ } else {
+ return getMinHeight();
}
// The headsUp remote input quickly switches to the expanded one, so lets also include that
// one
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index 60eda06..1088cdc3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -20,6 +20,7 @@
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED;
import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
import static android.provider.Settings.Secure.BUBBLE_IMPORTANT_CONVERSATIONS;
@@ -215,15 +216,7 @@
if (TextUtils.isEmpty(mConversationId)) {
throw new IllegalArgumentException("Does not have required information");
}
- // TODO: consider querying this earlier in the notification pipeline and passing it in
- LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery()
- .setPackage(mPackageName)
- .setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED)
- .setShortcutIds(Arrays.asList(mConversationId));
- List<ShortcutInfo> shortcuts = mLauncherApps.getShortcuts(query, mSbn.getUser());
- if (shortcuts != null && !shortcuts.isEmpty()) {
- mShortcutInfo = shortcuts.get(0);
- }
+ mShortcutInfo = entry.getRanking().getShortcutInfo();
mIsBubbleable = mEntry.getBubbleMetadata() != null
&& Settings.Global.getInt(mContext.getContentResolver(),
@@ -324,8 +317,9 @@
private void bindIcon() {
ImageView image = findViewById(R.id.conversation_icon);
if (mShortcutInfo != null) {
- image.setImageBitmap(mIconFactory.getConversationBitmap(
- mShortcutInfo, mPackageName, mAppUid));
+ image.setImageDrawable(mIconFactory.getConversationDrawable(
+ mShortcutInfo, mPackageName, mAppUid,
+ mNotificationChannel.isImportantConversation()));
} else {
if (mSbn.getNotification().extras.getBoolean(EXTRA_IS_GROUP_CONVERSATION, false)) {
// TODO: maybe use a generic group icon, or a composite of recent senders
@@ -479,6 +473,9 @@
mContext.getString(R.string.notification_conversation_mute));
mute.setImageResource(R.drawable.ic_notifications_silence);
}
+
+ // update icon in case importance has changed
+ bindIcon();
}
private void updateChannel() {
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 c01f6c4..d746822 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
@@ -390,7 +390,7 @@
};
}
ConversationIconFactory iconFactoryLoader = new ConversationIconFactory(mContext,
- launcherApps, pmUser, IconDrawableFactory.newInstance(mContext),
+ launcherApps, pmUser, IconDrawableFactory.newInstance(mContext, false),
mContext.getResources().getDimensionPixelSize(
R.dimen.notification_guts_conversation_icon_size));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 75ceb0f..d7c88e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -53,8 +53,7 @@
static final int NUMBER_OF_CHILDREN_WHEN_COLLAPSED = 2;
@VisibleForTesting
static final int NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED = 5;
- @VisibleForTesting
- static final int NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED = 8;
+ public static final int NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED = 8;
private static final AnimationProperties ALPHA_FADE_IN = new AnimationProperties() {
private AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
index b3561c2..8ee2f50 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.stack;
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
+
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
@@ -100,14 +102,11 @@
private boolean mInitialized = false;
private SectionHeaderView mGentleHeader;
- private boolean mGentleHeaderVisible;
@Nullable private View.OnClickListener mOnClearGentleNotifsClickListener;
private SectionHeaderView mAlertingHeader;
- private boolean mAlertingHeaderVisible;
private PeopleHubView mPeopleHubView;
- private boolean mPeopleHeaderVisible;
private boolean mPeopleHubVisible = false;
@Nullable private Subscription mPeopleHubSubscription;
@@ -231,88 +230,135 @@
return;
}
+ // The overall strategy here is to iterate over the current children of mParent, looking
+ // for where the sections headers are currently positioned, and where each section begins.
+ // Then, once we find the start of a new section, we track that position as the "target" for
+ // the section header, adjusted for the case where existing headers are in front of that
+ // target, but won't be once they are moved / removed after the pass has completed.
+
final boolean showHeaders = mStatusBarStateController.getState() != StatusBarState.KEYGUARD;
final boolean usingPeopleFiltering = mSectionsFeatureManager.isFilteringEnabled();
boolean peopleNotifsPresent = false;
+
+ int currentPeopleHeaderIdx = -1;
int peopleHeaderTarget = -1;
+ int currentAlertingHeaderIdx = -1;
int alertingHeaderTarget = -1;
+ int currentGentleHeaderIdx = -1;
int gentleHeaderTarget = -1;
- int viewCount = 0;
+ int lastNotifIndex = 0;
- if (showHeaders) {
- final int childCount = mParent.getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = mParent.getChildAt(i);
- if (child.getVisibility() == View.GONE
- || !(child instanceof ExpandableNotificationRow)) {
- continue;
- }
- ExpandableNotificationRow row = (ExpandableNotificationRow) child;
- switch (row.getEntry().getBucket()) {
- case BUCKET_PEOPLE:
- if (peopleHeaderTarget == -1) {
- peopleNotifsPresent = true;
- peopleHeaderTarget = viewCount;
- viewCount++;
- }
- break;
- case BUCKET_ALERTING:
- if (usingPeopleFiltering && alertingHeaderTarget == -1) {
- alertingHeaderTarget = viewCount;
- viewCount++;
- }
- break;
- case BUCKET_SILENT:
- if (gentleHeaderTarget == -1) {
- gentleHeaderTarget = viewCount;
- viewCount++;
- }
- break;
- }
- viewCount++;
+ final int childCount = mParent.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View child = mParent.getChildAt(i);
+
+ // Track the existing positions of the headers
+ if (child == mPeopleHubView) {
+ currentPeopleHeaderIdx = i;
+ continue;
}
- if (usingPeopleFiltering && mPeopleHubVisible && peopleHeaderTarget == -1) {
- // Insert the people header even if there are no people visible, in order to show
- // the hub. Put it directly above the next header.
- if (alertingHeaderTarget != -1) {
- peopleHeaderTarget = alertingHeaderTarget;
- alertingHeaderTarget++;
- gentleHeaderTarget++;
- } else if (gentleHeaderTarget != -1) {
- peopleHeaderTarget = gentleHeaderTarget;
- gentleHeaderTarget++;
- } else {
- // Put it at the end of the list.
- peopleHeaderTarget = viewCount;
- }
+ if (child == mAlertingHeader) {
+ currentAlertingHeaderIdx = i;
+ continue;
+ }
+ if (child == mGentleHeader) {
+ currentGentleHeaderIdx = i;
+ continue;
+ }
+
+ if (!(child instanceof ExpandableNotificationRow)) {
+ continue;
+ }
+ lastNotifIndex = i;
+ ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ // Once we enter a new section, calculate the target position for the header.
+ switch (row.getEntry().getBucket()) {
+ case BUCKET_HEADS_UP:
+ break;
+ case BUCKET_PEOPLE:
+ peopleNotifsPresent = true;
+ if (showHeaders && peopleHeaderTarget == -1) {
+ peopleHeaderTarget = i;
+ // Offset the target if there are other headers before this that will be
+ // moved.
+ if (currentPeopleHeaderIdx != -1) {
+ peopleHeaderTarget--;
+ }
+ if (currentAlertingHeaderIdx != -1) {
+ peopleHeaderTarget--;
+ }
+ if (currentGentleHeaderIdx != -1) {
+ peopleHeaderTarget--;
+ }
+ }
+ break;
+ case BUCKET_ALERTING:
+ if (showHeaders && usingPeopleFiltering && alertingHeaderTarget == -1) {
+ alertingHeaderTarget = i;
+ // Offset the target if there are other headers before this that will be
+ // moved.
+ if (currentAlertingHeaderIdx != -1) {
+ alertingHeaderTarget--;
+ }
+ if (currentGentleHeaderIdx != -1) {
+ alertingHeaderTarget--;
+ }
+ }
+ break;
+ case BUCKET_SILENT:
+ if (showHeaders && gentleHeaderTarget == -1) {
+ gentleHeaderTarget = i;
+ // Offset the target if there are other headers before this that will be
+ // moved.
+ if (currentGentleHeaderIdx != -1) {
+ gentleHeaderTarget--;
+ }
+ }
+ break;
+ default:
+ throw new IllegalStateException("Cannot find section bucket for view");
+ }
+ }
+ if (showHeaders && usingPeopleFiltering && mPeopleHubVisible && peopleHeaderTarget == -1) {
+ // Insert the people header even if there are no people visible, in order to show
+ // the hub. Put it directly above the next header.
+ if (alertingHeaderTarget != -1) {
+ peopleHeaderTarget = alertingHeaderTarget;
+ } else if (gentleHeaderTarget != -1) {
+ peopleHeaderTarget = gentleHeaderTarget;
+ } else {
+ // Put it at the end of the list.
+ peopleHeaderTarget = lastNotifIndex;
}
}
- // Allow swiping the people header if the section is empty
- mPeopleHubView.setCanSwipe(mPeopleHubVisible && !peopleNotifsPresent);
+ // Add headers in reverse order to preserve indices
+ adjustHeaderVisibilityAndPosition(
+ gentleHeaderTarget, mGentleHeader, currentGentleHeaderIdx);
+ adjustHeaderVisibilityAndPosition(
+ alertingHeaderTarget, mAlertingHeader, currentAlertingHeaderIdx);
+ adjustHeaderVisibilityAndPosition(
+ peopleHeaderTarget, mPeopleHubView, currentPeopleHeaderIdx);
- mPeopleHeaderVisible = adjustHeaderVisibilityAndPosition(
- peopleHeaderTarget, mPeopleHubView, mPeopleHeaderVisible);
- mAlertingHeaderVisible = adjustHeaderVisibilityAndPosition(
- alertingHeaderTarget, mAlertingHeader, mAlertingHeaderVisible);
- mGentleHeaderVisible = adjustHeaderVisibilityAndPosition(
- gentleHeaderTarget, mGentleHeader, mGentleHeaderVisible);
+ // Update headers to reflect state of section contents
+ mGentleHeader.setAreThereDismissableGentleNotifs(
+ mParent.hasActiveClearableNotifications(ROWS_GENTLE));
+ mPeopleHubView.setCanSwipe(showHeaders && mPeopleHubVisible && !peopleNotifsPresent);
+ if (peopleHeaderTarget != currentPeopleHeaderIdx) {
+ mPeopleHubView.resetTranslation();
+ }
}
- private boolean adjustHeaderVisibilityAndPosition(
- int targetIndex, StackScrollerDecorView header, boolean isCurrentlyVisible) {
- if (targetIndex == -1) {
- if (isCurrentlyVisible) {
+ private void adjustHeaderVisibilityAndPosition(
+ int targetPosition, StackScrollerDecorView header, int currentPosition) {
+ if (targetPosition == -1) {
+ if (currentPosition != -1) {
mParent.removeView(header);
}
- return false;
} else {
- if (header instanceof SwipeableView) {
- ((SwipeableView) header).resetTranslation();
- }
- if (!isCurrentlyVisible) {
+ if (currentPosition == -1) {
// If the header is animating away, it will still have a parent, so detach it first
// TODO: We should really cancel the active animations here. This will happen
// automatically when the view's intro animation starts, but it's a fragile link.
@@ -321,11 +367,10 @@
header.setTransientContainer(null);
}
header.setContentVisible(true);
- mParent.addView(header, targetIndex);
- } else if (mParent.indexOfChild(header) != targetIndex) {
- mParent.changeViewPosition(header, targetIndex);
+ mParent.addView(header, targetPosition);
+ } else {
+ mParent.changeViewPosition(header, targetPosition);
}
- return true;
}
}
@@ -400,10 +445,20 @@
@VisibleForTesting
- SectionHeaderView getGentleHeaderView() {
+ ExpandableView getGentleHeaderView() {
return mGentleHeader;
}
+ @VisibleForTesting
+ ExpandableView getAlertingHeaderView() {
+ return mAlertingHeader;
+ }
+
+ @VisibleForTesting
+ ExpandableView getPeopleHeaderView() {
+ return mPeopleHubView;
+ }
+
private final ConfigurationListener mConfigurationListener = new ConfigurationListener() {
@Override
public void onLocaleListChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
index 971f045..c05119d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
@@ -19,56 +19,64 @@
import android.content.Context;
import android.os.Handler;
import android.os.RemoteException;
+import android.util.ArraySet;
import android.util.Log;
import android.view.IWindowManager;
import android.view.MotionEvent;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule;
+import com.android.systemui.statusbar.AutoHideUiElement;
-/** A controller to control all auto-hide things. */
+import java.util.Set;
+
+import javax.inject.Inject;
+
+/** A controller to control all auto-hide things. Also see {@link AutoHideUiElement}. */
public class AutoHideController {
private static final String TAG = "AutoHideController";
+ private static final long AUTO_HIDE_TIMEOUT_MS = 2250;
private final IWindowManager mWindowManagerService;
-
private final Handler mHandler;
- private final NotificationRemoteInputManager mRemoteInputManager;
- private StatusBar mStatusBar;
- private NavigationBarFragment mNavigationBar;
+ private final Set<AutoHideUiElement> mElements;
private int mDisplayId;
private boolean mAutoHideSuspended;
- private static final long AUTO_HIDE_TIMEOUT_MS = 2250;
-
private final Runnable mAutoHide = () -> {
if (isAnyTransientBarShown()) {
hideTransientBars();
}
};
- /**
- * Injected constructor. See {@link StatusBarPhoneModule}.
- */
+ @Inject
public AutoHideController(Context context, @Main Handler handler,
- NotificationRemoteInputManager notificationRemoteInputManager,
IWindowManager iWindowManager) {
mHandler = handler;
- mRemoteInputManager = notificationRemoteInputManager;
mWindowManagerService = iWindowManager;
+ mElements = new ArraySet<>();
mDisplayId = context.getDisplayId();
}
- void setStatusBar(StatusBar statusBar) {
- mStatusBar = statusBar;
+ /**
+ * Adds an {@link AutoHideUiElement} whose behavior should be controlled by the
+ * {@link AutoHideController}.
+ */
+ public void addAutoHideUiElement(AutoHideUiElement element) {
+ if (element != null) {
+ mElements.add(element);
+ }
}
- void setNavigationBar(NavigationBarFragment navigationBar) {
- mNavigationBar = navigationBar;
+ /**
+ * Remove an {@link AutoHideUiElement} that was previously added.
+ */
+ public void removeAutoHideUiElement(AutoHideUiElement element) {
+ if (element != null) {
+ mElements.remove(element);
+ }
}
private void hideTransientBars() {
@@ -77,11 +85,9 @@
} catch (RemoteException ex) {
Log.w(TAG, "Cannot get WindowManager");
}
- if (mStatusBar != null) {
- mStatusBar.clearTransient();
- }
- if (mNavigationBar != null) {
- mNavigationBar.clearTransient();
+
+ for (AutoHideUiElement element : mElements) {
+ element.hide();
}
}
@@ -104,7 +110,8 @@
mAutoHideSuspended = isAnyTransientBarShown();
}
- void touchAutoHide() {
+ /** Schedules or cancels auto hide behavior based on current system bar state. */
+ public void touchAutoHide() {
// update transient bar auto hide
if (isAnyTransientBarShown()) {
scheduleAutoHide();
@@ -114,13 +121,15 @@
}
private Runnable getCheckBarModesRunnable() {
- if (mStatusBar != null) {
- return () -> mStatusBar.checkBarModes();
- } else if (mNavigationBar != null) {
- return () -> mNavigationBar.checkNavBarModes();
- } else {
+ if (mElements.isEmpty()) {
return null;
}
+
+ return () -> {
+ for (AutoHideUiElement element : mElements) {
+ element.synchronizeState();
+ }
+ };
}
private void cancelAutoHide() {
@@ -134,14 +143,15 @@
}
void checkUserAutoHide(MotionEvent event) {
- boolean shouldAutoHide = isAnyTransientBarShown()
+ boolean shouldHide = isAnyTransientBarShown()
&& event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar.
&& event.getX() == 0 && event.getY() == 0;
- if (mStatusBar != null) {
- // a touch outside both bars
- shouldAutoHide &= !mRemoteInputManager.getController().isRemoteInputActive();
+
+ for (AutoHideUiElement element : mElements) {
+ shouldHide &= element.shouldHideOnTouch();
}
- if (shouldAutoHide) {
+
+ if (shouldHide) {
userAutoHide();
}
}
@@ -152,7 +162,11 @@
}
private boolean isAnyTransientBarShown() {
- return (mStatusBar != null && mStatusBar.isTransientShown())
- || mNavigationBar != null && mNavigationBar.isTransientShown();
+ for (AutoHideUiElement element : mElements) {
+ if (element.isVisible()) {
+ return true;
+ }
+ }
+ return false;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
index 5703f06..8e192c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
@@ -172,7 +172,7 @@
@Override
public void onEntryRemoved(@Nullable NotificationEntry entry,
- NotificationVisibility visibility, boolean removedByUser) {
+ NotificationVisibility visibility, boolean removedByUser, int reason) {
updateLightsOutView();
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index fd8c71b..02cf8cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -105,8 +105,10 @@
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.statusbar.AutoHideUiElement;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.ContextualButton.ContextButtonListener;
@@ -166,6 +168,7 @@
private int mDisabledFlags2;
private final Lazy<StatusBar> mStatusBarLazy;
private final ShadeController mShadeController;
+ private final NotificationRemoteInputManager mNotificationRemoteInputManager;
private Recents mRecents;
private StatusBar mStatusBar;
private final Divider mDivider;
@@ -291,6 +294,7 @@
CommandQueue commandQueue, Divider divider,
Optional<Recents> recentsOptional, Lazy<StatusBar> statusBarLazy,
ShadeController shadeController,
+ NotificationRemoteInputManager notificationRemoteInputManager,
@Main Handler mainHandler) {
mAccessibilityManagerWrapper = accessibilityManagerWrapper;
mDeviceProvisionedController = deviceProvisionedController;
@@ -300,6 +304,7 @@
mSysUiFlagsContainer = sysUiFlagsContainer;
mStatusBarLazy = statusBarLazy;
mShadeController = shadeController;
+ mNotificationRemoteInputManager = notificationRemoteInputManager;
mAssistantAvailable = mAssistManager.getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
mOverviewProxyService = overviewProxyService;
mNavigationModeController = navigationModeController;
@@ -614,7 +619,7 @@
clearTransient();
}
- void clearTransient() {
+ private void clearTransient() {
if (mTransientShown) {
mTransientShown = false;
handleTransientChanged();
@@ -1048,10 +1053,30 @@
/** Sets {@link AutoHideController} to the navigation bar. */
public void setAutoHideController(AutoHideController autoHideController) {
mAutoHideController = autoHideController;
- mAutoHideController.setNavigationBar(this);
+ mAutoHideController.addAutoHideUiElement(new AutoHideUiElement() {
+ @Override
+ public void synchronizeState() {
+ checkNavBarModes();
+ }
+
+ @Override
+ public boolean shouldHideOnTouch() {
+ return !mNotificationRemoteInputManager.getController().isRemoteInputActive();
+ }
+
+ @Override
+ public boolean isVisible() {
+ return isTransientShown();
+ }
+
+ @Override
+ public void hide() {
+ clearTransient();
+ }
+ });
}
- boolean isTransientShown() {
+ private boolean isTransientShown() {
return mTransientShown;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index d709e02..8c31a37 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -196,7 +196,8 @@
public void onEntryRemoved(
@Nullable NotificationEntry entry,
NotificationVisibility visibility,
- boolean removedByUser) {
+ boolean removedByUser,
+ int reason) {
// Removes any alerts pending on this entry. Note that this will not stop any inflation
// tasks started by a transfer, so this should only be used as clean-up for when
// inflation is stopped and the pending alert no longer needs to happen.
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 8729e04..f38d416 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -44,8 +44,9 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.NotificationShadeWindowBlurController;
+import com.android.systemui.statusbar.PulseExpansionHandler;
+import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -91,6 +92,7 @@
private boolean mExpandAnimationRunning;
private NotificationStackScrollLayout mStackScrollLayout;
private PhoneStatusBarView mStatusBarView;
+ private PhoneStatusBarTransitions mBarTransitions;
private StatusBar mService;
private DragDownHelper mDragDownHelper;
private boolean mDoubleTapEnabled;
@@ -98,6 +100,7 @@
private boolean mExpandingBelowNotch;
private final DockManager mDockManager;
private final NotificationPanelViewController mNotificationPanelViewController;
+ private final SuperStatusBarViewFactory mStatusBarViewFactory;
// Used for determining view / touch intersection
private int[] mTempLocation = new int[2];
@@ -124,8 +127,9 @@
ShadeController shadeController,
DockManager dockManager,
@Nullable NotificationShadeWindowBlurController blurController,
- NotificationShadeWindowView statusBarWindowView,
- NotificationPanelViewController notificationPanelViewController) {
+ NotificationShadeWindowView notificationShadeWindowView,
+ NotificationPanelViewController notificationPanelViewController,
+ SuperStatusBarViewFactory statusBarViewFactory) {
mInjectionInflationController = injectionInflationController;
mCoordinator = coordinator;
mPulseExpansionHandler = pulseExpansionHandler;
@@ -141,11 +145,12 @@
mDozeLog = dozeLog;
mDozeParameters = dozeParameters;
mCommandQueue = commandQueue;
- mView = statusBarWindowView;
+ mView = notificationShadeWindowView;
mShadeController = shadeController;
mDockManager = dockManager;
mNotificationPanelViewController = notificationPanelViewController;
mBlurController = blurController;
+ mStatusBarViewFactory = statusBarViewFactory;
// This view is not part of the newly inflated expanded status bar.
mBrightnessMirror = mView.findViewById(R.id.brightness_mirror);
@@ -440,8 +445,18 @@
}
}
+ public PhoneStatusBarTransitions getBarTransitions() {
+ return mBarTransitions;
+ }
+
public void setStatusBarView(PhoneStatusBarView statusBarView) {
mStatusBarView = statusBarView;
+ if (statusBarView != null && mStatusBarViewFactory != null) {
+ mBarTransitions = new PhoneStatusBarTransitions(
+ statusBarView,
+ mStatusBarViewFactory.getStatusBarWindowView()
+ .findViewById(R.id.status_bar_container));
+ }
}
public void setService(StatusBar statusBar) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 1ab36c5..14af466 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -16,15 +16,18 @@
package com.android.systemui.statusbar.phone;
-import android.app.ActivityManager;
+import android.annotation.Nullable;
import android.app.ActivityTaskManager;
import android.app.AlarmManager;
import android.app.AlarmManager.AlarmClockInfo;
+import android.app.IActivityManager;
import android.app.SynchronousUserSwitchObserver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.content.res.Resources;
import android.media.AudioManager;
import android.os.Handler;
import android.os.RemoteException;
@@ -33,13 +36,13 @@
import android.provider.Settings.Global;
import android.service.notification.ZenModeConfig;
import android.telecom.TelecomManager;
-import android.telephony.TelephonyManager;
import android.text.format.DateFormat;
import android.util.Log;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.DisplayId;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.qs.tiles.DndTile;
import com.android.systemui.qs.tiles.RotationLockTile;
@@ -61,10 +64,13 @@
import com.android.systemui.statusbar.policy.SensorPrivacyController;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.util.time.DateFormatUtil;
import java.util.Locale;
import java.util.concurrent.Executor;
+import javax.inject.Inject;
+
/**
* This class contains all of the policy about which icons are installed in the status bar at boot
* time. It goes through the normal API for icons, even though it probably strictly doesn't need to.
@@ -82,7 +88,7 @@
private static final String TAG = "PhoneStatusBarPolicy";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- public static final int LOCATION_STATUS_ICON_ID =
+ static final int LOCATION_STATUS_ICON_ID =
com.android.internal.R.drawable.perm_group_location;
private final String mSlotCast;
@@ -97,20 +103,26 @@
private final String mSlotHeadset;
private final String mSlotDataSaver;
private final String mSlotLocation;
- private final String mSlotMicrophone;
- private final String mSlotCamera;
private final String mSlotSensorsOff;
private final String mSlotScreenRecord;
+ private final int mDisplayId;
+ private final SharedPreferences mSharedPreferences;
+ private final DateFormatUtil mDateFormatUtil;
+ private final TelecomManager mTelecomManager;
+ private final AudioManager mAudioManager;
- private final Context mContext;
private final Handler mHandler = new Handler();
private final CastController mCast;
private final HotspotController mHotspot;
private final NextAlarmController mNextAlarmController;
private final AlarmManager mAlarmManager;
private final UserInfoController mUserInfoController;
+ private final IActivityManager mIActivityManager;
private final UserManager mUserManager;
private final StatusBarIconController mIconController;
+ private final CommandQueue mCommandQueue;
+ private final BroadcastDispatcher mBroadcastDispatcher;
+ private final Resources mResources;
private final RotationLockController mRotationLockController;
private final DataSaverController mDataSaver;
private final ZenModeController mZenController;
@@ -121,10 +133,6 @@
private final SensorPrivacyController mSensorPrivacyController;
private final RecordingController mRecordingController;
- // Assume it's all good unless we hear otherwise. We don't always seem
- // to get broadcasts that it *is* there.
- int mSimState = TelephonyManager.SIM_STATE_READY;
-
private boolean mZenVisible;
private boolean mVolumeVisible;
private boolean mCurrentUserSetup;
@@ -134,47 +142,70 @@
private BluetoothController mBluetooth;
private AlarmManager.AlarmClockInfo mNextAlarm;
- public PhoneStatusBarPolicy(Context context, StatusBarIconController iconController,
+ @Inject
+ public PhoneStatusBarPolicy(StatusBarIconController iconController,
CommandQueue commandQueue, BroadcastDispatcher broadcastDispatcher,
- @UiBackground Executor uiBgExecutor) {
- mContext = context;
+ @UiBackground Executor uiBgExecutor, @Main Resources resources,
+ CastController castController, HotspotController hotspotController,
+ BluetoothController bluetoothController, NextAlarmController nextAlarmController,
+ UserInfoController userInfoController, RotationLockController rotationLockController,
+ DataSaverController dataSaverController, ZenModeController zenModeController,
+ DeviceProvisionedController deviceProvisionedController,
+ KeyguardStateController keyguardStateController,
+ LocationController locationController,
+ SensorPrivacyController sensorPrivacyController, IActivityManager iActivityManager,
+ AlarmManager alarmManager, UserManager userManager, AudioManager audioManager,
+ RecordingController recordingController,
+ @Nullable TelecomManager telecomManager, @DisplayId int displayId,
+ @Main SharedPreferences sharedPreferences, DateFormatUtil dateFormatUtil) {
mIconController = iconController;
- mCast = Dependency.get(CastController.class);
- mHotspot = Dependency.get(HotspotController.class);
- mBluetooth = Dependency.get(BluetoothController.class);
- mNextAlarmController = Dependency.get(NextAlarmController.class);
- mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
- mUserInfoController = Dependency.get(UserInfoController.class);
- mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- mRotationLockController = Dependency.get(RotationLockController.class);
- mDataSaver = Dependency.get(DataSaverController.class);
- mZenController = Dependency.get(ZenModeController.class);
- mProvisionedController = Dependency.get(DeviceProvisionedController.class);
- mKeyguardStateController = Dependency.get(KeyguardStateController.class);
- mLocationController = Dependency.get(LocationController.class);
- mSensorPrivacyController = Dependency.get(SensorPrivacyController.class);
- mRecordingController = Dependency.get(RecordingController.class);
+ mCommandQueue = commandQueue;
+ mBroadcastDispatcher = broadcastDispatcher;
+ mResources = resources;
+ mCast = castController;
+ mHotspot = hotspotController;
+ mBluetooth = bluetoothController;
+ mNextAlarmController = nextAlarmController;
+ mAlarmManager = alarmManager;
+ mUserInfoController = userInfoController;
+ mIActivityManager = iActivityManager;
+ mUserManager = userManager;
+ mRotationLockController = rotationLockController;
+ mDataSaver = dataSaverController;
+ mZenController = zenModeController;
+ mProvisionedController = deviceProvisionedController;
+ mKeyguardStateController = keyguardStateController;
+ mLocationController = locationController;
+ mSensorPrivacyController = sensorPrivacyController;
+ mRecordingController = recordingController;
mUiBgExecutor = uiBgExecutor;
+ mAudioManager = audioManager;
+ mTelecomManager = telecomManager;
- mSlotCast = context.getString(com.android.internal.R.string.status_bar_cast);
- mSlotHotspot = context.getString(com.android.internal.R.string.status_bar_hotspot);
- mSlotBluetooth = context.getString(com.android.internal.R.string.status_bar_bluetooth);
- mSlotTty = context.getString(com.android.internal.R.string.status_bar_tty);
- mSlotZen = context.getString(com.android.internal.R.string.status_bar_zen);
- mSlotVolume = context.getString(com.android.internal.R.string.status_bar_volume);
- mSlotAlarmClock = context.getString(com.android.internal.R.string.status_bar_alarm_clock);
- mSlotManagedProfile = context.getString(
+ mSlotCast = resources.getString(com.android.internal.R.string.status_bar_cast);
+ mSlotHotspot = resources.getString(com.android.internal.R.string.status_bar_hotspot);
+ mSlotBluetooth = resources.getString(com.android.internal.R.string.status_bar_bluetooth);
+ mSlotTty = resources.getString(com.android.internal.R.string.status_bar_tty);
+ mSlotZen = resources.getString(com.android.internal.R.string.status_bar_zen);
+ mSlotVolume = resources.getString(com.android.internal.R.string.status_bar_volume);
+ mSlotAlarmClock = resources.getString(com.android.internal.R.string.status_bar_alarm_clock);
+ mSlotManagedProfile = resources.getString(
com.android.internal.R.string.status_bar_managed_profile);
- mSlotRotate = context.getString(com.android.internal.R.string.status_bar_rotate);
- mSlotHeadset = context.getString(com.android.internal.R.string.status_bar_headset);
- mSlotDataSaver = context.getString(com.android.internal.R.string.status_bar_data_saver);
- mSlotLocation = context.getString(com.android.internal.R.string.status_bar_location);
- mSlotMicrophone = context.getString(com.android.internal.R.string.status_bar_microphone);
- mSlotCamera = context.getString(com.android.internal.R.string.status_bar_camera);
- mSlotSensorsOff = context.getString(com.android.internal.R.string.status_bar_sensors_off);
- mSlotScreenRecord = context.getString(
+ mSlotRotate = resources.getString(com.android.internal.R.string.status_bar_rotate);
+ mSlotHeadset = resources.getString(com.android.internal.R.string.status_bar_headset);
+ mSlotDataSaver = resources.getString(com.android.internal.R.string.status_bar_data_saver);
+ mSlotLocation = resources.getString(com.android.internal.R.string.status_bar_location);
+ mSlotSensorsOff = resources.getString(com.android.internal.R.string.status_bar_sensors_off);
+ mSlotScreenRecord = resources.getString(
com.android.internal.R.string.status_bar_screen_record);
+ mDisplayId = displayId;
+ mSharedPreferences = sharedPreferences;
+ mDateFormatUtil = dateFormatUtil;
+ }
+
+ /** Initialize the object after construction. */
+ public void init() {
// listen for broadcasts
IntentFilter filter = new IntentFilter();
filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
@@ -185,11 +216,11 @@
filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
- broadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter, mHandler);
+ mBroadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter, mHandler);
// listen for user / profile change.
try {
- ActivityManager.getService().registerUserSwitchObserver(mUserSwitchListener, TAG);
+ mIActivityManager.registerUserSwitchObserver(mUserSwitchListener, TAG);
} catch (RemoteException e) {
// Ignore
}
@@ -219,26 +250,26 @@
// hotspot
mIconController.setIcon(mSlotHotspot, R.drawable.stat_sys_hotspot,
- mContext.getString(R.string.accessibility_status_bar_hotspot));
+ mResources.getString(R.string.accessibility_status_bar_hotspot));
mIconController.setIconVisibility(mSlotHotspot, mHotspot.isHotspotEnabled());
// managed profile
mIconController.setIcon(mSlotManagedProfile, R.drawable.stat_sys_managed_profile_status,
- mContext.getString(R.string.accessibility_managed_profile));
+ mResources.getString(R.string.accessibility_managed_profile));
mIconController.setIconVisibility(mSlotManagedProfile, mManagedProfileIconVisible);
// data saver
mIconController.setIcon(mSlotDataSaver, R.drawable.stat_sys_data_saver,
- context.getString(R.string.accessibility_data_saver_on));
+ mResources.getString(R.string.accessibility_data_saver_on));
mIconController.setIconVisibility(mSlotDataSaver, false);
mIconController.setIcon(mSlotLocation, LOCATION_STATUS_ICON_ID,
- mContext.getString(R.string.accessibility_location_active));
+ mResources.getString(R.string.accessibility_location_active));
mIconController.setIconVisibility(mSlotLocation, false);
// sensors off
mIconController.setIcon(mSlotSensorsOff, R.drawable.stat_sys_sensors_off,
- mContext.getString(R.string.accessibility_sensors_off_active));
+ mResources.getString(R.string.accessibility_sensors_off_active));
mIconController.setIconVisibility(mSlotSensorsOff,
mSensorPrivacyController.isSensorPrivacyEnabled());
@@ -259,7 +290,7 @@
mLocationController.addCallback(this);
mRecordingController.addCallback(this);
- commandQueue.addCallback(this);
+ mCommandQueue.addCallback(this);
}
@Override
@@ -284,51 +315,17 @@
private String buildAlarmContentDescription() {
if (mNextAlarm == null) {
- return mContext.getString(R.string.status_bar_alarm);
+ return mResources.getString(R.string.status_bar_alarm);
}
- return formatNextAlarm(mNextAlarm, mContext);
- }
- private static String formatNextAlarm(AlarmManager.AlarmClockInfo info, Context context) {
- if (info == null) {
- return "";
- }
- String skeleton = DateFormat.is24HourFormat(
- context, ActivityManager.getCurrentUser()) ? "EHm" : "Ehma";
+ String skeleton = mDateFormatUtil.is24HourFormat() ? "EHm" : "Ehma";
String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
- String dateString = DateFormat.format(pattern, info.getTriggerTime()).toString();
+ String dateString = DateFormat.format(pattern, mNextAlarm.getTriggerTime()).toString();
- return context.getString(R.string.accessibility_quick_settings_alarm, dateString);
- }
-
- private final void updateSimState(Intent intent) {
- String stateExtra = intent.getStringExtra(Intent.EXTRA_SIM_STATE);
- if (Intent.SIM_STATE_ABSENT.equals(stateExtra)) {
- mSimState = TelephonyManager.SIM_STATE_READY;
- } else if (Intent.SIM_STATE_CARD_IO_ERROR.equals(stateExtra)) {
- mSimState = TelephonyManager.SIM_STATE_CARD_IO_ERROR;
- } else if (Intent.SIM_STATE_CARD_RESTRICTED.equals(stateExtra)) {
- mSimState = TelephonyManager.SIM_STATE_CARD_RESTRICTED;
- } else if (Intent.SIM_STATE_READY.equals(stateExtra)) {
- mSimState = TelephonyManager.SIM_STATE_READY;
- } else if (Intent.SIM_STATE_LOCKED.equals(stateExtra)) {
- final String lockedReason =
- intent.getStringExtra(Intent.EXTRA_SIM_LOCKED_REASON);
- if (Intent.SIM_LOCKED_ON_PIN.equals(lockedReason)) {
- mSimState = TelephonyManager.SIM_STATE_PIN_REQUIRED;
- } else if (Intent.SIM_LOCKED_ON_PUK.equals(lockedReason)) {
- mSimState = TelephonyManager.SIM_STATE_PUK_REQUIRED;
- } else {
- mSimState = TelephonyManager.SIM_STATE_NETWORK_LOCKED;
- }
- } else {
- mSimState = TelephonyManager.SIM_STATE_UNKNOWN;
- }
+ return mResources.getString(R.string.accessibility_quick_settings_alarm, dateString);
}
private final void updateVolumeZen() {
- AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
-
boolean zenVisible = false;
int zenIconId = 0;
String zenDescription = null;
@@ -338,29 +335,29 @@
String volumeDescription = null;
int zen = mZenController.getZen();
- if (DndTile.isVisible(mContext) || DndTile.isCombinedIcon(mContext)) {
+ if (DndTile.isVisible(mSharedPreferences) || DndTile.isCombinedIcon(mSharedPreferences)) {
zenVisible = zen != Global.ZEN_MODE_OFF;
zenIconId = R.drawable.stat_sys_dnd;
- zenDescription = mContext.getString(R.string.quick_settings_dnd_label);
+ zenDescription = mResources.getString(R.string.quick_settings_dnd_label);
} else if (zen == Global.ZEN_MODE_NO_INTERRUPTIONS) {
zenVisible = true;
zenIconId = R.drawable.stat_sys_dnd;
- zenDescription = mContext.getString(R.string.interruption_level_none);
+ zenDescription = mResources.getString(R.string.interruption_level_none);
} else if (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
zenVisible = true;
zenIconId = R.drawable.stat_sys_dnd;
- zenDescription = mContext.getString(R.string.interruption_level_priority);
+ zenDescription = mResources.getString(R.string.interruption_level_priority);
}
if (!ZenModeConfig.isZenOverridingRinger(zen, mZenController.getConsolidatedPolicy())) {
- if (audioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE) {
+ if (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE) {
volumeVisible = true;
volumeIconId = R.drawable.stat_sys_ringer_vibrate;
- volumeDescription = mContext.getString(R.string.accessibility_ringer_vibrate);
- } else if (audioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT) {
+ volumeDescription = mResources.getString(R.string.accessibility_ringer_vibrate);
+ } else if (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT) {
volumeVisible = true;
volumeIconId = R.drawable.stat_sys_ringer_silent;
- volumeDescription = mContext.getString(R.string.accessibility_ringer_silent);
+ volumeDescription = mResources.getString(R.string.accessibility_ringer_silent);
}
}
@@ -395,13 +392,13 @@
private final void updateBluetooth() {
int iconId = R.drawable.stat_sys_data_bluetooth_connected;
String contentDescription =
- mContext.getString(R.string.accessibility_quick_settings_bluetooth_on);
+ mResources.getString(R.string.accessibility_quick_settings_bluetooth_on);
boolean bluetoothVisible = false;
if (mBluetooth != null) {
if (mBluetooth.isBluetoothConnected()
&& (mBluetooth.isBluetoothAudioActive()
|| !mBluetooth.isBluetoothAudioProfileOnly())) {
- contentDescription = mContext.getString(
+ contentDescription = mResources.getString(
R.string.accessibility_bluetooth_connected);
bluetoothVisible = mBluetooth.isBluetoothEnabled();
}
@@ -412,12 +409,10 @@
}
private final void updateTTY() {
- TelecomManager telecomManager =
- (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
- if (telecomManager == null) {
+ if (mTelecomManager == null) {
updateTTY(TelecomManager.TTY_MODE_OFF);
} else {
- updateTTY(telecomManager.getCurrentTtyMode());
+ updateTTY(mTelecomManager.getCurrentTtyMode());
}
}
@@ -430,7 +425,7 @@
// TTY is on
if (DEBUG) Log.v(TAG, "updateTTY: set TTY on");
mIconController.setIcon(mSlotTty, R.drawable.stat_sys_tty_mode,
- mContext.getString(R.string.accessibility_tty_enabled));
+ mResources.getString(R.string.accessibility_tty_enabled));
mIconController.setIconVisibility(mSlotTty, true);
} else {
// TTY is off
@@ -452,7 +447,7 @@
mHandler.removeCallbacks(mRemoveCastIconRunnable);
if (isCasting && !mRecordingController.isRecording()) { // screen record has its own icon
mIconController.setIcon(mSlotCast, R.drawable.stat_sys_cast,
- mContext.getString(R.string.accessibility_casting));
+ mResources.getString(R.string.accessibility_casting));
mIconController.setIconVisibility(mSlotCast, true);
} else {
// don't turn off the screen-record icon for a few seconds, just to make sure the user
@@ -478,7 +473,7 @@
showIcon = true;
mIconController.setIcon(mSlotManagedProfile,
R.drawable.stat_sys_managed_profile_status,
- mContext.getString(R.string.accessibility_managed_profile));
+ mResources.getString(R.string.accessibility_managed_profile));
} else {
showIcon = false;
}
@@ -545,7 +540,7 @@
@Override
public void appTransitionStarting(int displayId, long startTime, long duration,
boolean forced) {
- if (mContext.getDisplayId() == displayId) {
+ if (mDisplayId == displayId) {
updateManagedProfile();
}
}
@@ -567,14 +562,14 @@
@Override
public void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible) {
boolean portrait = RotationLockTile.isCurrentOrientationLockPortrait(
- mRotationLockController, mContext);
+ mRotationLockController, mResources);
if (rotationLocked) {
if (portrait) {
mIconController.setIcon(mSlotRotate, R.drawable.stat_sys_rotate_portrait,
- mContext.getString(R.string.accessibility_rotation_lock_on_portrait));
+ mResources.getString(R.string.accessibility_rotation_lock_on_portrait));
} else {
mIconController.setIcon(mSlotRotate, R.drawable.stat_sys_rotate_landscape,
- mContext.getString(R.string.accessibility_rotation_lock_on_landscape));
+ mResources.getString(R.string.accessibility_rotation_lock_on_landscape));
}
mIconController.setIconVisibility(mSlotRotate, true);
} else {
@@ -586,7 +581,7 @@
boolean connected = intent.getIntExtra("state", 0) != 0;
boolean hasMic = intent.getIntExtra("microphone", 0) != 0;
if (connected) {
- String contentDescription = mContext.getString(hasMic
+ String contentDescription = mResources.getString(hasMic
? R.string.accessibility_status_bar_headset
: R.string.accessibility_status_bar_headphones);
mIconController.setIcon(mSlotHeadset, hasMic ? R.drawable.stat_sys_headset_mic
@@ -630,7 +625,6 @@
if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
break;
}
- updateSimState(intent);
break;
case TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED:
updateTTY(intent.getIntExtra(TelecomManager.EXTRA_CURRENT_TTY_MODE,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java
index e8bc2f5..2052ee6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java
@@ -29,23 +29,21 @@
private static final float ICON_ALPHA_WHEN_LIGHTS_OUT_BATTERY_CLOCK = 0.5f;
private static final float ICON_ALPHA_WHEN_LIGHTS_OUT_NON_BATTERY_CLOCK = 0;
- private final PhoneStatusBarView mView;
private final float mIconAlphaWhenOpaque;
- private View mLeftSide, mStatusIcons, mBattery, mClock;
+ private View mLeftSide, mStatusIcons, mBattery;
private Animator mCurrentAnimation;
- public PhoneStatusBarTransitions(PhoneStatusBarView view) {
- super(view, R.drawable.status_background);
- mView = view;
- final Resources res = mView.getContext().getResources();
+ /**
+ * @param backgroundView view to apply the background drawable
+ */
+ public PhoneStatusBarTransitions(PhoneStatusBarView statusBarView, View backgroundView) {
+ super(backgroundView, R.drawable.status_background);
+ final Resources res = statusBarView.getContext().getResources();
mIconAlphaWhenOpaque = res.getFraction(R.dimen.status_bar_icon_drawing_alpha, 1, 1);
- }
-
- public void init() {
- mLeftSide = mView.findViewById(R.id.status_bar_left_side);
- mStatusIcons = mView.findViewById(R.id.statusIcons);
- mBattery = mView.findViewById(R.id.battery);
+ mLeftSide = statusBarView.findViewById(R.id.status_bar_left_side);
+ mStatusIcons = statusBarView.findViewById(R.id.statusIcons);
+ mBattery = statusBarView.findViewById(R.id.battery);
applyModeBackground(-1, getMode(), false /*animate*/);
applyMode(getMode(), false /*animate*/);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index b949e3a..1359f74 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -53,7 +53,6 @@
StatusBar mBar;
boolean mIsFullyOpenedPanel = false;
- private final PhoneStatusBarTransitions mBarTransitions;
private ScrimController mScrimController;
private float mMinFraction;
private Runnable mHideExpandedRunnable = new Runnable() {
@@ -83,15 +82,9 @@
public PhoneStatusBarView(Context context, AttributeSet attrs) {
super(context, attrs);
-
- mBarTransitions = new PhoneStatusBarTransitions(this);
mCommandQueue = Dependency.get(CommandQueue.class);
}
- public BarTransitions getBarTransitions() {
- return mBarTransitions;
- }
-
public void setBar(StatusBar bar) {
mBar = bar;
}
@@ -102,7 +95,6 @@
@Override
public void onFinishInflate() {
- mBarTransitions.init();
mBattery = findViewById(R.id.battery);
mCutoutSpace = findViewById(R.id.cutout_space_view);
mCenterIconSpace = findViewById(R.id.centered_icon_area);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 0d3b09a..19df972 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -163,6 +163,7 @@
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.statusbar.AutoHideUiElement;
import com.android.systemui.statusbar.BackDropView;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CrossFadeHelper;
@@ -676,6 +677,7 @@
KeyguardDismissUtil keyguardDismissUtil,
ExtensionController extensionController,
UserInfoControllerImpl userInfoControllerImpl,
+ PhoneStatusBarPolicy phoneStatusBarPolicy,
DismissCallbackRegistry dismissCallbackRegistry,
StatusBarTouchableRegionManager statusBarTouchableRegionManager) {
super(context);
@@ -751,6 +753,7 @@
mKeyguardDismissUtil = keyguardDismissUtil;
mExtensionController = extensionController;
mUserInfoControllerImpl = userInfoControllerImpl;
+ mIconPolicy = phoneStatusBarPolicy;
mDismissCallbackRegistry = dismissCallbackRegistry;
mBubbleExpandListener =
@@ -875,8 +878,7 @@
// end old BaseStatusBar.start().
// Lastly, call to the icon policy to install/update all the icons.
- mIconPolicy = new PhoneStatusBarPolicy(mContext, mIconController, mCommandQueue,
- mBroadcastDispatcher, mUiBgExecutor);
+ mIconPolicy.init();
mSignalPolicy = new StatusBarSignalPolicy(mContext, mIconController);
mKeyguardStateController.addCallback(this);
@@ -1073,7 +1075,27 @@
}
});
- mAutoHideController.setStatusBar(this);
+ mAutoHideController.addAutoHideUiElement(new AutoHideUiElement() {
+ @Override
+ public void synchronizeState() {
+ checkBarModes();
+ }
+
+ @Override
+ public boolean shouldHideOnTouch() {
+ return !mRemoteInputManager.getController().isRemoteInputActive();
+ }
+
+ @Override
+ public boolean isVisible() {
+ return isTransientShown();
+ }
+
+ @Override
+ public void hide() {
+ clearTransient();
+ }
+ });
ScrimView scrimBehind = mNotificationShadeWindowView.findViewById(R.id.scrim_behind);
ScrimView scrimInFront = mNotificationShadeWindowView.findViewById(R.id.scrim_in_front);
@@ -2227,7 +2249,7 @@
clearTransient();
}
- void clearTransient() {
+ private void clearTransient() {
if (mTransientShown) {
mTransientShown = false;
handleTransientChanged();
@@ -2305,13 +2327,14 @@
}
protected BarTransitions getStatusBarTransitions() {
- return mStatusBarView.getBarTransitions();
+ return mNotificationShadeWindowViewController.getBarTransitions();
}
void checkBarModes() {
if (mDemoMode) return;
- if (mStatusBarView != null) checkBarMode(mStatusBarMode, mStatusBarWindowState,
- getStatusBarTransitions());
+ if (mNotificationShadeWindowViewController != null) {
+ checkBarMode(mStatusBarMode, mStatusBarWindowState, getStatusBarTransitions());
+ }
mNavigationBarController.checkNavBarModes(mDisplayId);
mNoAnimationOnNextBarModeChange = false;
}
@@ -2329,8 +2352,9 @@
}
private void finishBarAnimations() {
- if (mStatusBarView != null) {
- mStatusBarView.getBarTransitions().finishAnimations();
+ if (mNotificationShadeWindowController != null
+ && mNotificationShadeWindowViewController.getBarTransitions() != null) {
+ mNotificationShadeWindowViewController.getBarTransitions().finishAnimations();
}
mNavigationBarController.finishBarAnimations(mDisplayId);
}
@@ -2396,12 +2420,11 @@
pw.print(" mDozing="); pw.println(mDozing);
pw.print(" mWallpaperSupported= "); pw.println(mWallpaperSupported);
- if (mStatusBarView != null) {
- dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions());
- }
pw.println(" StatusBarWindowView: ");
if (mNotificationShadeWindowViewController != null) {
mNotificationShadeWindowViewController.dump(fd, pw, args);
+ dumpBarTransitions(pw, "PhoneStatusBarTransitions",
+ mNotificationShadeWindowViewController.getBarTransitions());
}
pw.println(" mMediaManager: ");
@@ -3004,8 +3027,10 @@
-1;
if (barMode != -1) {
boolean animate = true;
- if (mStatusBarView != null) {
- mStatusBarView.getBarTransitions().transitionTo(barMode, animate);
+ if (mNotificationShadeWindowController != null
+ && mNotificationShadeWindowViewController.getBarTransitions() != null) {
+ mNotificationShadeWindowViewController.getBarTransitions().transitionTo(
+ barMode, animate);
}
mNavigationBarController.transitionTo(mDisplayId, barMode, animate);
}
@@ -4258,7 +4283,7 @@
return mGutsManager;
}
- boolean isTransientShown() {
+ private boolean isTransientShown() {
return mTransientShown;
}
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 693cdd2..30d6b507 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -199,7 +199,8 @@
public void onEntryRemoved(
@Nullable NotificationEntry entry,
NotificationVisibility visibility,
- boolean removedByUser) {
+ boolean removedByUser,
+ int reason) {
StatusBarNotificationPresenter.this.onNotificationRemoved(
entry.getKey(), entry.getSbn());
if (removedByUser) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneDependenciesModule.java
index fcf698c..69c6814 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneDependenciesModule.java
@@ -16,14 +16,7 @@
package com.android.systemui.statusbar.phone.dagger;
-import android.content.Context;
-import android.os.Handler;
-import android.view.IWindowManager;
-
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.notification.row.RowContentBindStage;
-import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -39,16 +32,6 @@
*/
@Module
public interface StatusBarPhoneDependenciesModule {
- /** */
- @Singleton
- @Provides
- static AutoHideController newAutoHideController(Context context,
- @Main Handler handler,
- NotificationRemoteInputManager notificationRemoteInputManager,
- IWindowManager iWindowManager) {
- return new AutoHideController(context, handler, notificationRemoteInputManager,
- iWindowManager);
- }
/** */
@Singleton
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 e64f821..0a4fdc9 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
@@ -79,6 +79,7 @@
import com.android.systemui.statusbar.phone.LockscreenWallpaper;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -193,6 +194,7 @@
KeyguardDismissUtil keyguardDismissUtil,
ExtensionController extensionController,
UserInfoControllerImpl userInfoControllerImpl,
+ PhoneStatusBarPolicy phoneStatusBarPolicy,
DismissCallbackRegistry dismissCallbackRegistry,
StatusBarTouchableRegionManager statusBarTouchableRegionManager) {
return new StatusBar(
@@ -269,6 +271,7 @@
keyguardDismissUtil,
extensionController,
userInfoControllerImpl,
+ phoneStatusBarPolicy,
dismissCallbackRegistry,
statusBarTouchableRegionManager);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 9003de1..d2d76c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.policy;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_IN;
import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_INOUT;
import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_NONE;
@@ -157,6 +158,7 @@
ServiceState mLastServiceState;
private boolean mUserSetup;
private boolean mSimDetected;
+ private boolean mForceCellularValidated;
private ConfigurationController.ConfigurationListener mConfigurationListener =
new ConfigurationController.ConfigurationListener() {
@@ -284,12 +286,41 @@
mPhoneStateListener = new PhoneStateListener(mReceiverHandler::post) {
@Override
public void onActiveDataSubscriptionIdChanged(int subId) {
+ // For data switching from A to B, we assume B is validated for up to 2 seconds iff:
+ // 1) A and B are in the same subscription group e.g. CBRS data switch. And
+ // 2) A was validated before the switch.
+ // This is to provide smooth transition for UI without showing cross during data
+ // switch.
+ if (keepCellularValidationBitInSwitch(mActiveMobileDataSubscription, subId)) {
+ if (DEBUG) Log.d(TAG, ": mForceCellularValidated to true.");
+ mForceCellularValidated = true;
+ mReceiverHandler.removeCallbacks(mClearForceValidated);
+ mReceiverHandler.postDelayed(mClearForceValidated, 2000);
+ }
mActiveMobileDataSubscription = subId;
doUpdateMobileControllers();
}
};
}
+ private final Runnable mClearForceValidated = () -> {
+ if (DEBUG) Log.d(TAG, ": mClearForceValidated");
+ mForceCellularValidated = false;
+ updateConnectivity();
+ };
+
+ boolean isInGroupDataSwitch(int subId1, int subId2) {
+ SubscriptionInfo info1 = mSubscriptionManager.getActiveSubscriptionInfo(subId1);
+ SubscriptionInfo info2 = mSubscriptionManager.getActiveSubscriptionInfo(subId2);
+ return (info1 != null && info2 != null && info1.getGroupUuid() != null
+ && info1.getGroupUuid().equals(info2.getGroupUuid()));
+ }
+
+ boolean keepCellularValidationBitInSwitch(int sourceSubId, int destSubId) {
+ return mValidatedTransports.get(TRANSPORT_CELLULAR)
+ && isInGroupDataSwitch(sourceSubId, destSubId);
+ }
+
public DataSaverController getDataSaverController() {
return mDataSaverController;
}
@@ -793,6 +824,8 @@
}
}
+ if (mForceCellularValidated) mValidatedTransports.set(TRANSPORT_CELLULAR);
+
if (CHATTY) {
Log.d(TAG, "updateConnectivity: mConnectedTransports=" + mConnectedTransports);
Log.d(TAG, "updateConnectivity: mValidatedTransports=" + mValidatedTransports);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputUriController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputUriController.java
index 4d912de..b503183 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputUriController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputUriController.java
@@ -73,7 +73,7 @@
private final NotificationEntryListener mInlineUriListener = new NotificationEntryListener() {
@Override
public void onEntryRemoved(NotificationEntry entry, NotificationVisibility visibility,
- boolean removedByUser) {
+ boolean removedByUser, int reason) {
try {
mStatusBarManagerService.clearInlineReplyUriPermissions(entry.getKey());
} catch (RemoteException ex) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 312c4ac..d29f4fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -31,14 +31,12 @@
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
-import android.os.AsyncTask;
import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.security.KeyChain;
-import android.security.KeyChain.KeyChainConnection;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
@@ -55,6 +53,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -85,7 +84,7 @@
private final DevicePolicyManager mDevicePolicyManager;
private final PackageManager mPackageManager;
private final UserManager mUserManager;
- private final Handler mBgHandler;
+ private final Executor mBgExecutor;
@GuardedBy("mCallbacks")
private final ArrayList<SecurityControllerCallback> mCallbacks = new ArrayList<>();
@@ -101,16 +100,14 @@
/**
*/
@Inject
- public SecurityControllerImpl(Context context, @Background Handler bgHandler,
- BroadcastDispatcher broadcastDispatcher) {
- this(context, bgHandler, broadcastDispatcher, null);
- }
-
- public SecurityControllerImpl(Context context, Handler bgHandler,
- BroadcastDispatcher broadcastDispatcher, SecurityControllerCallback callback) {
+ public SecurityControllerImpl(
+ Context context,
+ @Background Handler bgHandler,
+ BroadcastDispatcher broadcastDispatcher,
+ @Background Executor bgExecutor
+ ) {
super(broadcastDispatcher);
mContext = context;
- mBgHandler = bgHandler;
mDevicePolicyManager = (DevicePolicyManager)
context.getSystemService(Context.DEVICE_POLICY_SERVICE);
mConnectivityManager = (ConnectivityManager)
@@ -118,10 +115,8 @@
mConnectivityManagerService = IConnectivityManager.Stub.asInterface(
ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
mPackageManager = context.getPackageManager();
- mUserManager = (UserManager)
- context.getSystemService(Context.USER_SERVICE);
-
- addCallback(callback);
+ mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ mBgExecutor = bgExecutor;
IntentFilter filter = new IntentFilter();
filter.addAction(KeyChain.ACTION_TRUST_STORE_CHANGED);
@@ -305,7 +300,23 @@
}
private void refreshCACerts(int userId) {
- new CACertLoader().execute(userId);
+ mBgExecutor.execute(() -> {
+ Pair<Integer, Boolean> idWithCert = null;
+ try (KeyChain.KeyChainConnection conn = KeyChain.bindAsUser(mContext,
+ UserHandle.of(userId))) {
+ boolean hasCACerts = !(conn.getService().getUserCaAliases().getList().isEmpty());
+ idWithCert = new Pair<Integer, Boolean>(userId, hasCACerts);
+ } catch (RemoteException | InterruptedException | AssertionError e) {
+ Log.i(TAG, "failed to get CA certs", e);
+ idWithCert = new Pair<Integer, Boolean>(userId, null);
+ } finally {
+ if (DEBUG) Log.d(TAG, "Refreshing CA Certs " + idWithCert);
+ if (idWithCert != null && idWithCert.second != null) {
+ mHasCACerts.put(idWithCert.first, idWithCert.second);
+ fireCallbacks();
+ }
+ }
+ });
}
private String getNameForVpnConfig(VpnConfig cfg, UserHandle user) {
@@ -408,28 +419,4 @@
}
}
};
-
- protected class CACertLoader extends AsyncTask<Integer, Void, Pair<Integer, Boolean> > {
-
- @Override
- protected Pair<Integer, Boolean> doInBackground(Integer... userId) {
- try (KeyChainConnection conn = KeyChain.bindAsUser(mContext,
- UserHandle.of(userId[0]))) {
- boolean hasCACerts = !(conn.getService().getUserCaAliases().getList().isEmpty());
- return new Pair<Integer, Boolean>(userId[0], hasCACerts);
- } catch (RemoteException | InterruptedException | AssertionError e) {
- Log.i(TAG, "failed to get CA certs", e);
- return new Pair<Integer, Boolean>(userId[0], null);
- }
- }
-
- @Override
- protected void onPostExecute(Pair<Integer, Boolean> result) {
- if (DEBUG) Log.d(TAG, "onPostExecute " + result);
- if (result.second != null) {
- mHasCACerts.put(result.first, result.second);
- fireCallbacks();
- }
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
index 86fe3008..311e8738 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
@@ -25,10 +25,10 @@
import android.util.KeyValueListParser;
import android.util.Log;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.util.DeviceConfigProxy;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -62,10 +62,15 @@
private final Handler mHandler;
private final Context mContext;
+ private final DeviceConfigProxy mDeviceConfig;
private final KeyValueListParser mParser = new KeyValueListParser(',');
@Inject
- public SmartReplyConstants(@Main Handler handler, Context context) {
+ public SmartReplyConstants(
+ @Main Handler handler,
+ Context context,
+ DeviceConfigProxy deviceConfig
+ ) {
mHandler = handler;
mContext = context;
final Resources resources = mContext.getResources();
@@ -86,31 +91,35 @@
mDefaultOnClickInitDelay = resources.getInteger(
R.integer.config_smart_replies_in_notifications_onclick_init_delay);
+ mDeviceConfig = deviceConfig;
registerDeviceConfigListener();
updateConstants();
}
private void registerDeviceConfigListener() {
- DeviceConfig.addOnPropertiesChangedListener(
+ mDeviceConfig.addOnPropertiesChangedListener(
DeviceConfig.NAMESPACE_SYSTEMUI,
this::postToHandler,
- (properties) -> onDeviceConfigPropertiesChanged(properties.getNamespace()));
+ mOnPropertiesChangedListener);
}
private void postToHandler(Runnable r) {
this.mHandler.post(r);
}
- @VisibleForTesting
- void onDeviceConfigPropertiesChanged(String namespace) {
- if (!DeviceConfig.NAMESPACE_SYSTEMUI.equals(namespace)) {
- Log.e(TAG, "Received update from DeviceConfig for unrelated namespace: "
- + namespace);
- return;
- }
-
- updateConstants();
- }
+ private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
+ new DeviceConfig.OnPropertiesChangedListener() {
+ @Override
+ public void onPropertiesChanged(DeviceConfig.Properties properties) {
+ if (!DeviceConfig.NAMESPACE_SYSTEMUI.equals(properties.getNamespace())) {
+ Log.e(TAG,
+ "Received update from DeviceConfig for unrelated namespace: "
+ + properties.getNamespace());
+ return;
+ }
+ updateConstants();
+ }
+ };
private void updateConstants() {
synchronized (SmartReplyConstants.this) {
@@ -120,7 +129,7 @@
mRequiresTargetingP = readDeviceConfigBooleanOrDefaultIfEmpty(
SystemUiDeviceConfigFlags.SSIN_REQUIRES_TARGETING_P,
mDefaultRequiresP);
- mMaxSqueezeRemeasureAttempts = DeviceConfig.getInt(
+ mMaxSqueezeRemeasureAttempts = mDeviceConfig.getInt(
DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.SSIN_MAX_SQUEEZE_REMEASURE_ATTEMPTS,
mDefaultMaxSqueezeRemeasureAttempts);
@@ -130,24 +139,24 @@
mShowInHeadsUp = readDeviceConfigBooleanOrDefaultIfEmpty(
SystemUiDeviceConfigFlags.SSIN_SHOW_IN_HEADS_UP,
mDefaultShowInHeadsUp);
- mMinNumSystemGeneratedReplies = DeviceConfig.getInt(
+ mMinNumSystemGeneratedReplies = mDeviceConfig.getInt(
DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.SSIN_MIN_NUM_SYSTEM_GENERATED_REPLIES,
mDefaultMinNumSystemGeneratedReplies);
- mMaxNumActions = DeviceConfig.getInt(
+ mMaxNumActions = mDeviceConfig.getInt(
DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.SSIN_MAX_NUM_ACTIONS,
mDefaultMaxNumActions);
- mOnClickInitDelay = DeviceConfig.getInt(
+ mOnClickInitDelay = mDeviceConfig.getInt(
DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.SSIN_ONCLICK_INIT_DELAY,
mDefaultOnClickInitDelay);
}
}
- private static boolean readDeviceConfigBooleanOrDefaultIfEmpty(String propertyName,
+ private boolean readDeviceConfigBooleanOrDefaultIfEmpty(String propertyName,
boolean defaultValue) {
- String value = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_SYSTEMUI, propertyName);
+ String value = mDeviceConfig.getProperty(DeviceConfig.NAMESPACE_SYSTEMUI, propertyName);
if (TextUtils.isEmpty(value)) {
return defaultValue;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 241d978..07985ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -74,11 +74,6 @@
startSystemActivity(new Intent(ACTION_OPEN_TV_NOTIFICATIONS_PANEL));
}
- @Override
- public void animateCollapsePanels(int flags, boolean force) {
- startSystemActivity(new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME));
- }
-
private void startSystemActivity(Intent intent) {
PackageManager pm = mContext.getPackageManager();
ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_SYSTEM_ONLY);
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
index edab4a7..0242e83 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
@@ -21,21 +21,16 @@
import android.app.INotificationManager;
import android.app.ITransientNotificationCallback;
import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.PixelFormat;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
-import android.view.Gravity;
-import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;
-import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
-import android.widget.TextView;
import android.widget.Toast;
+import android.widget.ToastPresenter;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -64,6 +59,7 @@
private final WindowManager mWindowManager;
private final INotificationManager mNotificationManager;
private final AccessibilityManager mAccessibilityManager;
+ private final ToastPresenter mPresenter;
private ToastEntry mCurrentToast;
@Inject
@@ -83,6 +79,7 @@
mWindowManager = windowManager;
mNotificationManager = notificationManager;
mAccessibilityManager = accessibilityManager;
+ mPresenter = new ToastPresenter(context, accessibilityManager);
}
@Override
@@ -97,8 +94,8 @@
if (mCurrentToast != null) {
hideCurrentToast();
}
- View view = getView(text);
- LayoutParams params = getLayoutParams(windowToken, duration);
+ View view = mPresenter.getTextToastView(text);
+ LayoutParams params = getLayoutParams(packageName, windowToken, duration);
mCurrentToast = new ToastEntry(packageName, token, view, windowToken, callback);
try {
mWindowManager.addView(view, params);
@@ -106,7 +103,7 @@
Log.w(TAG, "Error while attempting to show toast from " + packageName, e);
return;
}
- trySendAccessibilityEvent(view, packageName);
+ mPresenter.trySendAccessibilityEvent(view, packageName);
if (callback != null) {
try {
callback.onToastShown();
@@ -148,56 +145,13 @@
mCurrentToast = null;
}
- private void trySendAccessibilityEvent(View view, String packageName) {
- if (!mAccessibilityManager.isEnabled()) {
- return;
- }
- AccessibilityEvent event = AccessibilityEvent.obtain(
- AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
- event.setClassName(Toast.class.getName());
- event.setPackageName(packageName);
- view.dispatchPopulateAccessibilityEvent(event);
- mAccessibilityManager.sendAccessibilityEvent(event);
- }
-
- private View getView(CharSequence text) {
- View view = LayoutInflater.from(mContext).inflate(
- R.layout.transient_notification, null);
- TextView textView = view.findViewById(com.android.internal.R.id.message);
- textView.setText(text);
- return view;
- }
-
- private LayoutParams getLayoutParams(IBinder windowToken, int duration) {
+ private LayoutParams getLayoutParams(String packageName, IBinder windowToken, int duration) {
WindowManager.LayoutParams params = new WindowManager.LayoutParams();
- params.height = WindowManager.LayoutParams.WRAP_CONTENT;
- params.width = WindowManager.LayoutParams.WRAP_CONTENT;
- params.format = PixelFormat.TRANSLUCENT;
- params.windowAnimations = com.android.internal.R.style.Animation_Toast;
- params.type = WindowManager.LayoutParams.TYPE_TOAST;
- params.setTitle("Toast");
- params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
- Configuration config = mContext.getResources().getConfiguration();
- int specificGravity = mContext.getResources().getInteger(
+ mPresenter.startLayoutParams(params, packageName);
+ int gravity = mContext.getResources().getInteger(
com.android.internal.R.integer.config_toastDefaultGravity);
- int gravity = Gravity.getAbsoluteGravity(specificGravity, config.getLayoutDirection());
- params.gravity = gravity;
- if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
- params.horizontalWeight = 1.0f;
- }
- if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
- params.verticalWeight = 1.0f;
- }
- params.x = 0;
- params.y = mContext.getResources().getDimensionPixelSize(R.dimen.toast_y_offset);
- params.verticalMargin = 0;
- params.horizontalMargin = 0;
- params.packageName = mContext.getPackageName();
- params.hideTimeoutMilliseconds =
- (duration == Toast.LENGTH_LONG) ? DURATION_LONG : DURATION_SHORT;
- params.token = windowToken;
+ int yOffset = mContext.getResources().getDimensionPixelSize(R.dimen.toast_y_offset);
+ mPresenter.adjustLayoutParams(params, windowToken, duration, gravity, 0, yOffset, 0, 0);
return params;
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index cfa2947..5f82118 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -126,9 +126,10 @@
/**
* Allow the media player to be shown in the QS area, controlled by 2 flags.
+ * On by default, but can be disabled by setting to 0
*/
public static boolean useQsMediaPlayer(Context context) {
- int flag = Settings.System.getInt(context.getContentResolver(), "qs_media_player", 0);
+ int flag = Settings.System.getInt(context.getContentResolver(), "qs_media_player", 1);
return flag > 0;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
index b5bede4..13ba1a3c 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
@@ -40,12 +40,11 @@
*/
public class ProximitySensor {
private static final String TAG = "ProxSensor";
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final Sensor mSensor;
private final AsyncSensorManager mSensorManager;
- private final boolean mUsingBrightnessSensor;
- private final float mMaxRange;
+ private final float mThreshold;
private List<ProximitySensorListener> mListeners = new ArrayList<>();
private String mTag = null;
@VisibleForTesting ProximityEvent mLastEvent;
@@ -68,20 +67,27 @@
public ProximitySensor(@Main Resources resources,
AsyncSensorManager sensorManager) {
mSensorManager = sensorManager;
- Sensor sensor = findBrightnessSensor(resources);
+ Sensor sensor = findCustomProxSensor(resources);
+ float threshold = 0;
+ if (sensor != null) {
+ try {
+ threshold = getCustomProxThreshold(resources);
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "Can not load custom proximity sensor.", e);
+ sensor = null;
+ }
+ }
if (sensor == null) {
- mUsingBrightnessSensor = false;
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
- } else {
- mUsingBrightnessSensor = true;
+ if (sensor != null) {
+ threshold = sensor.getMaximumRange();
+ }
}
+
+ mThreshold = threshold;
+
mSensor = sensor;
- if (mSensor != null) {
- mMaxRange = mSensor.getMaximumRange();
- } else {
- mMaxRange = 0;
- }
}
public void setTag(String tag) {
@@ -107,9 +113,15 @@
mPaused = false;
registerInternal();
}
+ /**
+ * Returns a brightness sensor that can be used for proximity purposes.
+ */
+ private Sensor findCustomProxSensor(Resources resources) {
+ String sensorType = resources.getString(R.string.proximity_sensor_type);
+ if (sensorType.isEmpty()) {
+ return null;
+ }
- private Sensor findBrightnessSensor(Resources resources) {
- String sensorType = resources.getString(R.string.doze_brightness_sensor_type);
List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
Sensor sensor = null;
for (Sensor s : sensorList) {
@@ -123,6 +135,17 @@
}
/**
+ * Returns a threshold value that can be used along with {@link #findCustomProxSensor}
+ */
+ private float getCustomProxThreshold(Resources resources) {
+ try {
+ return resources.getFloat(R.dimen.proximity_sensor_threshold);
+ } catch (Resources.NotFoundException e) {
+ throw new IllegalStateException("R.dimen.proximity_sensor_threshold must be set.");
+ }
+ }
+
+ /**
* Returns true if we are registered with the SensorManager.
*/
public boolean isRegistered() {
@@ -157,7 +180,6 @@
if (mRegistered || mPaused || mListeners.isEmpty()) {
return;
}
- logDebug("Using brightness sensor? " + mUsingBrightnessSensor);
logDebug("Registering sensor listener");
mRegistered = true;
mSensorManager.registerListener(mSensorEventListener, mSensor, mSensorDelay);
@@ -196,10 +218,7 @@
}
private void onSensorEvent(SensorEvent event) {
- boolean near = event.values[0] < mMaxRange;
- if (mUsingBrightnessSensor) {
- near = event.values[0] == 0;
- }
+ boolean near = event.values[0] < mThreshold;
mLastEvent = new ProximityEvent(near, event.timestamp);
alertListeners();
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/time/DateFormatUtil.java b/packages/SystemUI/src/com/android/systemui/util/time/DateFormatUtil.java
new file mode 100644
index 0000000..d7c4e93
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/time/DateFormatUtil.java
@@ -0,0 +1,40 @@
+/*
+ * 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.util.time;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.text.format.DateFormat;
+
+import javax.inject.Inject;
+
+/**
+ * Instantiable wrapper around {@link DateFormat}.
+ */
+public class DateFormatUtil {
+ private final Context mContext;
+
+ @Inject
+ public DateFormatUtil(Context context) {
+ mContext = context;
+ }
+
+ /** Returns true if the phone is in 24 hour format. */
+ public boolean is24HourFormat() {
+ return DateFormat.is24HourFormat(mContext, ActivityManager.getCurrentUser());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
index c912b67..69e933e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
@@ -16,6 +16,8 @@
package com.android.systemui;
+import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNull;
import static junit.framework.TestCase.fail;
@@ -528,7 +530,8 @@
.setSbn(notification)
.build(),
null,
- false);
+ false,
+ REASON_APP_CANCEL);
}
private void entryAdded(StatusBarNotification notification, int importance) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
index 689eed9..678cfd2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
@@ -34,6 +34,7 @@
import android.graphics.ColorSpace;
import android.graphics.Rect;
import android.hardware.display.DisplayManagerGlobal;
+import android.os.Handler;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -72,6 +73,8 @@
private Bitmap mWallpaperBitmap;
@Mock
private DozeParameters mDozeParam;
+ @Mock
+ private Handler mHandler;
private CountDownLatch mEventCountdown;
@@ -104,7 +107,7 @@
return new ImageWallpaper(mDozeParam) {
@Override
public Engine onCreateEngine() {
- return new GLEngine(mMockContext, mDozeParam) {
+ return new GLEngine(mDozeParam, mHandler) {
@Override
public Context getDisplayContext() {
return mMockContext;
@@ -196,5 +199,6 @@
when(mSurfaceHolder.getSurfaceFrame()).thenReturn(frame);
assertThat(engineSpy.checkIfShouldStopTransition()).isEqualTo(assertion);
+ // destroy
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index a974c6d..1b34b3d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -456,4 +456,52 @@
assertThat(rectsToRegion(Collections.singletonList(rect)).getBounds(), is(rect));
}
+ @Test
+ public void testRegistration_From_NoOverlay_To_HasOverlays() {
+ doReturn(false).when(mScreenDecorations).hasOverlays();
+ mScreenDecorations.start();
+ verify(mTunerService, times(0)).addTunable(any(), any());
+ verify(mTunerService, times(1)).removeTunable(any());
+ assertThat(mScreenDecorations.mIsRegistered, is(false));
+ reset(mTunerService);
+
+ doReturn(true).when(mScreenDecorations).hasOverlays();
+ mScreenDecorations.onConfigurationChanged(new Configuration());
+ verify(mTunerService, times(1)).addTunable(any(), any());
+ verify(mTunerService, times(0)).removeTunable(any());
+ assertThat(mScreenDecorations.mIsRegistered, is(true));
+ }
+
+ @Test
+ public void testRegistration_From_HasOverlays_To_HasOverlays() {
+ doReturn(true).when(mScreenDecorations).hasOverlays();
+
+ mScreenDecorations.start();
+ verify(mTunerService, times(1)).addTunable(any(), any());
+ verify(mTunerService, times(0)).removeTunable(any());
+ assertThat(mScreenDecorations.mIsRegistered, is(true));
+ reset(mTunerService);
+
+ mScreenDecorations.onConfigurationChanged(new Configuration());
+ verify(mTunerService, times(0)).addTunable(any(), any());
+ verify(mTunerService, times(0)).removeTunable(any());
+ assertThat(mScreenDecorations.mIsRegistered, is(true));
+ }
+
+ @Test
+ public void testRegistration_From_HasOverlays_To_NoOverlay() {
+ doReturn(true).when(mScreenDecorations).hasOverlays();
+
+ mScreenDecorations.start();
+ verify(mTunerService, times(1)).addTunable(any(), any());
+ verify(mTunerService, times(0)).removeTunable(any());
+ assertThat(mScreenDecorations.mIsRegistered, is(true));
+ reset(mTunerService);
+
+ doReturn(false).when(mScreenDecorations).hasOverlays();
+ mScreenDecorations.onConfigurationChanged(new Configuration());
+ verify(mTunerService, times(0)).addTunable(any(), any());
+ verify(mTunerService, times(1)).removeTunable(any());
+ assertThat(mScreenDecorations.mIsRegistered, is(false));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index 7ac5443..a36f2c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -86,7 +86,9 @@
public void SysuiTeardown() {
InstrumentationRegistry.registerInstance(mRealInstrumentation,
InstrumentationRegistry.getArguments());
- // Reset the assert's testable looper to null.
+ if (TestableLooper.get(this) != null) {
+ TestableLooper.get(this).processAllMessages();
+ }
disallowTestableLooperAsMainThread();
SystemUIFactory.cleanup();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 3009593..9fe9e6d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -685,7 +685,7 @@
assertTrue(mBubbleController.hasBubbles());
// Removes the notification
- mEntryListener.onEntryRemoved(mRow.getEntry(), null, false);
+ mEntryListener.onEntryRemoved(mRow.getEntry(), null, false, REASON_APP_CANCEL);
assertFalse(mBubbleController.hasBubbles());
}
@@ -827,7 +827,7 @@
mBubbleController.handleDismissalInterception(groupSummary.getEntry());
// WHEN the summary is cancelled by the app
- mEntryListener.onEntryRemoved(groupSummary.getEntry(), null, true);
+ mEntryListener.onEntryRemoved(groupSummary.getEntry(), null, false, REASON_APP_CANCEL);
// THEN the summary and its children are removed from bubble data
assertFalse(mBubbleData.hasBubbleWithKey(groupedBubble.getEntry().getKey()));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
index 545d2d4..5b78067 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
@@ -33,7 +33,9 @@
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerFake;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.DeviceConfigProxyFake;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -63,6 +65,7 @@
private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
private DockManager mDockManager = new DockManagerFake();
+ private StatusBarStateController mStatusBarStateController = new StatusBarStateControllerImpl();
@Before
public void setup() {
@@ -83,7 +86,7 @@
public void test_brightLineFalsingManagerDisabled() {
mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor, mDisplayMetrics,
mProximitySensor, mDeviceConfig, mDockManager, mKeyguardUpdateMonitor,
- mDumpManager, mUiBgExecutor);
+ mDumpManager, mUiBgExecutor, mStatusBarStateController);
assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class));
}
@@ -94,7 +97,7 @@
mExecutor.runAllReady();
mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor, mDisplayMetrics,
mProximitySensor, mDeviceConfig, mDockManager, mKeyguardUpdateMonitor,
- mDumpManager, mUiBgExecutor);
+ mDumpManager, mUiBgExecutor, mStatusBarStateController);
assertThat(mProxy.getInternalFalsingManager(), instanceOf(BrightLineFalsingManager.class));
}
@@ -102,7 +105,7 @@
public void test_brightLineFalsingManagerToggled() throws InterruptedException {
mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor, mDisplayMetrics,
mProximitySensor, mDeviceConfig, mDockManager, mKeyguardUpdateMonitor,
- mDumpManager, mUiBgExecutor);
+ mDumpManager, mUiBgExecutor, mStatusBarStateController);
assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class));
mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java
index 0aaa3b6..8b5cc9a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java
@@ -22,12 +22,16 @@
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.util.DisplayMetrics;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerFake;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.StatusBarStateControllerImpl;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.DeviceConfigProxyFake;
import com.android.systemui.util.sensors.ProximitySensor;
@@ -40,6 +44,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
public class BrightLineFalsingManagerTest extends SysuiTestCase {
@@ -47,6 +52,7 @@
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
private ProximitySensor mProximitySensor;
+ private SysuiStatusBarStateController mStatusBarStateController;
private BrightLineFalsingManager mFalsingManager;
@@ -61,8 +67,11 @@
FalsingDataProvider falsingDataProvider = new FalsingDataProvider(dm);
DeviceConfigProxy deviceConfigProxy = new DeviceConfigProxyFake();
DockManager dockManager = new DockManagerFake();
+ mStatusBarStateController = new StatusBarStateControllerImpl();
+ mStatusBarStateController.setState(StatusBarState.KEYGUARD);
mFalsingManager = new BrightLineFalsingManager(falsingDataProvider,
- mKeyguardUpdateMonitor, mProximitySensor, deviceConfigProxy, dockManager);
+ mKeyguardUpdateMonitor, mProximitySensor, deviceConfigProxy, dockManager,
+ mStatusBarStateController);
}
@Test
@@ -98,4 +107,12 @@
mFalsingManager.onBouncerHidden();
verify(mProximitySensor).register(any(ProximitySensor.ProximitySensorListener.class));
}
+
+ @Test
+ public void testUnregisterSensor_StateTransition() {
+ mFalsingManager.onScreenTurningOn();
+ reset(mProximitySensor);
+ mStatusBarStateController.setState(StatusBarState.SHADE);
+ verify(mProximitySensor).unregister(any(ProximitySensor.ProximitySensorListener.class));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt
new file mode 100644
index 0000000..68e1ec1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.systemui.controls.management
+
+import android.app.PendingIntent
+import android.service.controls.Control
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.controls.ControlStatus
+import com.android.systemui.controls.controller.ControlInfo
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class AllModelTest : SysuiTestCase() {
+
+ companion object {
+ private const val EMPTY_STRING = "Other"
+ }
+
+ @Mock
+ lateinit var pendingIntent: PendingIntent
+
+ val idPrefix = "controlId"
+ val favoritesIndices = listOf(7, 3, 1, 9)
+ val favoritesList = favoritesIndices.map { "controlId$it" }
+ lateinit var controls: List<ControlStatus>
+
+ lateinit var model: AllModel
+
+ private fun zoneMap(id: Int): String? {
+ return when (id) {
+ 10 -> ""
+ 11 -> null
+ else -> ((id + 1) % 3).toString()
+ }
+ }
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ // controlId0 --> zone = 1
+ // controlId1 --> zone = 2, favorite
+ // controlId2 --> zone = 0
+ // controlId3 --> zone = 1, favorite
+ // controlId4 --> zone = 2
+ // controlId5 --> zone = 0
+ // controlId6 --> zone = 1
+ // controlId7 --> zone = 2, favorite
+ // controlId8 --> zone = 0
+ // controlId9 --> zone = 1, favorite
+ // controlId10 --> zone = ""
+ // controlId11 --> zone = null
+ controls = (0..11).map {
+ ControlStatus(
+ Control.StatelessBuilder("$idPrefix$it", pendingIntent)
+ .setZone(zoneMap(it))
+ .build(),
+ it in favoritesIndices
+ )
+ }
+ model = AllModel(controls, favoritesList, EMPTY_STRING)
+ }
+
+ @Test
+ fun testElements() {
+
+ // Zones are sorted by order of appearance, with empty at the end with special header.
+ val expected = listOf(
+ ZoneNameWrapper("1"),
+ ControlWrapper(controls[0]),
+ ControlWrapper(controls[3]),
+ ControlWrapper(controls[6]),
+ ControlWrapper(controls[9]),
+ ZoneNameWrapper("2"),
+ ControlWrapper(controls[1]),
+ ControlWrapper(controls[4]),
+ ControlWrapper(controls[7]),
+ ZoneNameWrapper("0"),
+ ControlWrapper(controls[2]),
+ ControlWrapper(controls[5]),
+ ControlWrapper(controls[8]),
+ ZoneNameWrapper(EMPTY_STRING),
+ ControlWrapper(controls[10]),
+ ControlWrapper(controls[11])
+ )
+ expected.zip(model.elements).forEachIndexed { index, it ->
+ assertEquals("Error in item at index $index", it.first, it.second)
+ }
+ }
+
+ private fun sameControl(controlInfo: ControlInfo.Builder, control: Control): Boolean {
+ return controlInfo.controlId == control.controlId &&
+ controlInfo.controlTitle == control.title &&
+ controlInfo.deviceType == control.deviceType
+ }
+
+ @Test
+ fun testAllEmpty_noHeader() {
+ val selected_controls = listOf(controls[10], controls[11])
+ val new_model = AllModel(selected_controls, emptyList(), EMPTY_STRING)
+ val expected = listOf(
+ ControlWrapper(controls[10]),
+ ControlWrapper(controls[11])
+ )
+
+ expected.zip(new_model.elements).forEachIndexed { index, it ->
+ assertEquals("Error in item at index $index", it.first, it.second)
+ }
+ }
+
+ @Test
+ fun testFavorites() {
+ val expectedFavorites = favoritesIndices.map(controls::get).map(ControlStatus::control)
+ model.favorites.zip(expectedFavorites).forEach {
+ assertTrue(sameControl(it.first, it.second))
+ }
+ }
+
+ @Test
+ fun testAddFavorite() {
+ val indexToAdd = 6
+ model.changeFavoriteStatus("$idPrefix$indexToAdd", true)
+
+ val expectedFavorites = favoritesIndices.map(controls::get).map(ControlStatus::control) +
+ controls[indexToAdd].control
+
+ model.favorites.zip(expectedFavorites).forEach {
+ assertTrue(sameControl(it.first, it.second))
+ }
+ }
+
+ @Test
+ fun testAddFavorite_alreadyThere() {
+ val indexToAdd = 7
+ model.changeFavoriteStatus("$idPrefix$indexToAdd", true)
+
+ val expectedFavorites = favoritesIndices.map(controls::get).map(ControlStatus::control)
+
+ model.favorites.zip(expectedFavorites).forEach {
+ assertTrue(sameControl(it.first, it.second))
+ }
+ }
+
+ @Test
+ fun testRemoveFavorite() {
+ val indexToRemove = 3
+ model.changeFavoriteStatus("$idPrefix$indexToRemove", false)
+
+ val expectedFavorites = (favoritesIndices.filterNot { it == indexToRemove })
+ .map(controls::get)
+ .map(ControlStatus::control)
+
+ model.favorites.zip(expectedFavorites).forEach {
+ assertTrue(sameControl(it.first, it.second))
+ }
+ }
+
+ @Test
+ fun testRemoveFavorite_notThere() {
+ val indexToRemove = 4
+ model.changeFavoriteStatus("$idPrefix$indexToRemove", false)
+
+ val expectedFavorites = favoritesIndices.map(controls::get).map(ControlStatus::control)
+
+ model.favorites.zip(expectedFavorites).forEach {
+ assertTrue(sameControl(it.first, it.second))
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 9ef5520..8320b05 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -44,6 +44,8 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.util.DeviceConfigProxy;
+import com.android.systemui.util.DeviceConfigProxyFake;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -69,6 +71,7 @@
private @Mock DumpManager mDumpManager;
private @Mock PowerManager mPowerManager;
private @Mock TrustManager mTrustManager;
+ private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake();
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
private FalsingManagerFake mFalsingManager;
@@ -85,7 +88,7 @@
mContext, mFalsingManager, mLockPatternUtils, mBroadcastDispatcher,
mNotificationShadeWindowController, () -> mStatusBarKeyguardViewManager,
mDismissCallbackRegistry, mUpdateMonitor, mDumpManager, mUiBgExecutor,
- mPowerManager, mTrustManager);
+ mPowerManager, mTrustManager, mDeviceConfig);
mViewMediator.start();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
index 1dbcf10..3b00684 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
@@ -16,6 +16,7 @@
package com.android.systemui.pip;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import android.content.ComponentName;
@@ -44,8 +45,11 @@
@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class PipBoundsHandlerTest extends SysuiTestCase {
- private static final int ROUNDING_ERROR_MARGIN = 10;
+ private static final int ROUNDING_ERROR_MARGIN = 16;
+ private static final float ASPECT_RATIO_ERROR_MARGIN = 0.01f;
private static final float DEFAULT_ASPECT_RATIO = 1f;
+ private static final float MIN_ASPECT_RATIO = 0.5f;
+ private static final float MAX_ASPECT_RATIO = 2f;
private static final Rect EMPTY_CURRENT_BOUNDS = null;
private PipBoundsHandler mPipBoundsHandler;
@@ -53,8 +57,8 @@
@Before
public void setUp() throws Exception {
- mPipBoundsHandler = new PipBoundsHandler(mContext);
initializeMockResources();
+ mPipBoundsHandler = new PipBoundsHandler(mContext);
mPipBoundsHandler.onDisplayInfoChanged(mDefaultDisplayInfo);
}
@@ -62,7 +66,8 @@
private void initializeMockResources() {
final TestableResources res = mContext.getOrCreateTestableResources();
res.addOverride(
- com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio, 1f);
+ com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio,
+ DEFAULT_ASPECT_RATIO);
res.addOverride(
com.android.internal.R.integer.config_defaultPictureInPictureGravity,
Gravity.END | Gravity.BOTTOM);
@@ -72,9 +77,11 @@
com.android.internal.R.string.config_defaultPictureInPictureScreenEdgeInsets,
"16x16");
res.addOverride(
- com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio, 0.5f);
+ com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio,
+ MIN_ASPECT_RATIO);
res.addOverride(
- com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio, 2f);
+ com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio,
+ MAX_ASPECT_RATIO);
mDefaultDisplayInfo = new DisplayInfo();
mDefaultDisplayInfo.displayId = 1;
@@ -83,6 +90,76 @@
}
@Test
+ public void getDefaultAspectRatio() {
+ assertEquals("Default aspect ratio matches resources",
+ DEFAULT_ASPECT_RATIO, mPipBoundsHandler.getDefaultAspectRatio(),
+ ASPECT_RATIO_ERROR_MARGIN);
+ }
+
+ @Test
+ public void onConfigurationChanged_reloadResources() {
+ final float newDefaultAspectRatio = (DEFAULT_ASPECT_RATIO + MAX_ASPECT_RATIO) / 2;
+ final TestableResources res = mContext.getOrCreateTestableResources();
+ res.addOverride(com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio,
+ newDefaultAspectRatio);
+
+ mPipBoundsHandler.onConfigurationChanged();
+
+ assertEquals("Default aspect ratio should be reloaded",
+ mPipBoundsHandler.getDefaultAspectRatio(), newDefaultAspectRatio,
+ ASPECT_RATIO_ERROR_MARGIN);
+ }
+
+ @Test
+ public void getDestinationBounds_returnBoundsMatchesAspectRatio() {
+ final float[] aspectRatios = new float[] {
+ (MIN_ASPECT_RATIO + DEFAULT_ASPECT_RATIO) / 2,
+ DEFAULT_ASPECT_RATIO,
+ (MAX_ASPECT_RATIO + DEFAULT_ASPECT_RATIO) / 2
+ };
+ for (float aspectRatio : aspectRatios) {
+ final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
+ aspectRatio, EMPTY_CURRENT_BOUNDS);
+ final float actualAspectRatio =
+ destinationBounds.width() / (destinationBounds.height() * 1f);
+ assertEquals("Destination bounds matches the given aspect ratio",
+ aspectRatio, actualAspectRatio, ASPECT_RATIO_ERROR_MARGIN);
+ }
+ }
+
+ @Test
+ public void getDestinationBounds_invalidAspectRatio_returnsDefaultAspectRatio() {
+ final float[] invalidAspectRatios = new float[] {
+ MIN_ASPECT_RATIO / 2,
+ MAX_ASPECT_RATIO * 2
+ };
+ for (float aspectRatio : invalidAspectRatios) {
+ final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
+ aspectRatio, EMPTY_CURRENT_BOUNDS);
+ final float actualAspectRatio =
+ destinationBounds.width() / (destinationBounds.height() * 1f);
+ assertEquals("Destination bounds fallbacks to default aspect ratio",
+ mPipBoundsHandler.getDefaultAspectRatio(), actualAspectRatio,
+ ASPECT_RATIO_ERROR_MARGIN);
+ }
+ }
+
+ @Test
+ public void getDestinationBounds_withCurrentBounds_returnBoundsMatchesAspectRatio() {
+ final float aspectRatio = (DEFAULT_ASPECT_RATIO + MAX_ASPECT_RATIO) / 2;
+ final Rect currentBounds = new Rect(0, 0, 0, 100);
+ currentBounds.right = (int) (currentBounds.height() * aspectRatio) + currentBounds.left;
+
+ final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
+ aspectRatio, currentBounds);
+
+ final float actualAspectRatio =
+ destinationBounds.width() / (destinationBounds.height() * 1f);
+ assertEquals("Destination bounds matches the given aspect ratio",
+ aspectRatio, actualAspectRatio, ASPECT_RATIO_ERROR_MARGIN);
+ }
+
+ @Test
public void setShelfHeight_offsetBounds() {
final int shelfHeight = 100;
final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(
@@ -93,7 +170,7 @@
DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
oldPosition.offset(0, -shelfHeight);
- assertBoundsWithMargin("PiP bounds offset by shelf height", oldPosition, newPosition);
+ assertBoundsWithMargin("offsetBounds by shelf", oldPosition, newPosition);
}
@Test
@@ -107,7 +184,7 @@
DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
oldPosition.offset(0, -imeHeight);
- assertBoundsWithMargin("PiP bounds offset by IME height", oldPosition, newPosition);
+ assertBoundsWithMargin("offsetBounds by IME", oldPosition, newPosition);
}
@Test
@@ -122,11 +199,11 @@
final Rect newPosition = mPipBoundsHandler.getDestinationBounds(
DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
- assertBoundsWithMargin("Last position is restored", oldPosition, newPosition);
+ assertBoundsWithMargin("restoreLastPosition", oldPosition, newPosition);
}
@Test
- public void onResetReentryBounds_componentMatch_useDefaultBounds() {
+ public void onResetReentryBounds_useDefaultBounds() {
final ComponentName componentName = new ComponentName(mContext, "component1");
final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(
DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
@@ -138,7 +215,7 @@
final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(
DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
- assertBoundsWithMargin("Use default bounds", defaultBounds, actualBounds);
+ assertBoundsWithMargin("useDefaultBounds", defaultBounds, actualBounds);
}
@Test
@@ -154,11 +231,15 @@
final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(
DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
- assertBoundsWithMargin("Last position is restored", newBounds, actualBounds);
+ assertBoundsWithMargin("restoreLastPosition", newBounds, actualBounds);
}
- private void assertBoundsWithMargin(String msg, Rect expected, Rect actual) {
- expected.inset(-ROUNDING_ERROR_MARGIN, -ROUNDING_ERROR_MARGIN);
- assertTrue(msg, expected.contains(actual));
+ private void assertBoundsWithMargin(String from, Rect expected, Rect actual) {
+ final Rect expectedWithMargin = new Rect(expected);
+ expectedWithMargin.inset(-ROUNDING_ERROR_MARGIN, -ROUNDING_ERROR_MARGIN);
+ assertTrue(from + ": expect " + expected
+ + " contains " + actual
+ + " with error margin " + ROUNDING_ERROR_MARGIN,
+ expectedWithMargin.contains(actual));
}
}
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 36a9ea6..0098012 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -27,6 +27,7 @@
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
@@ -76,7 +77,9 @@
public class QSTileHostTest extends SysuiTestCase {
private static String MOCK_STATE_STRING = "MockState";
- private static final String CUSTOM_TILE_SPEC = "custom(TEST_PKG/.TEST_CLS)";
+ private static ComponentName CUSTOM_TILE =
+ ComponentName.unflattenFromString("TEST_PKG/.TEST_CLS");
+ private static final String CUSTOM_TILE_SPEC = CustomTile.toSpec(CUSTOM_TILE);
@Mock
private StatusBarIconController mIconController;
@@ -114,24 +117,30 @@
mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpManager,
mBroadcastDispatcher, mStatusBar, mQSLogger);
setUpTileFactory();
+
+ // Override this config so there are no unexpected tiles
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.string.config_defaultExtraQuickSettingsTiles,
+ "");
+
Settings.Secure.putStringForUser(mContext.getContentResolver(), QSTileHost.TILES_SETTING,
"", ActivityManager.getCurrentUser());
}
private void setUpTileFactory() {
when(mMockState.toString()).thenReturn(MOCK_STATE_STRING);
+ // Only create this kind of tiles
when(mDefaultFactory.createTile(anyString())).thenAnswer(
invocation -> {
String spec = invocation.getArgument(0);
- switch (spec) {
- case "spec1":
- return new TestTile1(mQSTileHost);
- case "spec2":
- return new TestTile2(mQSTileHost);
- case CUSTOM_TILE_SPEC:
- return mCustomTile;
- default:
- return null;
+ if ("spec1".equals(spec)) {
+ return new TestTile1(mQSTileHost);
+ } else if ("spec2".equals(spec)) {
+ return new TestTile2(mQSTileHost);
+ } else if (CUSTOM_TILE_SPEC.equals(spec)) {
+ return mCustomTile;
+ } else {
+ return null;
}
});
when(mCustomTile.isAvailable()).thenReturn(true);
@@ -222,6 +231,58 @@
verify(mQSLogger).logTileAdded(CUSTOM_TILE_SPEC);
}
+ @Test
+ public void testNoRepeatedSpecs_addTile() {
+ mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1,spec2");
+
+ mQSTileHost.addTile("spec1");
+
+ assertEquals(2, mQSTileHost.mTileSpecs.size());
+ assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
+ assertEquals("spec2", mQSTileHost.mTileSpecs.get(1));
+ }
+
+ @Test
+ public void testNoRepeatedSpecs_customTile() {
+ mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, CUSTOM_TILE_SPEC);
+
+ mQSTileHost.addTile(CUSTOM_TILE);
+
+ assertEquals(1, mQSTileHost.mTileSpecs.size());
+ assertEquals(CUSTOM_TILE_SPEC, mQSTileHost.mTileSpecs.get(0));
+ }
+
+ @Test
+ public void testLoadTileSpec_repeated() {
+ List<String> specs = QSTileHost.loadTileSpecs(mContext, "spec1,spec1,spec2");
+
+ assertEquals(2, specs.size());
+ assertEquals("spec1", specs.get(0));
+ assertEquals("spec2", specs.get(1));
+ }
+
+ @Test
+ public void testLoadTileSpec_repeatedInDefault() {
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.string.quick_settings_tiles_default, "spec1,spec1");
+ List<String> specs = QSTileHost.loadTileSpecs(mContext, "default");
+
+ // Remove spurious tiles, like dbg:mem
+ specs.removeIf(spec -> !"spec1".equals(spec));
+ assertEquals(1, specs.size());
+ }
+
+ @Test
+ public void testLoadTileSpec_repeatedDefaultAndSetting() {
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.string.quick_settings_tiles_default, "spec1");
+ List<String> specs = QSTileHost.loadTileSpecs(mContext, "default,spec1");
+
+ // Remove spurious tiles, like dbg:mem
+ specs.removeIf(spec -> !"spec1".equals(spec));
+ assertEquals(1, specs.size());
+ }
+
private static class TestQSTileHost extends QSTileHost {
TestQSTileHost(Context context, StatusBarIconController iconController,
QSFactoryImpl defaultFactory, Handler mainHandler, Looper bgLooper,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
index 9fe2569..58be50e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
@@ -33,6 +33,7 @@
import com.android.systemui.qs.QSTileHost
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
+import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -84,13 +85,26 @@
.thenReturn(mServiceInfo)
mServiceInfo.applicationInfo = mApplicationInfo
- customTile = CustomTile.create(mTileHost, TILE_SPEC)
+ customTile = CustomTile.create(mTileHost, TILE_SPEC, mContext)
+ }
+
+ @Test
+ fun testCorrectUser() {
+ assertEquals(0, customTile.user)
+
+ val userContext = mock(Context::class.java)
+ `when`(userContext.packageManager).thenReturn(mPackageManager)
+ `when`(userContext.userId).thenReturn(10)
+
+ val tile = CustomTile.create(mTileHost, TILE_SPEC, userContext)
+
+ assertEquals(10, tile.user)
}
@Test
fun testToggleableTileHasBooleanState() {
`when`(mTileServiceManager.isToggleableTile).thenReturn(true)
- customTile = CustomTile.create(mTileHost, TILE_SPEC)
+ customTile = CustomTile.create(mTileHost, TILE_SPEC, mContext)
assertTrue(customTile.state is QSTile.BooleanState)
assertTrue(customTile.newTileState() is QSTile.BooleanState)
@@ -105,7 +119,7 @@
@Test
fun testValueUpdatedInBooleanTile() {
`when`(mTileServiceManager.isToggleableTile).thenReturn(true)
- customTile = CustomTile.create(mTileHost, TILE_SPEC)
+ customTile = CustomTile.create(mTileHost, TILE_SPEC, mContext)
customTile.qsTile.icon = mock(Icon::class.java)
`when`(customTile.qsTile.icon.loadDrawable(any(Context::class.java)))
.thenReturn(mock(Drawable::class.java))
@@ -125,4 +139,12 @@
customTile.handleUpdateState(state, null)
assertFalse(state.value)
}
+
+ @Test
+ fun testNoCrashOnNullDrawable() {
+ customTile.qsTile.icon = mock(Icon::class.java)
+ `when`(customTile.qsTile.icon.loadDrawable(any(Context::class.java)))
+ .thenReturn(null)
+ customTile.handleUpdateState(customTile.newTileState(), null)
+ }
}
\ No newline at end of file
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 8e87e0a..cc5f149 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -41,11 +41,11 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
+import com.android.systemui.statusbar.notification.DynamicChildBindController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -106,17 +106,14 @@
mock(KeyguardBypassController.class),
mock(BubbleController.class),
mock(DynamicPrivacyController.class),
- mock(ForegroundServiceSectionController.class));
+ mock(ForegroundServiceSectionController.class),
+ mock(DynamicChildBindController.class));
mViewHierarchyManager.setUpWithPresenter(mPresenter, mListContainer);
}
private NotificationEntry createEntry() throws Exception {
ExpandableNotificationRow row = mHelper.createRow();
- NotificationEntry entry = new NotificationEntryBuilder()
- .setSbn(row.getEntry().getSbn())
- .build();
- entry.setRow(row);
- return entry;
+ return row.getEntry();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
index 16f105d..fe8b89f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
@@ -20,6 +20,7 @@
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager.Importance;
+import android.content.pm.ShortcutInfo;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.SnoozeCriterion;
@@ -53,6 +54,7 @@
private boolean mCanBubble = false;
private boolean mIsVisuallyInterruptive = false;
private boolean mIsConversation = false;
+ private ShortcutInfo mShortcutInfo = null;
public RankingBuilder() {
}
@@ -79,6 +81,7 @@
mCanBubble = ranking.canBubble();
mIsVisuallyInterruptive = ranking.visuallyInterruptive();
mIsConversation = ranking.isConversation();
+ mShortcutInfo = ranking.getShortcutInfo();
}
public Ranking build() {
@@ -104,7 +107,8 @@
mSmartReplies,
mCanBubble,
mIsVisuallyInterruptive,
- mIsConversation);
+ mIsConversation,
+ mShortcutInfo);
return ranking;
}
@@ -189,6 +193,11 @@
return this;
}
+ public RankingBuilder setShortcutInfo(ShortcutInfo shortcutInfo) {
+ mShortcutInfo = shortcutInfo;
+ return this;
+ }
+
public RankingBuilder setImportance(@Importance int importance) {
mImportance = importance;
return this;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java
new file mode 100644
index 0000000..bf2d598
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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 static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
+import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.util.ArrayMap;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.RowContentBindParams;
+import com.android.systemui.statusbar.notification.row.RowContentBindStage;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class DynamicChildBindControllerTest extends SysuiTestCase {
+
+ private DynamicChildBindController mDynamicChildBindController;
+ private Map<NotificationEntry, List<NotificationEntry>> mGroupNotifs = new ArrayMap<>();
+ private static final int TEST_CHILD_BIND_CUTOFF = 5;
+
+ @Mock private RowContentBindStage mBindStage;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ allowTestableLooperAsMainThread();
+ when(mBindStage.getStageParams(any())).thenReturn(new RowContentBindParams());
+ mDynamicChildBindController =
+ new DynamicChildBindController(mBindStage, TEST_CHILD_BIND_CUTOFF);
+ }
+
+ @Test
+ public void testContentViewsOfChildrenBeyondCutoffAreFreed() {
+ // GIVEN a group notification with one view beyond the cutoff with content bound
+ NotificationEntry summary = addGroup(TEST_CHILD_BIND_CUTOFF + 1);
+ NotificationEntry lastChild = mGroupNotifs.get(summary).get(TEST_CHILD_BIND_CUTOFF);
+
+ RowContentBindParams bindParams = mock(RowContentBindParams.class);
+ when(mBindStage.getStageParams(lastChild)).thenReturn(bindParams);
+
+ // WHEN the controller gets the list
+ mDynamicChildBindController.updateChildContentViews(mGroupNotifs);
+
+ // THEN we free content views
+ verify(bindParams).freeContentViews(FLAG_CONTENT_VIEW_CONTRACTED);
+ verify(bindParams).freeContentViews(FLAG_CONTENT_VIEW_EXPANDED);
+ verify(mBindStage).requestRebind(eq(lastChild), any());
+ }
+
+ @Test
+ public void testContentViewsBeforeCutoffAreBound() {
+ // GIVEN a group notification with one view before the cutoff with content unbound
+ NotificationEntry summary = addGroup(TEST_CHILD_BIND_CUTOFF);
+ NotificationEntry lastChild = mGroupNotifs.get(summary).get(TEST_CHILD_BIND_CUTOFF - 1);
+
+ lastChild.getRow().getPrivateLayout().setContractedChild(null);
+ lastChild.getRow().getPrivateLayout().setExpandedChild(null);
+
+ RowContentBindParams bindParams = mock(RowContentBindParams.class);
+ when(mBindStage.getStageParams(lastChild)).thenReturn(bindParams);
+
+ // WHEN the controller gets the list
+ mDynamicChildBindController.updateChildContentViews(mGroupNotifs);
+
+ // THEN we bind content views
+ verify(bindParams).requireContentViews(FLAG_CONTENT_VIEW_CONTRACTED);
+ verify(bindParams).requireContentViews(FLAG_CONTENT_VIEW_EXPANDED);
+ verify(mBindStage).requestRebind(eq(lastChild), any());
+ }
+
+ private NotificationEntry addGroup(int size) {
+ NotificationEntry summary = new NotificationEntryBuilder().build();
+ summary.setRow(createRow());
+ ArrayList<NotificationEntry> children = new ArrayList<>();
+ for (int i = 0; i < size; i++) {
+ NotificationEntry child = new NotificationEntryBuilder().build();
+ child.setRow(createRow());
+ children.add(child);
+ }
+ mGroupNotifs.put(summary, children);
+ return summary;
+ }
+
+ private ExpandableNotificationRow createRow() {
+ ExpandableNotificationRow row = (ExpandableNotificationRow)
+ LayoutInflater.from(mContext).inflate(R.layout.status_bar_notification_row, null);
+ row.getPrivateLayout().setContractedChild(new View(mContext));
+ row.getPrivateLayout().setExpandedChild(new View(mContext));
+ return row;
+ }
+}
+
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 98f12ce..312bb7f 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
@@ -143,7 +143,7 @@
IMPORTANCE_DEFAULT,
null, null,
null, null, null, true, sentiment, false, -1, false, null, null, false, false,
- false);
+ false, null);
return true;
}).when(mRankingMap).getRanking(eq(key), any(Ranking.class));
}
@@ -162,7 +162,7 @@
null, null,
null, null, null, true,
Ranking.USER_SENTIMENT_NEUTRAL, false, -1,
- false, smartActions, null, false, false, false);
+ false, smartActions, null, false, false, false, null);
return true;
}).when(mRankingMap).getRanking(eq(key), any(Ranking.class));
}
@@ -254,7 +254,7 @@
verify(mPresenter).updateNotificationViews();
verify(mEntryListener).onEntryRemoved(
- eq(mEntry), any(), eq(false) /* removedByUser */);
+ eq(mEntry), any(), eq(false) /* removedByUser */, eq(UNDEFINED_DISMISS_REASON));
verify(mRow).setRemoved();
assertNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()));
@@ -266,7 +266,7 @@
mEntryManager.removeNotification("not_a_real_key", mRankingMap, UNDEFINED_DISMISS_REASON);
verify(mEntryListener, never()).onEntryRemoved(
- eq(mEntry), any(), eq(false) /* removedByUser */);
+ eq(mEntry), any(), eq(false) /* removedByUser */, eq(UNDEFINED_DISMISS_REASON));
}
@Test
@@ -275,7 +275,7 @@
mEntryManager.removeNotification(mSbn.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
verify(mEntryListener, never()).onEntryRemoved(
- eq(mEntry), any(), eq(false /* removedByUser */));
+ eq(mEntry), any(), eq(false /* removedByUser */), eq(UNDEFINED_DISMISS_REASON));
}
@Test
@@ -356,7 +356,8 @@
verify(extender).setShouldManageLifetime(mEntry, true);
// THEN the notification is retained
assertNotNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()));
- verify(mEntryListener, never()).onEntryRemoved(eq(mEntry), any(), eq(false));
+ verify(mEntryListener, never()).onEntryRemoved(
+ eq(mEntry), any(), eq(false), eq(UNDEFINED_DISMISS_REASON));
}
@Test
@@ -374,7 +375,8 @@
// THEN the notification is removed
assertNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()));
- verify(mEntryListener).onEntryRemoved(eq(mEntry), any(), eq(false));
+ verify(mEntryListener).onEntryRemoved(
+ eq(mEntry), any(), eq(false), eq(UNDEFINED_DISMISS_REASON));
}
@Test
@@ -447,7 +449,7 @@
// THEN the interceptor intercepts & the entry is not removed & no listeners are called
assertNotNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()));
verify(mEntryListener, never()).onEntryRemoved(eq(mEntry),
- any(NotificationVisibility.class), anyBoolean());
+ any(NotificationVisibility.class), anyBoolean(), eq(UNDEFINED_DISMISS_REASON));
}
@Test
@@ -466,7 +468,7 @@
// THEN the interceptor intercepts & the entry is not removed & no listeners are called
assertNull(mEntryManager.getActiveNotificationUnfiltered(mSbn.getKey()));
verify(mEntryListener, atLeastOnce()).onEntryRemoved(eq(mEntry),
- any(NotificationVisibility.class), anyBoolean());
+ any(NotificationVisibility.class), anyBoolean(), eq(UNDEFINED_DISMISS_REASON));
}
private NotificationEntry createNotification() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java
index 7343e5e..19dd027 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification;
+import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON;
+
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.verify;
@@ -87,7 +89,8 @@
mEntryListener.onEntryRemoved(
entry,
NotificationVisibility.obtain(entry.getKey(), 0, 0, true),
- false);
+ false,
+ UNDEFINED_DISMISS_REASON);
verify(mListContainer).cleanUpViewStateForEntry(entry);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
index d2bb011..92a9080 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
@@ -21,6 +21,7 @@
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
+import android.content.pm.ShortcutInfo;
import android.os.UserHandle;
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
@@ -266,4 +267,9 @@
mRankingBuilder.setSmartReplies(smartReplies);
return this;
}
+
+ public NotificationEntryBuilder setShortcutInfo(ShortcutInfo shortcutInfo) {
+ mRankingBuilder.setShortcutInfo(shortcutInfo);
+ return this;
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
index f37836c..5a6f888 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
@@ -40,6 +40,7 @@
import dagger.Lazy
import junit.framework.Assert.assertEquals
import org.junit.Before
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
@@ -195,6 +196,7 @@
assertEquals(listOf(b, a), rankingManager.updateRanking(null, listOf(a, b), "test"))
}
+ @Ignore // TODO: (b/149046729) fix test and re-enable
@Test
fun testSort_importantPeople() {
whenever(sectionsManager.isFilteringEnabled()).thenReturn(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt
index b0ca943..b1288f8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt
@@ -101,15 +101,13 @@
@Test
fun testViewModelDataSourceTransformsModel() {
- val fakeClickIntent = PendingIntent.getActivity(context, 0, Intent("action"), 0)
- val fakePerson = fakePersonModel("id", "name", fakeClickIntent)
+ val fakeClickRunnable = mock(Runnable::class.java)
+ val fakePerson = fakePersonModel("id", "name", fakeClickRunnable)
val fakeModel = PeopleHubModel(listOf(fakePerson))
val fakeModelDataSource = FakeDataSource(fakeModel)
- val fakeSettingDataSource = FakeDataSource(true)
val factoryDataSource = PeopleHubViewModelFactoryDataSourceImpl(
mockActivityStarter,
- fakeModelDataSource,
- fakeSettingDataSource
+ fakeModelDataSource
)
val fakeListener = FakeDataListener<PeopleHubViewModelFactory>()
val mockClickView = mock(View::class.java)
@@ -126,35 +124,7 @@
people[0].onClick()
- verify(mockActivityStarter).startPendingIntentDismissingKeyguard(
- same(fakeClickIntent),
- any(),
- same(mockClickView)
- )
- }
-
- @Test
- fun testViewModelDataSource_notVisibleIfSettingDisabled() {
- val fakeClickIntent = PendingIntent.getActivity(context, 0, Intent("action"), 0)
- val fakePerson = fakePersonModel("id", "name", fakeClickIntent)
- val fakeModel = PeopleHubModel(listOf(fakePerson))
- val fakeModelDataSource = FakeDataSource(fakeModel)
- val fakeSettingDataSource = FakeDataSource(false)
- val factoryDataSource = PeopleHubViewModelFactoryDataSourceImpl(
- mockActivityStarter,
- fakeModelDataSource,
- fakeSettingDataSource
- )
- val fakeListener = FakeDataListener<PeopleHubViewModelFactory>()
- val mockClickView = mock(View::class.java)
-
- factoryDataSource.registerListener(fakeListener)
-
- val viewModel = (fakeListener.lastSeen as Maybe.Just).value
- .createWithAssociatedClickView(mockClickView)
- assertThat(viewModel.isVisible).isFalse()
- val people = viewModel.people.toList()
- assertThat(people.size).isEqualTo(0)
+ verify(fakeClickRunnable).run()
}
}
@@ -178,10 +148,10 @@
private fun fakePersonModel(
id: String,
name: CharSequence,
- clickIntent: PendingIntent,
+ clickRunnable: Runnable,
userId: Int = 0
): PersonModel =
- PersonModel(id, name, mock(Drawable::class.java), clickIntent, userId)
+ PersonModel(id, name, mock(Drawable::class.java), clickRunnable, userId)
private fun fakePersonViewModel(name: CharSequence): PersonViewModel =
PersonViewModel(name, mock(Drawable::class.java), mock({}.javaClass))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
index 138ea39..e1ab33a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
@@ -30,6 +30,7 @@
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
@@ -53,8 +54,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.UserHandle;
import android.provider.Settings;
@@ -117,7 +117,7 @@
@Mock
private ShortcutInfo mShortcutInfo;
@Mock
- private Bitmap mImage;
+ private Drawable mIconDrawable;
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
@@ -183,8 +183,9 @@
when(mShortcutInfo.getShortLabel()).thenReturn("Convo name");
List<ShortcutInfo> shortcuts = Arrays.asList(mShortcutInfo);
when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcuts);
- when(mIconFactory.getConversationBitmap(any(ShortcutInfo.class), anyString(), anyInt()))
- .thenReturn(mImage);
+ when(mIconFactory.getConversationDrawable(
+ any(ShortcutInfo.class), anyString(), anyInt(), anyBoolean()))
+ .thenReturn(mIconDrawable);
mNotificationChannel = new NotificationChannel(
TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_LOW);
@@ -197,7 +198,7 @@
.build();
mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
notification, UserHandle.CURRENT, null, 0);
- mEntry = new NotificationEntryBuilder().setSbn(mSbn).build();
+ mEntry = new NotificationEntryBuilder().setSbn(mSbn).setShortcutInfo(mShortcutInfo).build();
PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0,
new Intent(mContext, BubblesTestActivity.class), 0);
@@ -206,7 +207,10 @@
.createIntentBubble(bubbleIntent,
Icon.createWithResource(mContext, R.drawable.android)).build())
.build();
- mBubbleEntry = new NotificationEntryBuilder().setSbn(mBubbleSbn).build();
+ mBubbleEntry = new NotificationEntryBuilder()
+ .setSbn(mBubbleSbn)
+ .setShortcutInfo(mShortcutInfo)
+ .build();
mConversationChannel = new NotificationChannel(
TEST_CHANNEL + " : " + CONVERSATION_ID, TEST_CHANNEL_NAME, IMPORTANCE_LOW);
@@ -233,7 +237,7 @@
mIconFactory,
true);
final ImageView view = mNotificationInfo.findViewById(R.id.conversation_icon);
- assertEquals(mImage, ((BitmapDrawable) view.getDrawable()).getBitmap());
+ assertEquals(mIconDrawable, view.getDrawable());
}
@Test
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 ac40808..5d0349d 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
@@ -77,6 +77,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -89,6 +90,7 @@
* Functional tests for notification inflation from {@link NotificationEntryManager}.
*/
@SmallTest
+@Ignore("Flaking")
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class NotificationEntryManagerInflationTest extends SysuiTestCase {
@@ -281,7 +283,8 @@
null,
false,
false,
- false);
+ false,
+ null);
mRankingMap = new NotificationListenerService.RankingMap(new Ranking[] {ranking});
TestableLooper.get(this).processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java
index 69e4f22..18ea774 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java
@@ -25,6 +25,7 @@
import android.media.MediaMetadata;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
+import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
@@ -66,6 +67,9 @@
allowTestableLooperAsMainThread();
mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
+
+ // These tests are for regular media style notifications, not controls in quick settings
+ Settings.System.putInt(mContext.getContentResolver(), "qs_media_player", 0);
}
private void makeTestNotification(long duration, boolean allowSeeking) throws Exception {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index abfbcd9..c64dd09 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -19,6 +19,8 @@
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING;
+import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_HEADS_UP;
+import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE;
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT;
import static org.mockito.ArgumentMatchers.any;
@@ -107,7 +109,7 @@
@Test
public void testInsertHeader() {
// GIVEN a stack with HI and LO rows but no section headers
- setStackState(ChildType.HIPRI, ChildType.HIPRI, ChildType.HIPRI, ChildType.LOPRI);
+ setStackState(ChildType.ALERTING, ChildType.ALERTING, ChildType.ALERTING, ChildType.GENTLE);
// WHEN we update the section headers
mSectionsManager.updateSectionBoundaries();
@@ -119,11 +121,15 @@
@Test
public void testRemoveHeader() {
// GIVEN a stack that originally had a header between the HI and LO sections
- setStackState(ChildType.HIPRI, ChildType.HIPRI, ChildType.LOPRI);
+ setStackState(ChildType.ALERTING, ChildType.ALERTING, ChildType.GENTLE);
mSectionsManager.updateSectionBoundaries();
// WHEN the last LO row is replaced with a HI row
- setStackState(ChildType.HIPRI, ChildType.HIPRI, ChildType.HEADER, ChildType.HIPRI);
+ setStackState(
+ ChildType.ALERTING,
+ ChildType.ALERTING,
+ ChildType.GENTLE_HEADER,
+ ChildType.ALERTING);
clearInvocations(mNssl);
mSectionsManager.updateSectionBoundaries();
@@ -134,7 +140,7 @@
@Test
public void testDoNothingIfHeaderAlreadyRemoved() {
// GIVEN a stack with only HI rows
- setStackState(ChildType.HIPRI, ChildType.HIPRI, ChildType.HIPRI);
+ setStackState(ChildType.ALERTING, ChildType.ALERTING, ChildType.ALERTING);
// WHEN we update the sections headers
mSectionsManager.updateSectionBoundaries();
@@ -147,19 +153,19 @@
public void testMoveHeaderForward() {
// GIVEN a stack that originally had a header between the HI and LO sections
setStackState(
- ChildType.HIPRI,
- ChildType.HIPRI,
- ChildType.HIPRI,
- ChildType.LOPRI);
+ ChildType.ALERTING,
+ ChildType.ALERTING,
+ ChildType.ALERTING,
+ ChildType.GENTLE);
mSectionsManager.updateSectionBoundaries();
// WHEN the LO section moves forward
setStackState(
- ChildType.HIPRI,
- ChildType.HIPRI,
- ChildType.LOPRI,
- ChildType.HEADER,
- ChildType.LOPRI);
+ ChildType.ALERTING,
+ ChildType.ALERTING,
+ ChildType.GENTLE,
+ ChildType.GENTLE_HEADER,
+ ChildType.GENTLE);
mSectionsManager.updateSectionBoundaries();
// THEN the LO section header is also moved forward
@@ -170,19 +176,19 @@
public void testMoveHeaderBackward() {
// GIVEN a stack that originally had a header between the HI and LO sections
setStackState(
- ChildType.HIPRI,
- ChildType.LOPRI,
- ChildType.LOPRI,
- ChildType.LOPRI);
+ ChildType.ALERTING,
+ ChildType.GENTLE,
+ ChildType.GENTLE,
+ ChildType.GENTLE);
mSectionsManager.updateSectionBoundaries();
// WHEN the LO section moves backward
setStackState(
- ChildType.HIPRI,
- ChildType.HEADER,
- ChildType.HIPRI,
- ChildType.HIPRI,
- ChildType.LOPRI);
+ ChildType.ALERTING,
+ ChildType.GENTLE_HEADER,
+ ChildType.ALERTING,
+ ChildType.ALERTING,
+ ChildType.GENTLE);
mSectionsManager.updateSectionBoundaries();
// THEN the LO section header is also moved backward (with appropriate index shifting)
@@ -193,14 +199,14 @@
public void testHeaderRemovedFromTransientParent() {
// GIVEN a stack where the header is animating away
setStackState(
- ChildType.HIPRI,
- ChildType.LOPRI,
- ChildType.LOPRI,
- ChildType.LOPRI);
+ ChildType.ALERTING,
+ ChildType.GENTLE,
+ ChildType.GENTLE,
+ ChildType.GENTLE);
mSectionsManager.updateSectionBoundaries();
setStackState(
- ChildType.HIPRI,
- ChildType.HEADER);
+ ChildType.ALERTING,
+ ChildType.GENTLE_HEADER);
mSectionsManager.updateSectionBoundaries();
clearInvocations(mNssl);
@@ -209,8 +215,8 @@
// WHEN the LO section reappears
setStackState(
- ChildType.HIPRI,
- ChildType.LOPRI);
+ ChildType.ALERTING,
+ ChildType.GENTLE);
mSectionsManager.updateSectionBoundaries();
// THEN the header is first removed from the transient parent before being added to the
@@ -223,7 +229,7 @@
public void testHeaderNotShownOnLockscreen() {
// GIVEN a stack of HI and LO notifs on the lockscreen
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
- setStackState(ChildType.HIPRI, ChildType.HIPRI, ChildType.HIPRI, ChildType.LOPRI);
+ setStackState(ChildType.ALERTING, ChildType.ALERTING, ChildType.ALERTING, ChildType.GENTLE);
// WHEN we update the section headers
mSectionsManager.updateSectionBoundaries();
@@ -236,7 +242,7 @@
public void testHeaderShownWhenEnterLockscreen() {
// GIVEN a stack of HI and LO notifs on the lockscreen
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
- setStackState(ChildType.HIPRI, ChildType.HIPRI, ChildType.HIPRI, ChildType.LOPRI);
+ setStackState(ChildType.ALERTING, ChildType.ALERTING, ChildType.ALERTING, ChildType.GENTLE);
mSectionsManager.updateSectionBoundaries();
// WHEN we unlock
@@ -250,37 +256,104 @@
@Test
public void testHeaderHiddenWhenEnterLockscreen() {
// GIVEN a stack of HI and LO notifs on the shade
- when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED);
- setStackState(ChildType.HIPRI, ChildType.HIPRI, ChildType.HIPRI, ChildType.LOPRI);
- mSectionsManager.updateSectionBoundaries();
+ setStackState(ChildType.ALERTING, ChildType.GENTLE_HEADER, ChildType.GENTLE);
// WHEN we go back to the keyguard
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
mSectionsManager.updateSectionBoundaries();
// Then the section header is removed
- verify(mNssl).removeView(eq(mSectionsManager.getGentleHeaderView()));
+ verify(mNssl).removeView(mSectionsManager.getGentleHeaderView());
}
- private enum ChildType { HEADER, HIPRI, LOPRI }
+ @Test
+ public void testPeopleFiltering_addHeadersFromShowingOnlyGentle() {
+ enablePeopleFiltering();
+
+ setStackState(
+ ChildType.GENTLE_HEADER,
+ ChildType.PERSON,
+ ChildType.ALERTING,
+ ChildType.GENTLE);
+ mSectionsManager.updateSectionBoundaries();
+
+ verify(mNssl).changeViewPosition(mSectionsManager.getGentleHeaderView(), 2);
+ verify(mNssl).addView(mSectionsManager.getAlertingHeaderView(), 1);
+ verify(mNssl).addView(mSectionsManager.getPeopleHeaderView(), 0);
+ }
+
+ @Test
+ public void testPeopleFiltering_addAllHeaders() {
+ enablePeopleFiltering();
+
+ setStackState(
+ ChildType.PERSON,
+ ChildType.ALERTING,
+ ChildType.GENTLE);
+ mSectionsManager.updateSectionBoundaries();
+
+ verify(mNssl).addView(mSectionsManager.getGentleHeaderView(), 2);
+ verify(mNssl).addView(mSectionsManager.getAlertingHeaderView(), 1);
+ verify(mNssl).addView(mSectionsManager.getPeopleHeaderView(), 0);
+ }
+
+ @Test
+ public void testPeopleFiltering_moveAllHeaders() {
+ enablePeopleFiltering();
+
+ setStackState(
+ ChildType.PEOPLE_HEADER,
+ ChildType.ALERTING_HEADER,
+ ChildType.GENTLE_HEADER,
+ ChildType.PERSON,
+ ChildType.ALERTING,
+ ChildType.GENTLE);
+ mSectionsManager.updateSectionBoundaries();
+
+ verify(mNssl).changeViewPosition(mSectionsManager.getGentleHeaderView(), 4);
+ verify(mNssl).changeViewPosition(mSectionsManager.getAlertingHeaderView(), 2);
+ verify(mNssl).changeViewPosition(mSectionsManager.getPeopleHeaderView(), 0);
+ }
+
+ private void enablePeopleFiltering() {
+ when(mSectionsFeatureManager.isFilteringEnabled()).thenReturn(true);
+ when(mSectionsFeatureManager.getNumberOfBuckets()).thenReturn(4);
+ }
+
+ private enum ChildType {
+ PEOPLE_HEADER, ALERTING_HEADER, GENTLE_HEADER, HEADS_UP, PERSON, ALERTING, GENTLE, OTHER
+ }
private void setStackState(ChildType... children) {
when(mNssl.getChildCount()).thenReturn(children.length);
for (int i = 0; i < children.length; i++) {
View child;
switch (children[i]) {
- case HEADER:
+ case PEOPLE_HEADER:
+ child = mSectionsManager.getPeopleHeaderView();
+ break;
+ case ALERTING_HEADER:
+ child = mSectionsManager.getAlertingHeaderView();
+ break;
+ case GENTLE_HEADER:
child = mSectionsManager.getGentleHeaderView();
break;
- case HIPRI:
- case LOPRI:
- ExpandableNotificationRow notifRow = mock(ExpandableNotificationRow.class,
- RETURNS_DEEP_STUBS);
- when(notifRow.getVisibility()).thenReturn(View.VISIBLE);
- when(notifRow.getEntry().getBucket()).thenReturn(
- children[i] == ChildType.HIPRI ? BUCKET_ALERTING : BUCKET_SILENT);
- when(notifRow.getParent()).thenReturn(mNssl);
- child = notifRow;
+ case HEADS_UP:
+ child = mockNotification(BUCKET_HEADS_UP);
+ break;
+ case PERSON:
+ child = mockNotification(BUCKET_PEOPLE);
+ break;
+ case ALERTING:
+ child = mockNotification(BUCKET_ALERTING);
+ break;
+ case GENTLE:
+ child = mockNotification(BUCKET_SILENT);
+ break;
+ case OTHER:
+ child = mock(View.class);
+ when(child.getVisibility()).thenReturn(View.VISIBLE);
+ when(child.getParent()).thenReturn(mNssl);
break;
default:
throw new RuntimeException("Unknown ChildType: " + children[i]);
@@ -289,4 +362,13 @@
when(mNssl.indexOfChild(child)).thenReturn(i);
}
}
+
+ private View mockNotification(int bucket) {
+ ExpandableNotificationRow notifRow = mock(ExpandableNotificationRow.class,
+ RETURNS_DEEP_STUBS);
+ when(notifRow.getVisibility()).thenReturn(View.VISIBLE);
+ when(notifRow.getEntry().getBucket()).thenReturn(bucket);
+ when(notifRow.getParent()).thenReturn(mNssl);
+ return notifRow;
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
index 3d59d61..dbb4512 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
import static junit.framework.Assert.assertFalse;
@@ -205,7 +206,8 @@
// WHEN all active notifications are removed
when(mEntryManager.hasActiveNotifications()).thenReturn(false);
assertFalse(mLightsOutNotifController.shouldShowDot());
- mEntryListener.onEntryRemoved(mock(NotificationEntry.class), null, false);
+ mEntryListener.onEntryRemoved(
+ mock(NotificationEntry.class), null, false, REASON_CANCEL_ALL);
// THEN we shouldn't see the dot view
assertIsShowingDot(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
index 8f645b6..9aa0fdd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
@@ -70,6 +70,7 @@
import com.android.systemui.recents.Recents;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -255,6 +256,7 @@
Optional.of(mRecents),
() -> mock(StatusBar.class),
mock(ShadeController.class),
+ mock(NotificationRemoteInputManager.class),
mHandler);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
index e171a28..f6a099d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
import static org.junit.Assert.assertFalse;
@@ -254,7 +255,8 @@
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
- mNotificationEntryListener.onEntryRemoved(childEntry, null, false);
+ mNotificationEntryListener.onEntryRemoved(
+ childEntry, null, false, UNDEFINED_DISMISS_REASON);
assertFalse(mGroupAlertTransferHelper.isAlertTransferPending(childEntry));
}
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 e917c93..c5b6969 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
@@ -37,8 +37,9 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.NotificationShadeWindowBlurController;
+import com.android.systemui.statusbar.PulseExpansionHandler;
+import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -81,6 +82,7 @@
@Mock private NotificationPanelViewController mNotificationPanelViewController;
@Mock private NotificationStackScrollLayout mNotificationStackScrollLayout;
@Mock private NotificationShadeWindowBlurController mNotificationShadeWindowBlurController;
+ @Mock private SuperStatusBarViewFactory mStatusBarViewFactory;
@Before
public void setUp() {
@@ -116,7 +118,8 @@
mDockManager,
mNotificationShadeWindowBlurController,
mView,
- mNotificationPanelViewController);
+ mNotificationPanelViewController,
+ mStatusBarViewFactory);
mController.setupExpandedStatusBar();
mController.setService(mStatusBar);
mController.setDragDownHelper(mDragDownHelper);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index d81b8c2..5253e2ca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -247,6 +247,7 @@
@Mock private KeyguardDismissUtil mKeyguardDismissUtil;
@Mock private ExtensionController mExtensionController;
@Mock private UserInfoControllerImpl mUserInfoControllerImpl;
+ @Mock private PhoneStatusBarPolicy mPhoneStatusBarPolicy;
private ShadeController mShadeController;
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
private InitController mInitController = new InitController();
@@ -400,6 +401,7 @@
mKeyguardDismissUtil,
mExtensionController,
mUserInfoControllerImpl,
+ mPhoneStatusBarPolicy,
mDismissCallbackRegistry,
mStatusBarTouchableRegionManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
index e6b0440..44184ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
@@ -28,6 +29,7 @@
import static org.mockito.Mockito.when;
import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -37,7 +39,6 @@
import android.net.ConnectivityManager.NetworkCallback;
import android.net.NetworkRequest;
import android.os.Handler;
-import android.os.Looper;
import android.os.UserManager;
import android.security.IKeyChainService;
import android.test.suitebuilder.annotation.SmallTest;
@@ -46,34 +47,30 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.statusbar.policy.SecurityController.SecurityControllerCallback;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class SecurityControllerTest extends SysuiTestCase implements SecurityControllerCallback {
+public class SecurityControllerTest extends SysuiTestCase {
private final DevicePolicyManager mDevicePolicyManager = mock(DevicePolicyManager.class);
private final IKeyChainService.Stub mKeyChainService = mock(IKeyChainService.Stub.class);
private final UserManager mUserManager = mock(UserManager.class);
+ private final BroadcastDispatcher mBroadcastDispatcher = mock(BroadcastDispatcher.class);
+ private final Handler mHandler = mock(Handler.class);
private SecurityControllerImpl mSecurityController;
- private CountDownLatch mStateChangedLatch;
private ConnectivityManager mConnectivityManager = mock(ConnectivityManager.class);
-
- // implementing SecurityControllerCallback
- @Override
- public void onStateChanged() {
- mStateChangedLatch.countDown();
- }
+ private FakeExecutor mBgExecutor;
+ private BroadcastReceiver mBroadcastReceiver;
@Before
public void setUp() throws Exception {
@@ -95,18 +92,23 @@
when(mKeyChainService.queryLocalInterface("android.security.IKeyChainService"))
.thenReturn(mKeyChainService);
- // Wait for callbacks from the onUserSwitched() function in the
- // constructor of mSecurityController
- mStateChangedLatch = new CountDownLatch(1);
- // TODO: Migrate this test to TestableLooper and use a handler attached
- // to that.
- mSecurityController = new SecurityControllerImpl(mContext,
- new Handler(Looper.getMainLooper()), mock(BroadcastDispatcher.class), this);
- }
+ ArgumentCaptor<BroadcastReceiver> brCaptor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
- @After
- public void tearDown() {
- mSecurityController.removeCallback(this);
+ mBgExecutor = new FakeExecutor(new FakeSystemClock());
+ mSecurityController = new SecurityControllerImpl(
+ mContext,
+ mHandler,
+ mBroadcastDispatcher,
+ mBgExecutor);
+
+ verify(mBroadcastDispatcher).registerReceiverWithHandler(
+ brCaptor.capture(),
+ anyObject(),
+ anyObject(),
+ anyObject());
+
+ mBroadcastReceiver = brCaptor.getValue();
}
@Test
@@ -126,8 +128,6 @@
@Test
public void testWorkAccount() throws Exception {
- // Wait for the callbacks from setUp()
- assertTrue(mStateChangedLatch.await(1, TimeUnit.SECONDS));
assertFalse(mSecurityController.hasCACertInCurrentUser());
final int PRIMARY_USER_ID = 0;
@@ -140,53 +140,41 @@
assertTrue(mSecurityController.hasWorkProfile());
assertFalse(mSecurityController.hasCACertInWorkProfile());
- mStateChangedLatch = new CountDownLatch(1);
-
when(mKeyChainService.getUserCaAliases())
.thenReturn(new StringParceledListSlice(Arrays.asList("One CA Alias")));
- mSecurityController.new CACertLoader()
- .execute(MANAGED_USER_ID);
+ refreshCACerts(MANAGED_USER_ID);
+ mBgExecutor.runAllReady();
- assertTrue(mStateChangedLatch.await(3, TimeUnit.SECONDS));
assertTrue(mSecurityController.hasCACertInWorkProfile());
}
@Test
public void testCaCertLoader() throws Exception {
- // Wait for the callbacks from setUp()
- assertTrue(mStateChangedLatch.await(1, TimeUnit.SECONDS));
assertFalse(mSecurityController.hasCACertInCurrentUser());
// With a CA cert
- mStateChangedLatch = new CountDownLatch(1);
-
when(mKeyChainService.getUserCaAliases())
.thenReturn(new StringParceledListSlice(Arrays.asList("One CA Alias")));
- mSecurityController.new CACertLoader()
- .execute(0);
+ refreshCACerts(0);
+ mBgExecutor.runAllReady();
- assertTrue(mStateChangedLatch.await(3, TimeUnit.SECONDS));
assertTrue(mSecurityController.hasCACertInCurrentUser());
// Exception
- mStateChangedLatch = new CountDownLatch(1);
-
when(mKeyChainService.getUserCaAliases())
.thenThrow(new AssertionError("Test AssertionError"))
.thenReturn(new StringParceledListSlice(new ArrayList<String>()));
- mSecurityController.new CACertLoader()
- .execute(0);
+ refreshCACerts(0);
+ mBgExecutor.runAllReady();
- assertFalse(mStateChangedLatch.await(1, TimeUnit.SECONDS));
assertTrue(mSecurityController.hasCACertInCurrentUser());
- mSecurityController.new CACertLoader()
- .execute(0);
+ refreshCACerts(0);
+ mBgExecutor.runAllReady();
- assertTrue(mStateChangedLatch.await(1, TimeUnit.SECONDS));
assertFalse(mSecurityController.hasCACertInCurrentUser());
}
@@ -197,4 +185,13 @@
&& request.networkCapabilities.getCapabilities().length == 0
), any(NetworkCallback.class));
}
+
+ /**
+ * refresh CA certs by sending a user unlocked broadcast for the desired user
+ */
+ private void refreshCACerts(int userId) {
+ Intent intent = new Intent(Intent.ACTION_USER_UNLOCKED);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ mBroadcastReceiver.onReceive(mContext, intent);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
index c761a44..e4e0dc9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
@@ -22,7 +22,6 @@
import android.app.RemoteInput;
import android.os.Handler;
-import android.os.Looper;
import android.provider.DeviceConfig;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -32,8 +31,8 @@
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.DeviceConfigProxyFake;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -42,14 +41,13 @@
@TestableLooper.RunWithLooper
@SmallTest
public class SmartReplyConstantsTest extends SysuiTestCase {
-
- private static final int CONTENT_OBSERVER_TIMEOUT_SECONDS = 10;
-
private SmartReplyConstants mConstants;
+ private DeviceConfigProxyFake mDeviceConfig;
+ private TestableLooper mTestableLooper;
@Before
public void setUp() {
- resetAllDeviceConfigFlags();
+ mDeviceConfig = new DeviceConfigProxyFake();
TestableResources resources = mContext.getOrCreateTestableResources();
resources.addOverride(R.bool.config_smart_replies_in_notifications_enabled, true);
resources.addOverride(
@@ -62,12 +60,12 @@
2);
resources.addOverride(
R.integer.config_smart_replies_in_notifications_max_num_actions, -1);
- mConstants = new SmartReplyConstants(Handler.createAsync(Looper.myLooper()), mContext);
- }
-
- @After
- public void tearDown() {
- resetAllDeviceConfigFlags();
+ mTestableLooper = TestableLooper.get(this);
+ mConstants = new SmartReplyConstants(
+ new Handler(mTestableLooper.getLooper()),
+ mContext,
+ mDeviceConfig
+ );
}
@Test
@@ -78,25 +76,21 @@
@Test
public void testIsEnabledWithInvalidConfig() {
overrideSetting(SystemUiDeviceConfigFlags.SSIN_ENABLED, "invalid config");
- triggerConstantsOnChange();
assertTrue(mConstants.isEnabled());
}
@Test
public void testIsEnabledWithValidConfig() {
overrideSetting(SystemUiDeviceConfigFlags.SSIN_ENABLED, "false");
- triggerConstantsOnChange();
assertFalse(mConstants.isEnabled());
}
@Test
public void testRequiresTargetingPConfig() {
overrideSetting(SystemUiDeviceConfigFlags.SSIN_REQUIRES_TARGETING_P, "false");
- triggerConstantsOnChange();
assertEquals(false, mConstants.requiresTargetingP());
overrideSetting(SystemUiDeviceConfigFlags.SSIN_REQUIRES_TARGETING_P, null);
- triggerConstantsOnChange();
assertEquals(true, mConstants.requiresTargetingP());
}
@@ -110,20 +104,17 @@
public void testGetMaxSqueezeRemeasureAttemptsWithInvalidConfig() {
overrideSetting(SystemUiDeviceConfigFlags.SSIN_MAX_SQUEEZE_REMEASURE_ATTEMPTS,
"invalid config");
- triggerConstantsOnChange();
assertEquals(7, mConstants.getMaxSqueezeRemeasureAttempts());
}
@Test
public void testGetMaxSqueezeRemeasureAttemptsWithValidConfig() {
overrideSetting(SystemUiDeviceConfigFlags.SSIN_MAX_SQUEEZE_REMEASURE_ATTEMPTS, "5");
- triggerConstantsOnChange();
assertEquals(5, mConstants.getMaxSqueezeRemeasureAttempts());
}
@Test
public void testGetEffectiveEditChoicesBeforeSendingWithNoConfig() {
- triggerConstantsOnChange();
assertFalse(
mConstants.getEffectiveEditChoicesBeforeSending(
RemoteInput.EDIT_CHOICES_BEFORE_SENDING_AUTO));
@@ -138,7 +129,6 @@
@Test
public void testGetEffectiveEditChoicesBeforeSendingWithEnabledConfig() {
overrideSetting(SystemUiDeviceConfigFlags.SSIN_EDIT_CHOICES_BEFORE_SENDING, "true");
- triggerConstantsOnChange();
assertTrue(
mConstants.getEffectiveEditChoicesBeforeSending(
RemoteInput.EDIT_CHOICES_BEFORE_SENDING_AUTO));
@@ -153,7 +143,6 @@
@Test
public void testGetEffectiveEditChoicesBeforeSendingWithDisabledConfig() {
overrideSetting(SystemUiDeviceConfigFlags.SSIN_EDIT_CHOICES_BEFORE_SENDING, "false");
- triggerConstantsOnChange();
assertFalse(
mConstants.getEffectiveEditChoicesBeforeSending(
RemoteInput.EDIT_CHOICES_BEFORE_SENDING_AUTO));
@@ -174,14 +163,12 @@
@Test
public void testShowInHeadsUpEnabled() {
overrideSetting(SystemUiDeviceConfigFlags.SSIN_SHOW_IN_HEADS_UP, "true");
- triggerConstantsOnChange();
assertTrue(mConstants.getShowInHeadsUp());
}
@Test
public void testShowInHeadsUpDisabled() {
overrideSetting(SystemUiDeviceConfigFlags.SSIN_SHOW_IN_HEADS_UP, "false");
- triggerConstantsOnChange();
assertFalse(mConstants.getShowInHeadsUp());
}
@@ -194,7 +181,6 @@
@Test
public void testGetMinNumSystemGeneratedRepliesWithValidConfig() {
overrideSetting(SystemUiDeviceConfigFlags.SSIN_MIN_NUM_SYSTEM_GENERATED_REPLIES, "5");
- triggerConstantsOnChange();
assertEquals(5, mConstants.getMinNumSystemGeneratedReplies());
}
@@ -207,7 +193,6 @@
@Test
public void testMaxNumActionsSet() {
overrideSetting(SystemUiDeviceConfigFlags.SSIN_MAX_NUM_ACTIONS, "10");
- triggerConstantsOnChange();
assertEquals(10, mConstants.getMaxNumActions());
}
@@ -219,38 +204,12 @@
@Test
public void testOnClickInitDelaySet() {
overrideSetting(SystemUiDeviceConfigFlags.SSIN_ONCLICK_INIT_DELAY, "50");
- triggerConstantsOnChange();
assertEquals(50, mConstants.getOnClickInitDelay());
}
private void overrideSetting(String propertyName, String value) {
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
+ mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
propertyName, value, false /* makeDefault */);
- }
-
- private void triggerConstantsOnChange() {
- mConstants.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
- }
-
- private void resetAllDeviceConfigFlags() {
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.SSIN_ENABLED, null, false /* makeDefault */);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.SSIN_REQUIRES_TARGETING_P, null, false /* makeDefault */);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.SSIN_MAX_SQUEEZE_REMEASURE_ATTEMPTS, null,
- false /* makeDefault */);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.SSIN_EDIT_CHOICES_BEFORE_SENDING, null,
- false /* makeDefault */);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.SSIN_SHOW_IN_HEADS_UP, null, false /* makeDefault */);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.SSIN_MIN_NUM_SYSTEM_GENERATED_REPLIES, null,
- false /* makeDefault */);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.SSIN_MAX_NUM_ACTIONS, null, false /* makeDefault */);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.SSIN_ONCLICK_INIT_DELAY, null, false /* makeDefault */);
+ mTestableLooper.processAllMessages();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
index d58f2c9..bc3a5b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
@@ -105,6 +105,33 @@
assertThat(windowParams.packageName).isEqualTo(mContext.getPackageName());
assertThat(windowParams.getTitle()).isEqualTo("Toast");
assertThat(windowParams.token).isEqualTo(WINDOW_TOKEN_1);
+ assertThat(windowParams.privateFlags
+ & WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS).isEqualTo(0);
+ }
+
+ @Test
+ public void testShowToast_forAndroidPackage_addsAllUserFlag() throws Exception {
+ mToastUI.showToast("android", TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, null);
+
+ verify(mWindowManager).addView(any(), mParamsCaptor.capture());
+ ViewGroup.LayoutParams params = mParamsCaptor.getValue();
+ assertThat(params).isInstanceOf(WindowManager.LayoutParams.class);
+ WindowManager.LayoutParams windowParams = (WindowManager.LayoutParams) params;
+ assertThat(windowParams.privateFlags
+ & WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS).isNotEqualTo(0);
+ }
+
+ @Test
+ public void testShowToast_forSystemUiPackage_addsAllUserFlag() throws Exception {
+ mToastUI.showToast("com.android.systemui", TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
+ null);
+
+ verify(mWindowManager).addView(any(), mParamsCaptor.capture());
+ ViewGroup.LayoutParams params = mParamsCaptor.getValue();
+ assertThat(params).isInstanceOf(WindowManager.LayoutParams.class);
+ WindowManager.LayoutParams windowParams = (WindowManager.LayoutParams) params;
+ assertThat(windowParams.privateFlags
+ & WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS).isNotEqualTo(0);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java
index 54cb0b8..31d884c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java
@@ -20,11 +20,9 @@
public class FakeProximitySensor extends ProximitySensor {
private boolean mAvailable;
- private boolean mPaused;
public FakeProximitySensor(Resources resources, AsyncSensorManager sensorManager) {
super(resources, sensorManager);
-
mAvailable = true;
}
diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp
index 3111ab7..0c37235 100644
--- a/packages/Tethering/Android.bp
+++ b/packages/Tethering/Android.bp
@@ -95,11 +95,7 @@
// TODO (b/148190005): change to module-libs-api-stubs-current once it is ready.
sdk_version: "core_platform",
privileged: true,
- // Build system doesn't track transitive dependeicies for jni_libs, list all the dependencies
- // explicitly.
jni_libs: [
- "liblog",
- "libnativehelper_compat_libc++",
"libtetherutilsjni",
],
resource_dirs: [
diff --git a/packages/Tethering/apex/Android.bp b/packages/Tethering/apex/Android.bp
index 94ef11c..96a4d20 100644
--- a/packages/Tethering/apex/Android.bp
+++ b/packages/Tethering/apex/Android.bp
@@ -16,6 +16,7 @@
apex {
name: "com.android.tethering",
+ updatable: true,
java_libs: ["framework-tethering"],
apps: ["Tethering"],
manifest: "manifest.json",
diff --git a/packages/overlays/IconShapeFlowerOverlay/Android.mk b/packages/overlays/IconShapeFlowerOverlay/Android.mk
new file mode 100644
index 0000000..d410bb7
--- /dev/null
+++ b/packages/overlays/IconShapeFlowerOverlay/Android.mk
@@ -0,0 +1,29 @@
+#
+# Copyright 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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := IconShapeFlower
+
+LOCAL_PRODUCT_MODULE := true
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := IconShapeFlowerOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/IconShapeFlowerOverlay/AndroidManifest.xml b/packages/overlays/IconShapeFlowerOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..9d20c6b
--- /dev/null
+++ b/packages/overlays/IconShapeFlowerOverlay/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.theme.icon.flower"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <overlay
+ android:targetPackage="android"
+ android:category="android.theme.customization.adaptive_icon_shape"
+ android:priority="1"/>
+
+ <application android:label="@string/icon_shape_flower_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/IconShapeFlowerOverlay/res/values/config.xml b/packages/overlays/IconShapeFlowerOverlay/res/values/config.xml
new file mode 100644
index 0000000..73f4f21
--- /dev/null
+++ b/packages/overlays/IconShapeFlowerOverlay/res/values/config.xml
@@ -0,0 +1,27 @@
+<?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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Specifies the path that is used by AdaptiveIconDrawable class to crop launcher icons. -->
+ <string name="config_icon_mask" translatable="false">"M50,0 C60.6,0 69.9,5.3 75.6,13.5 78.5,17.8 82.3,21.5 86.6,24.5 94.7,30.1 100,39.4 100,50 100,60.6 94.7,69.9 86.5,75.6 82.2,78.5 78.5,82.3 75.5,86.6 69.9,94.7 60.6,100 50,100 39.4,100 30.1,94.7 24.4,86.5 21.5,82.2 17.7,78.5 13.4,75.5 5.3,69.9 0,60.6 0,50 0,39.4 5.3,30.1 13.5,24.4 17.8,21.5 21.5,17.7 24.5,13.4 30.1,5.3 39.4,0 50,0 Z"</string>
+ <!-- Flag indicating whether round icons should be parsed from the application manifest. -->
+ <bool name="config_useRoundIcon">false</bool>
+ <!-- Corner radius of system dialogs -->
+ <dimen name="config_dialogCornerRadius">8dp</dimen>
+ <!-- Corner radius for bottom sheet system dialogs -->
+ <dimen name="config_bottomDialogCornerRadius">16dp</dimen>
+
+</resources>
diff --git a/packages/overlays/IconShapeFlowerOverlay/res/values/strings.xml b/packages/overlays/IconShapeFlowerOverlay/res/values/strings.xml
new file mode 100644
index 0000000..47c1479
--- /dev/null
+++ b/packages/overlays/IconShapeFlowerOverlay/res/values/strings.xml
@@ -0,0 +1,21 @@
+<?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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Flower icon overlay -->
+ <string name="icon_shape_flower_overlay" translatable="false">Flower</string>
+
+</resources>
diff --git a/packages/overlays/IconShapeHexagonOverlay/Android.mk b/packages/overlays/IconShapeHexagonOverlay/Android.mk
new file mode 100644
index 0000000..16ef399
--- /dev/null
+++ b/packages/overlays/IconShapeHexagonOverlay/Android.mk
@@ -0,0 +1,29 @@
+#
+# Copyright 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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := IconShapeHexagon
+
+LOCAL_PRODUCT_MODULE := true
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := IconShapeHexagonOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/IconShapeHexagonOverlay/AndroidManifest.xml b/packages/overlays/IconShapeHexagonOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..bf408fd
--- /dev/null
+++ b/packages/overlays/IconShapeHexagonOverlay/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.theme.icon.hexagon"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <overlay
+ android:targetPackage="android"
+ android:category="android.theme.customization.adaptive_icon_shape"
+ android:priority="1"/>
+
+ <application android:label="@string/icon_shape_hexagon_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/IconShapeHexagonOverlay/res/values/config.xml b/packages/overlays/IconShapeHexagonOverlay/res/values/config.xml
new file mode 100644
index 0000000..f7cb595
--- /dev/null
+++ b/packages/overlays/IconShapeHexagonOverlay/res/values/config.xml
@@ -0,0 +1,27 @@
+<?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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Specifies the path that is used by AdaptiveIconDrawable class to crop launcher icons. -->
+ <string name="config_icon_mask" translatable="false">"M12,0 88,0 100,50 88,100 12,100 0,50 12,0 Z"</string>
+ <!-- Flag indicating whether round icons should be parsed from the application manifest. -->
+ <bool name="config_useRoundIcon">false</bool>
+ <!-- Corner radius of system dialogs -->
+ <dimen name="config_dialogCornerRadius">0dp</dimen>
+ <!-- Corner radius for bottom sheet system dialogs -->
+ <dimen name="config_bottomDialogCornerRadius">0dp</dimen>
+
+</resources>
diff --git a/packages/overlays/IconShapeHexagonOverlay/res/values/strings.xml b/packages/overlays/IconShapeHexagonOverlay/res/values/strings.xml
new file mode 100644
index 0000000..e00dc9d
--- /dev/null
+++ b/packages/overlays/IconShapeHexagonOverlay/res/values/strings.xml
@@ -0,0 +1,20 @@
+<!--
+ ~ 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Hexagon icon overlay -->
+ <string name="icon_shape_hexagon_overlay" translatable="false">Hexagon</string>
+
+</resources>
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index c5a5d34..34d2b73 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -252,9 +252,6 @@
// Package: android
NOTE_ID_WIFI_SIM_REQUIRED = 60;
- // Inform the user a foreground service while-in-use permission is restricted.
- NOTE_FOREGROUND_SERVICE_WHILE_IN_USE_PERMISSION = 61;
-
// Display the Android Debug Protocol status
// Package: android
NOTE_ADB_WIFI_ACTIVE = 62;
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index b78d024..ad21075 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -641,8 +641,8 @@
final SuspendDialogInfo dialogInfo =
mPackageManagerInternal.getSuspendedDialogInfo(providerPackage,
suspendingPackage, providerUserId);
- // TODO(b/148035643): Send the original widget intent or ACTION_MAIN as an
- // IntentSender to SuspendedAppActivity.
+ // onUnsuspend is null because we don't want to start any activity on
+ // unsuspending from a suspended widget.
onClickIntent = SuspendedAppActivity.createSuspendedAppInterceptIntent(
providerPackage, suspendingPackage, dialogInfo, null, null,
providerUserId);
diff --git a/services/art-profile b/services/art-profile
index 1f53ebd..ae3f6d1 100644
--- a/services/art-profile
+++ b/services/art-profile
@@ -42281,7 +42281,6 @@
Lcom/android/server/incident/RequestQueue$Rec;
Lcom/android/server/incident/RequestQueue;
Lcom/android/server/incremental/IncrementalManagerService;
-Lcom/android/server/incremental/IncrementalManagerShellCommand;
Lcom/android/server/infra/-$$Lambda$AbstractMasterSystemService$1$TLhe3_2yHs5UB69Y7lf2s7OxJCo;
Lcom/android/server/infra/-$$Lambda$AbstractMasterSystemService$_fKw-VUP0pSfcMMlgRqoT4OPhxw;
Lcom/android/server/infra/-$$Lambda$AbstractMasterSystemService$su3lJpEVIbL-C7doP4eboTpqjxU;
diff --git a/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java b/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
index 6daa106..9412449 100644
--- a/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
+++ b/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
@@ -24,8 +24,10 @@
import android.os.Bundle;
import android.os.RemoteException;
import android.util.Log;
+import android.util.Slog;
import android.view.autofill.AutofillId;
import android.view.inputmethod.InlineSuggestionsRequest;
+import android.view.inputmethod.InlineSuggestionsResponse;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
@@ -33,6 +35,8 @@
import com.android.internal.view.InlineSuggestionsRequestInfo;
import com.android.server.inputmethod.InputMethodManagerInternal;
+import java.util.Collections;
+import java.util.Optional;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
@@ -40,9 +44,16 @@
import java.util.concurrent.TimeoutException;
/**
- * Maintains an inline suggestion autofill session.
+ * Maintains an autofill inline suggestion session that communicates with the IME.
*
- * <p> This class is thread safe.
+ * <p>
+ * The same session may be reused for multiple input fields involved in the same autofill
+ * {@link Session}. Therefore, one {@link InlineSuggestionsRequest} and one
+ * {@link IInlineSuggestionsResponseCallback} may be used to generate and callback with inline
+ * suggestions for different input fields.
+ *
+ * <p>
+ * This class is thread safe.
*/
final class InlineSuggestionSession {
@@ -61,6 +72,9 @@
@Nullable
private CompletableFuture<ImeResponse> mPendingImeResponse;
+ @GuardedBy("mLock")
+ private boolean mIsLastResponseNonEmpty = false;
+
InlineSuggestionSession(InputMethodManagerInternal inputMethodManagerInternal,
int userId, ComponentName componentName) {
mInputMethodManagerInternal = inputMethodManagerInternal;
@@ -69,26 +83,28 @@
mLock = new Object();
}
- public void createRequest(@NonNull AutofillId currentViewId) {
+ public void onCreateInlineSuggestionsRequest(@NonNull AutofillId autofillId) {
+ if (sDebug) Log.d(TAG, "onCreateInlineSuggestionsRequest called for " + autofillId);
+
synchronized (mLock) {
cancelCurrentRequest();
mPendingImeResponse = new CompletableFuture<>();
// TODO(b/146454892): pipe the uiExtras from the ExtServices.
mInputMethodManagerInternal.onCreateInlineSuggestionsRequest(
mUserId,
- new InlineSuggestionsRequestInfo(mComponentName, currentViewId, new Bundle()),
+ new InlineSuggestionsRequestInfo(mComponentName, autofillId, new Bundle()),
new InlineSuggestionsRequestCallbackImpl(mPendingImeResponse));
}
}
- @Nullable
- public ImeResponse waitAndGetImeResponse() {
- CompletableFuture<ImeResponse> pendingImeResponse = getPendingImeResponse();
- if (pendingImeResponse == null || pendingImeResponse.isCancelled()) {
- return null;
+ public Optional<InlineSuggestionsRequest> waitAndGetInlineSuggestionsRequest() {
+ final CompletableFuture<ImeResponse> pendingImeResponse = getPendingImeResponse();
+ if (pendingImeResponse == null) {
+ return Optional.empty();
}
try {
- return pendingImeResponse.get(INLINE_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ return Optional.ofNullable(pendingImeResponse.get(INLINE_REQUEST_TIMEOUT_MS,
+ TimeUnit.MILLISECONDS)).map(ImeResponse::getRequest);
} catch (TimeoutException e) {
Log.w(TAG, "Exception getting inline suggestions request in time: " + e);
} catch (CancellationException e) {
@@ -96,13 +112,59 @@
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
- return null;
+ return Optional.empty();
+ }
+
+ public boolean hideInlineSuggestionsUi(@NonNull AutofillId autofillId) {
+ if (sDebug) Log.d(TAG, "Called hideInlineSuggestionsUi for " + autofillId);
+ synchronized (mLock) {
+ if (mIsLastResponseNonEmpty) {
+ if (sDebug) Log.d(TAG, "Send empty suggestion to IME");
+ return onInlineSuggestionsResponseLocked(autofillId,
+ new InlineSuggestionsResponse(Collections.EMPTY_LIST));
+ }
+ return false;
+ }
+ }
+
+ public boolean onInlineSuggestionsResponse(@NonNull AutofillId autofillId,
+ @NonNull InlineSuggestionsResponse inlineSuggestionsResponse) {
+ synchronized (mLock) {
+ return onInlineSuggestionsResponseLocked(autofillId, inlineSuggestionsResponse);
+ }
+ }
+
+ private boolean onInlineSuggestionsResponseLocked(@NonNull AutofillId autofillId,
+ @NonNull InlineSuggestionsResponse inlineSuggestionsResponse) {
+ final CompletableFuture<ImeResponse> completedImsResponse = getPendingImeResponse();
+ if (completedImsResponse == null || !completedImsResponse.isDone()) {
+ return false;
+ }
+ // There is no need to wait on the CompletableFuture since it should have been completed
+ // when {@link #waitAndGetInlineSuggestionsRequest()} was called.
+ ImeResponse imeResponse = completedImsResponse.getNow(null);
+ if (imeResponse == null) {
+ return false;
+ }
+ try {
+ imeResponse.mCallback.onInlineSuggestionsResponse(autofillId,
+ inlineSuggestionsResponse);
+ mIsLastResponseNonEmpty = !inlineSuggestionsResponse.getInlineSuggestions().isEmpty();
+ if (sDebug) {
+ Log.d(TAG, "Autofill sends inline response to IME: "
+ + inlineSuggestionsResponse.getInlineSuggestions().size());
+ }
+ return true;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException sending InlineSuggestionsResponse to IME");
+ return false;
+ }
}
private void cancelCurrentRequest() {
CompletableFuture<ImeResponse> pendingImeResponse = getPendingImeResponse();
- if (pendingImeResponse != null) {
- pendingImeResponse.cancel(true);
+ if (pendingImeResponse != null && !pendingImeResponse.isDone()) {
+ pendingImeResponse.complete(null);
}
}
@@ -125,22 +187,18 @@
@Override
public void onInlineSuggestionsUnsupported() throws RemoteException {
- if (sDebug) {
- Log.d(TAG, "onInlineSuggestionsUnsupported() called.");
- }
- mResponse.cancel(true);
+ if (sDebug) Log.d(TAG, "onInlineSuggestionsUnsupported() called.");
+ mResponse.complete(null);
}
@Override
public void onInlineSuggestionsRequest(InlineSuggestionsRequest request,
- IInlineSuggestionsResponseCallback callback) throws RemoteException {
- if (sDebug) {
- Log.d(TAG, "onInlineSuggestionsRequest() received: " + request);
- }
+ IInlineSuggestionsResponseCallback callback) {
+ if (sDebug) Log.d(TAG, "onInlineSuggestionsRequest() received: " + request);
if (request != null && callback != null) {
mResponse.complete(new ImeResponse(request, callback));
} else {
- mResponse.cancel(true);
+ mResponse.complete(null);
}
}
}
@@ -148,12 +206,12 @@
/**
* A data class wrapping IME responses for the inline suggestion request.
*/
- public static class ImeResponse {
+ private static class ImeResponse {
@NonNull
- private final InlineSuggestionsRequest mRequest;
+ final InlineSuggestionsRequest mRequest;
@NonNull
- private final IInlineSuggestionsResponseCallback mCallback;
+ final IInlineSuggestionsResponseCallback mCallback;
ImeResponse(@NonNull InlineSuggestionsRequest request,
@NonNull IInlineSuggestionsResponseCallback callback) {
@@ -161,12 +219,8 @@
mCallback = callback;
}
- public InlineSuggestionsRequest getRequest() {
+ InlineSuggestionsRequest getRequest() {
return mRequest;
}
-
- public IInlineSuggestionsResponseCallback getCallback() {
- return mCallback;
- }
}
}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index 1eb7692..dcc9181 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -37,6 +37,7 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.service.autofill.Dataset;
+import android.service.autofill.InlinePresentation;
import android.service.autofill.augmented.AugmentedAutofillService;
import android.service.autofill.augmented.IAugmentedAutofillService;
import android.service.autofill.augmented.IFillCallback;
@@ -47,20 +48,21 @@
import android.view.autofill.AutofillValue;
import android.view.autofill.IAutoFillManagerClient;
import android.view.inputmethod.InlineSuggestionsRequest;
+import android.view.inputmethod.InlineSuggestionsResponse;
import com.android.internal.infra.AbstractRemoteService;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.infra.ServiceConnector;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.os.IResultReceiver;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.view.IInlineSuggestionsResponseCallback;
import com.android.server.autofill.ui.InlineSuggestionFactory;
+import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Function;
final class RemoteAugmentedAutofillService
extends ServiceConnector.Impl<IAugmentedAutofillService> {
@@ -145,7 +147,7 @@
int taskId, @NonNull ComponentName activityComponent, @NonNull AutofillId focusedId,
@Nullable AutofillValue focusedValue,
@Nullable InlineSuggestionsRequest inlineSuggestionsRequest,
- @Nullable IInlineSuggestionsResponseCallback inlineSuggestionsCallback,
+ @Nullable Function<InlineSuggestionsResponse, Boolean> inlineSuggestionsCallback,
@NonNull Runnable onErrorCallback,
@NonNull RemoteInlineSuggestionRenderService remoteRenderService) {
long requestTime = SystemClock.elapsedRealtime();
@@ -164,12 +166,13 @@
focusedId, focusedValue, requestTime, inlineSuggestionsRequest,
new IFillCallback.Stub() {
@Override
- public void onSuccess(@Nullable Dataset[] inlineSuggestionsData) {
+ public void onSuccess(@Nullable List<Dataset> inlineSuggestionsData,
+ @Nullable List<InlinePresentation> inlineActions) {
mCallbacks.resetLastResponse();
maybeRequestShowInlineSuggestions(sessionId,
inlineSuggestionsRequest, inlineSuggestionsData,
- focusedId, inlineSuggestionsCallback, client,
- onErrorCallback, remoteRenderService);
+ inlineActions, focusedId, inlineSuggestionsCallback,
+ client, onErrorCallback, remoteRenderService);
requestAutofill.complete(null);
}
@@ -232,36 +235,39 @@
}
private void maybeRequestShowInlineSuggestions(int sessionId,
- @Nullable InlineSuggestionsRequest request, @Nullable Dataset[] inlineSuggestionsData,
- @NonNull AutofillId focusedId,
- @Nullable IInlineSuggestionsResponseCallback inlineSuggestionsCallback,
+ @Nullable InlineSuggestionsRequest request,
+ @Nullable List<Dataset> inlineSuggestionsData,
+ @Nullable List<InlinePresentation> inlineActions, @NonNull AutofillId focusedId,
+ @Nullable Function<InlineSuggestionsResponse, Boolean> inlineSuggestionsCallback,
@NonNull IAutoFillManagerClient client, @NonNull Runnable onErrorCallback,
@NonNull RemoteInlineSuggestionRenderService remoteRenderService) {
- if (ArrayUtils.isEmpty(inlineSuggestionsData) || inlineSuggestionsCallback == null
- || request == null) {
+ if (inlineSuggestionsData == null || inlineSuggestionsData.isEmpty()
+ || inlineSuggestionsCallback == null || request == null) {
return;
}
mCallbacks.setLastResponse(sessionId);
- try {
- inlineSuggestionsCallback.onInlineSuggestionsResponse(
- InlineSuggestionFactory.createAugmentedInlineSuggestionsResponse(
- request, inlineSuggestionsData, focusedId, mContext,
- dataset -> {
- mCallbacks.logAugmentedAutofillSelected(sessionId,
- dataset.getId());
- try {
- client.autofill(sessionId, dataset.getFieldIds(),
- dataset.getFieldValues());
- } catch (RemoteException e) {
- Slog.w(TAG, "Encounter exception autofilling the values");
- }
- }, onErrorCallback, remoteRenderService));
- } catch (RemoteException e) {
- Slog.w(TAG, "Exception sending inline suggestions response back to IME.");
- }
+ final InlineSuggestionsResponse inlineSuggestionsResponse =
+ InlineSuggestionFactory.createAugmentedInlineSuggestionsResponse(
+ request, inlineSuggestionsData, inlineActions, focusedId, mContext,
+ dataset -> {
+ mCallbacks.logAugmentedAutofillSelected(sessionId,
+ dataset.getId());
+ try {
+ client.autofill(sessionId, dataset.getFieldIds(),
+ dataset.getFieldValues());
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Encounter exception autofilling the values");
+ }
+ }, onErrorCallback, remoteRenderService);
- mCallbacks.logAugmentedAutofillShown(sessionId);
+ if (inlineSuggestionsResponse == null) {
+ Slog.w(TAG, "InlineSuggestionFactory created null response");
+ return;
+ }
+ if (inlineSuggestionsCallback.apply(inlineSuggestionsResponse)) {
+ mCallbacks.logAugmentedAutofillShown(sessionId);
+ }
}
@Override
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 317ce4c..6fb65ca 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -100,7 +100,6 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.view.IInlineSuggestionsResponseCallback;
import com.android.server.autofill.ui.AutoFillUI;
import com.android.server.autofill.ui.InlineSuggestionFactory;
import com.android.server.autofill.ui.PendingUi;
@@ -113,6 +112,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -403,11 +403,10 @@
final ArrayList<FillContext> contexts =
mergePreviousSessionLocked(/* forSave= */ false);
- final InlineSuggestionSession.ImeResponse imeResponse =
- mInlineSuggestionSession.waitAndGetImeResponse();
-
+ final Optional<InlineSuggestionsRequest> inlineSuggestionsRequest =
+ mInlineSuggestionSession.waitAndGetInlineSuggestionsRequest();
request = new FillRequest(requestId, contexts, mClientState, flags,
- imeResponse != null ? imeResponse.getRequest() : null);
+ inlineSuggestionsRequest.orElse(null));
}
if (mActivityToken != null) {
@@ -605,7 +604,7 @@
private void maybeRequestInlineSuggestionsRequestThenFillLocked(@NonNull ViewState viewState,
int newState, int flags) {
if (isInlineSuggestionsEnabled()) {
- mInlineSuggestionSession.createRequest(mCurrentViewId);
+ mInlineSuggestionSession.onCreateInlineSuggestionsRequest(mCurrentViewId);
}
requestNewFillResponseLocked(viewState, newState, flags);
@@ -1158,18 +1157,6 @@
} catch (RemoteException e) {
Slog.e(TAG, "Error requesting to hide fill UI", e);
}
- try {
- final InlineSuggestionSession.ImeResponse imeResponse =
- mInlineSuggestionSession.waitAndGetImeResponse();
- if (imeResponse == null) {
- Log.w(TAG, "Session input method callback is not set yet");
- return;
- }
- imeResponse.getCallback().onInlineSuggestionsResponse(
- new InlineSuggestionsResponse(Collections.EMPTY_LIST));
- } catch (RemoteException e) {
- Slog.e(TAG, "RemoteException hiding inline suggestions");
- }
}
}
@@ -2501,6 +2488,7 @@
if (sVerbose) Slog.v(TAG, "Exiting view " + id);
mUi.hideFillUi(this);
hideAugmentedAutofillLocked(viewState);
+ mInlineSuggestionSession.hideInlineSuggestionsUi(mCurrentViewId);
mCurrentViewId = null;
}
break;
@@ -2668,32 +2656,27 @@
*/
private boolean requestShowInlineSuggestionsLocked(@NonNull FillResponse response,
@Nullable String filterText) {
- final List<Dataset> datasets = response.getDatasets();
-
- final InlineSuggestionSession.ImeResponse imeResponse =
- mInlineSuggestionSession.waitAndGetImeResponse();
- if (imeResponse == null) {
- Log.w(TAG, "Session input method callback is not set yet");
+ final Optional<InlineSuggestionsRequest> inlineSuggestionsRequest =
+ mInlineSuggestionSession.waitAndGetInlineSuggestionsRequest();
+ if (!inlineSuggestionsRequest.isPresent()) {
+ Log.w(TAG, "InlineSuggestionsRequest unavailable");
return false;
}
-
- final InlineSuggestionsRequest request = imeResponse.getRequest();
InlineSuggestionsResponse inlineSuggestionsResponse =
- InlineSuggestionFactory.createInlineSuggestionsResponse(request,
+ InlineSuggestionFactory.createInlineSuggestionsResponse(
+ inlineSuggestionsRequest.get(),
response, filterText, response.getInlineActions(), mCurrentViewId, mContext,
this, () -> {
synchronized (mLock) {
requestHideFillUi(mCurrentViewId);
}
}, mService.getRemoteInlineSuggestionRenderServiceLocked());
- try {
- imeResponse.getCallback().onInlineSuggestionsResponse(inlineSuggestionsResponse);
- } catch (RemoteException e) {
- Log.w(TAG, "onFillReady() remote error calling onInlineSuggestionsResponse()");
+ if (inlineSuggestionsResponse == null) {
+ Slog.w(TAG, "InlineSuggestionFactory created null response");
return false;
}
-
- return true;
+ return mInlineSuggestionSession.onInlineSuggestionsResponse(mCurrentViewId,
+ inlineSuggestionsResponse);
}
boolean isDestroyed() {
@@ -2976,16 +2959,16 @@
// doesn't want autofill
if (mForAugmentedAutofillOnly || !isInlineSuggestionsEnabled()) {
if (sDebug) Slog.d(TAG, "Create inline request for augmented autofill");
- mInlineSuggestionSession.createRequest(mCurrentViewId);
+ mInlineSuggestionSession.onCreateInlineSuggestionsRequest(mCurrentViewId);
}
- InlineSuggestionSession.ImeResponse imeResponse =
- mInlineSuggestionSession.waitAndGetImeResponse();
- final InlineSuggestionsRequest inlineSuggestionsRequest =
- imeResponse != null ? imeResponse.getRequest() : null;
- final IInlineSuggestionsResponseCallback inlineSuggestionsResponseCallback =
- imeResponse != null ? imeResponse.getCallback() : null;
+
+ Optional<InlineSuggestionsRequest> inlineSuggestionsRequest =
+ mInlineSuggestionSession.waitAndGetInlineSuggestionsRequest();
remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName, focusedId,
- currentValue, inlineSuggestionsRequest, inlineSuggestionsResponseCallback, () -> {
+ currentValue, inlineSuggestionsRequest.orElse(null),
+ response -> mInlineSuggestionSession.onInlineSuggestionsResponse(
+ mCurrentViewId, response),
+ () -> {
synchronized (mLock) {
cancelAugmentedAutofillLocked();
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
index fef49d4..9bf3690 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
@@ -70,6 +70,7 @@
* Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by the
* autofill service, potentially filtering the datasets.
*/
+ @Nullable
public static InlineSuggestionsResponse createInlineSuggestionsResponse(
@NonNull InlineSuggestionsRequest request, @NonNull FillResponse response,
@Nullable String filterText, @Nullable List<InlinePresentation> inlineActions,
@@ -92,23 +93,21 @@
};
}
- final List<Dataset> datasetList = response.getDatasets();
- final Dataset[] datasets = datasetList == null
- ? null
- : datasetList.toArray(new Dataset[]{});
final InlinePresentation inlineAuthentication =
response.getAuthentication() == null ? null : response.getInlinePresentation();
return createInlineSuggestionsResponseInternal(/* isAugmented= */ false, request,
- datasets, filterText, inlineAuthentication, inlineActions, autofillId, context,
- onErrorCallback, onClickFactory, remoteRenderService);
+ response.getDatasets(), filterText, inlineAuthentication, inlineActions, autofillId,
+ context, onErrorCallback, onClickFactory, remoteRenderService);
}
/**
* Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by augmented
* autofill service.
*/
+ @Nullable
public static InlineSuggestionsResponse createAugmentedInlineSuggestionsResponse(
- @NonNull InlineSuggestionsRequest request, @NonNull Dataset[] datasets,
+ @NonNull InlineSuggestionsRequest request, @NonNull List<Dataset> datasets,
+ @Nullable List<InlinePresentation> inlineActions,
@NonNull AutofillId autofillId, @NonNull Context context,
@NonNull InlineSuggestionUiCallback inlineSuggestionUiCallback,
@NonNull Runnable onErrorCallback,
@@ -116,14 +115,15 @@
if (sDebug) Slog.d(TAG, "createAugmentedInlineSuggestionsResponse called");
return createInlineSuggestionsResponseInternal(/* isAugmented= */ true, request,
datasets, /* filterText= */ null, /* inlineAuthentication= */ null,
- /* inlineActions= */ null, autofillId, context, onErrorCallback,
+ inlineActions, autofillId, context, onErrorCallback,
(dataset, datasetIndex) ->
inlineSuggestionUiCallback.autofill(dataset), remoteRenderService);
}
+ @Nullable
private static InlineSuggestionsResponse createInlineSuggestionsResponseInternal(
boolean isAugmented, @NonNull InlineSuggestionsRequest request,
- @Nullable Dataset[] datasets, @Nullable String filterText,
+ @Nullable List<Dataset> datasets, @Nullable String filterText,
@Nullable InlinePresentation inlineAuthentication,
@Nullable List<InlinePresentation> inlineActions, @NonNull AutofillId autofillId,
@NonNull Context context, @NonNull Runnable onErrorCallback,
@@ -145,18 +145,18 @@
return null;
}
- for (int datasetIndex = 0; datasetIndex < datasets.length; datasetIndex++) {
- final Dataset dataset = datasets[datasetIndex];
+ for (int datasetIndex = 0; datasetIndex < datasets.size(); datasetIndex++) {
+ final Dataset dataset = datasets.get(datasetIndex);
final int fieldIndex = dataset.getFieldIds().indexOf(autofillId);
if (fieldIndex < 0) {
Slog.w(TAG, "AutofillId=" + autofillId + " not found in dataset");
- return null;
+ continue;
}
final InlinePresentation inlinePresentation = dataset.getFieldInlinePresentation(
fieldIndex);
if (inlinePresentation == null) {
Slog.w(TAG, "InlinePresentation not found in dataset");
- return null;
+ continue;
}
if (!includeDataset(dataset, fieldIndex, filterText)) {
continue;
@@ -169,7 +169,8 @@
inlineSuggestions.add(inlineSuggestion);
}
- if (inlineActions != null) {
+ // We should only add inline actions if there is at least one suggestion.
+ if (!inlineSuggestions.isEmpty() && inlineActions != null) {
for (InlinePresentation inlinePresentation : inlineActions) {
final InlineSuggestion inlineAction = createInlineAction(isAugmented, context,
mergedInlinePresentation(request, 0, inlinePresentation),
@@ -219,12 +220,12 @@
@Nullable RemoteInlineSuggestionRenderService remoteRenderService,
@NonNull Runnable onErrorCallback, @Nullable IBinder hostInputToken,
int displayId) {
- // TODO(b/146453195): fill in the autofill hint properly.
final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
inlinePresentation.getInlinePresentationSpec(),
isAugmented ? InlineSuggestionInfo.SOURCE_PLATFORM
- : InlineSuggestionInfo.SOURCE_AUTOFILL, new String[]{""},
- InlineSuggestionInfo.TYPE_ACTION);
+ : InlineSuggestionInfo.SOURCE_AUTOFILL,
+ inlinePresentation.getAutofillHints(),
+ InlineSuggestionInfo.TYPE_ACTION, inlinePresentation.isPinned());
final Runnable onClickAction = () -> {
Toast.makeText(context, "icon clicked", Toast.LENGTH_SHORT).show();
};
@@ -240,12 +241,12 @@
@NonNull RemoteInlineSuggestionRenderService remoteRenderService,
@NonNull Runnable onErrorCallback, @Nullable IBinder hostInputToken,
int displayId) {
- // TODO(b/146453195): fill in the autofill hint properly.
final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
inlinePresentation.getInlinePresentationSpec(),
isAugmented ? InlineSuggestionInfo.SOURCE_PLATFORM
- : InlineSuggestionInfo.SOURCE_AUTOFILL, new String[]{""},
- InlineSuggestionInfo.TYPE_SUGGESTION);
+ : InlineSuggestionInfo.SOURCE_AUTOFILL,
+ inlinePresentation.getAutofillHints(),
+ InlineSuggestionInfo.TYPE_SUGGESTION, inlinePresentation.isPinned());
final InlineSuggestion inlineSuggestion = new InlineSuggestion(inlineSuggestionInfo,
createInlineContentProvider(inlinePresentation,
@@ -262,7 +263,8 @@
@Nullable IBinder hostInputToken, int displayId) {
final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
inlinePresentation.getInlinePresentationSpec(),
- InlineSuggestionInfo.SOURCE_AUTOFILL, null, InlineSuggestionInfo.TYPE_SUGGESTION);
+ InlineSuggestionInfo.SOURCE_AUTOFILL, inlinePresentation.getAutofillHints(),
+ InlineSuggestionInfo.TYPE_SUGGESTION, inlinePresentation.isPinned());
return new InlineSuggestion(inlineSuggestionInfo,
createInlineContentProvider(inlinePresentation,
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 434a97e..6ccdf24 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -369,10 +369,13 @@
boolean bypassMacPermission = getContext().getPackageManager().checkPermission(
android.Manifest.permission.COMPANION_APPROVE_WIFI_CONNECTIONS, packageName)
== PackageManager.PERMISSION_GRANTED;
+ if (bypassMacPermission) {
+ return true;
+ }
return CollectionUtils.any(
readAllAssociations(userId, packageName),
- a -> bypassMacPermission || Objects.equals(a.deviceAddress, macAddress));
+ a -> Objects.equals(a.deviceAddress, macAddress));
}
private void checkCanCallNotificationApi(String callingPackage) throws RemoteException {
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 4da60b8..84ce34b 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -99,6 +99,7 @@
"android.hardware.vibrator-java",
"app-compat-annotations",
"framework-tethering-stubs",
+ "ike-stubs",
],
required: [
@@ -127,6 +128,7 @@
"dnsresolver_aidl_interface-V2-java",
"netd_event_listener_interface-java",
"ike-stubs",
+ "overlayable_policy_aidl-java",
],
plugins: [
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 1b1e06a..485127a 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -845,18 +845,11 @@
"android.content.pm.extra.ENABLE_ROLLBACK_TOKEN";
/**
- * Extra field name for the installFlags of a request to enable rollback
+ * Extra field name for the session id of a request to enable rollback
* for a package.
*/
- public static final String EXTRA_ENABLE_ROLLBACK_INSTALL_FLAGS =
- "android.content.pm.extra.ENABLE_ROLLBACK_INSTALL_FLAGS";
-
- /**
- * Extra field name for the user id an install is associated with when
- * enabling rollback.
- */
- public static final String EXTRA_ENABLE_ROLLBACK_USER =
- "android.content.pm.extra.ENABLE_ROLLBACK_USER";
+ public static final String EXTRA_ENABLE_ROLLBACK_SESSION_ID =
+ "android.content.pm.extra.ENABLE_ROLLBACK_SESSION_ID";
/**
* Used as the {@code enableRollbackCode} argument for
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 3441a5f..7840b19 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -1670,6 +1670,7 @@
Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
| Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
+ | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
intent.putExtra("time-zone", zone.getID());
getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java
index 7ec2a34..dbf179a 100644
--- a/services/core/java/com/android/server/IntentResolver.java
+++ b/services/core/java/com/android/server/IntentResolver.java
@@ -687,7 +687,7 @@
}
private final void remove_all_objects(ArrayMap<String, F[]> map, String name,
- Object object) {
+ F object) {
F[] array = map.get(name);
if (array != null) {
int LAST = array.length-1;
@@ -695,7 +695,8 @@
LAST--;
}
for (int idx=LAST; idx>=0; idx--) {
- if (array[idx] == object) {
+ F arrayValue = array[idx];
+ if (arrayValue != null && getIntentFilter(arrayValue) == getIntentFilter(object)) {
final int remain = LAST - idx;
if (remain > 0) {
System.arraycopy(array, idx+1, array, idx, remain);
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 6763c51..f619d69 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -98,7 +98,8 @@
private int mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
private int mNightMode = UiModeManager.MODE_NIGHT_NO;
// we use the override auto mode
- // for example: force night mode off in the night time while in auto mode
+ // for example: force night mode off in the night time
+ // while in auto mode
private int mNightModeOverride = mNightMode;
private final LocalTime DEFAULT_CUSTOM_NIGHT_START_TIME = LocalTime.of(22, 0);
private final LocalTime DEFAULT_CUSTOM_NIGHT_END_TIME = LocalTime.of(6, 0);
@@ -152,6 +153,7 @@
private PowerManager.WakeLock mWakeLock;
private final LocalService mLocalService = new LocalService();
+ private PowerManagerInternal mLocalPowerManager;
public UiModeManagerService(Context context) {
super(context);
@@ -160,6 +162,7 @@
@VisibleForTesting
protected UiModeManagerService(Context context, WindowManagerInternal wm, AlarmManager am,
PowerManager pm, PowerManager.WakeLock wl, TwilightManager tm,
+ PowerManagerInternal localPowerManager,
boolean setupWizardComplete) {
super(context);
mWindowManager = wm;
@@ -168,6 +171,8 @@
mSetupWizardComplete = setupWizardComplete;
mAlarmManager = am;
mPowerManager = pm;
+ mLocalPowerManager = localPowerManager;
+ initPowerSave();
}
private static Intent buildHomeIntent(String category) {
@@ -230,11 +235,11 @@
@Override
public void onTwilightStateChanged(@Nullable TwilightState state) {
synchronized (mLock) {
- if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
+ if (mNightMode == UiModeManager.MODE_NIGHT_AUTO && mSystemReady) {
if (mCar) {
updateLocked(0, 0);
} else {
- registerScreenOffEvent();
+ registerScreenOffEventLocked();
}
}
}
@@ -250,7 +255,7 @@
public void onReceive(Context context, Intent intent) {
synchronized (mLock) {
// must unregister first before updating
- unregisterScreenOffEvent();
+ unregisterScreenOffEventLocked();
updateLocked(0, 0);
}
}
@@ -326,8 +331,7 @@
public void onStart() {
final Context context = getContext();
- mPowerManager =
- (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mWakeLock = mPowerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
mWindowManager = LocalServices.getService(WindowManagerInternal.class);
mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
@@ -341,21 +345,11 @@
IntentFilter batteryFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
context.registerReceiver(mBatteryReceiver, batteryFilter);
- PowerManagerInternal localPowerManager =
+ mLocalPowerManager =
LocalServices.getService(PowerManagerInternal.class);
- mPowerSave = localPowerManager.getLowPowerState(ServiceType.NIGHT_MODE).batterySaverEnabled;
- localPowerManager.registerLowPowerModeObserver(ServiceType.NIGHT_MODE,
- state -> {
- synchronized (mLock) {
- if (mPowerSave == state.batterySaverEnabled) {
- return;
- }
- mPowerSave = state.batterySaverEnabled;
- if (mSystemReady) {
- updateLocked(0, 0);
- }
- }
- });
+ initPowerSave();
+
+ mTwilightManager = getLocalService(TwilightManager.class);
mConfiguration.setToDefaults();
@@ -396,7 +390,24 @@
context.getContentResolver().registerContentObserver(Secure.getUriFor(Secure.UI_NIGHT_MODE),
false, mDarkThemeObserver, 0);
- updateSystemProperties();
+ mHandler.post(() -> updateSystemProperties());
+ }
+
+ private void initPowerSave() {
+ mPowerSave =
+ mLocalPowerManager.getLowPowerState(ServiceType.NIGHT_MODE)
+ .batterySaverEnabled;
+ mLocalPowerManager.registerLowPowerModeObserver(ServiceType.NIGHT_MODE, state -> {
+ synchronized (mLock) {
+ if (mPowerSave == state.batterySaverEnabled) {
+ return;
+ }
+ mPowerSave = state.batterySaverEnabled;
+ if (mSystemReady) {
+ updateLocked(0, 0);
+ }
+ }
+ });
}
@VisibleForTesting
@@ -433,7 +444,7 @@
if (shouldApplyAutomaticChangesImmediately()) {
updateLocked(0, 0);
} else {
- registerScreenOffEvent();
+ registerScreenOffEventLocked();
}
scheduleNextCustomTimeListener();
}
@@ -473,7 +484,8 @@
return oldNightMode != mNightMode;
}
- private void registerScreenOffEvent() {
+ private void registerScreenOffEventLocked() {
+ if (mPowerSave) return;
mWaitForScreenOff = true;
final IntentFilter intentFilter =
new IntentFilter(Intent.ACTION_SCREEN_OFF);
@@ -484,7 +496,7 @@
mAlarmManager.cancel(mCustomTimeListener);
}
- private void unregisterScreenOffEvent() {
+ private void unregisterScreenOffEventLocked() {
mWaitForScreenOff = false;
try {
getContext().unregisterReceiver(mOnScreenOffHandler);
@@ -630,7 +642,7 @@
synchronized (mLock) {
if (mNightMode != mode) {
if (mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM) {
- unregisterScreenOffEvent();
+ unregisterScreenOffEventLocked();
cancelCustomAlarm();
}
@@ -643,10 +655,10 @@
// on screen off will update configuration instead
if ((mNightMode != MODE_NIGHT_AUTO && mNightMode != MODE_NIGHT_CUSTOM)
|| shouldApplyAutomaticChangesImmediately()) {
- unregisterScreenOffEvent();
+ unregisterScreenOffEventLocked();
updateLocked(0, 0);
} else {
- registerScreenOffEvent();
+ registerScreenOffEventLocked();
}
}
}
@@ -695,7 +707,7 @@
final long ident = Binder.clearCallingIdentity();
try {
if (mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM) {
- unregisterScreenOffEvent();
+ unregisterScreenOffEventLocked();
mNightModeOverride = active
? UiModeManager.MODE_NIGHT_YES : UiModeManager.MODE_NIGHT_NO;
} else if (mNightMode == UiModeManager.MODE_NIGHT_NO
@@ -737,7 +749,7 @@
persistNightMode(user);
onCustomTimeUpdated(user);
} catch (DateTimeException e) {
- unregisterScreenOffEvent();
+ unregisterScreenOffEventLocked();
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -764,7 +776,7 @@
mCustomAutoNightModeEndMilliseconds = newTime;
onCustomTimeUpdated(user);
} catch (DateTimeException e) {
- unregisterScreenOffEvent();
+ unregisterScreenOffEventLocked();
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -775,10 +787,10 @@
persistNightMode(user);
if (mNightMode != MODE_NIGHT_CUSTOM) return;
if (shouldApplyAutomaticChangesImmediately()) {
- unregisterScreenOffEvent();
+ unregisterScreenOffEventLocked();
updateLocked(0, 0);
} else {
- registerScreenOffEvent();
+ registerScreenOffEventLocked();
}
}
@@ -788,6 +800,7 @@
pw.print(" mDockState="); pw.print(mDockState);
pw.print(" mLastBroadcastState="); pw.println(mLastBroadcastState);
+ pw.print(" mNightModeOverride="); pw.print(mNightModeOverride);
pw.print(" mNightMode="); pw.print(mNightMode); pw.print(" (");
pw.print(Shell.nightModeToStr(mNightMode)); pw.print(") ");
pw.print(" mNightModeLocked="); pw.println(mNightModeLocked);
@@ -802,6 +815,8 @@
}
pw.println("");
pw.print(" mComputedNightMode="); pw.print(mComputedNightMode);
+ pw.print(" customStart="); pw.print(mCustomAutoNightModeStartMilliseconds);
+ pw.print(" customEnd"); pw.print(mCustomAutoNightModeEndMilliseconds);
pw.print(" mCarModeEnableFlags="); pw.print(mCarModeEnableFlags);
pw.print(" mEnableCarDockLaunch="); pw.println(mEnableCarDockLaunch);
@@ -824,7 +839,6 @@
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
synchronized (mLock) {
- mTwilightManager = getLocalService(TwilightManager.class);
mSystemReady = true;
mCarModeEnabled = mDockState == Intent.EXTRA_DOCK_STATE_CAR;
registerVrStateListener();
@@ -1011,6 +1025,10 @@
uiMode = Configuration.UI_MODE_TYPE_VR_HEADSET;
}
+ if (mNightMode == MODE_NIGHT_YES || mNightMode == UiModeManager.MODE_NIGHT_NO) {
+ updateComputedNightModeLocked(mNightMode == MODE_NIGHT_YES);
+ }
+
if (mNightMode == MODE_NIGHT_AUTO) {
boolean activateNightMode = mComputedNightMode;
if (mTwilightManager != null) {
@@ -1020,12 +1038,10 @@
}
updateComputedNightModeLocked(activateNightMode);
- uiMode = getComputedUiModeConfiguration(uiMode);
} else {
if (mTwilightManager != null) {
mTwilightManager.unregisterListener(mTwilightListener);
}
- uiMode |= mNightMode << 4;
}
if (mNightMode == MODE_NIGHT_CUSTOM) {
@@ -1033,7 +1049,6 @@
final boolean activate = computeCustomNightMode();
updateComputedNightModeLocked(activate);
scheduleNextCustomTimeListener();
- uiMode = getComputedUiModeConfiguration(uiMode);
} else {
unregisterTimeChangeEvent();
}
@@ -1042,6 +1057,8 @@
if (mPowerSave && !mCarModeEnabled) {
uiMode &= ~Configuration.UI_MODE_NIGHT_NO;
uiMode |= Configuration.UI_MODE_NIGHT_YES;
+ } else {
+ uiMode = getComputedUiModeConfiguration(uiMode);
}
if (LOG) {
@@ -1053,7 +1070,7 @@
}
mCurUiMode = uiMode;
- if (!mHoldingConfiguration && !mWaitForScreenOff) {
+ if (!mHoldingConfiguration && (!mWaitForScreenOff || mPowerSave)) {
mConfiguration.uiMode = uiMode;
}
}
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 0f1a652..dd9cc64 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -183,7 +183,8 @@
static native boolean vibratorSupportsAmplitudeControl();
static native void vibratorSetAmplitude(int amplitude);
static native int[] vibratorGetSupportedEffects();
- static native long vibratorPerformEffect(long effect, long strength, Vibration vibration);
+ static native long vibratorPerformEffect(long effect, long strength, Vibration vibration,
+ boolean withCallback);
static native void vibratorPerformComposedEffect(
VibrationEffect.Composition.PrimitiveEffect[] effect, Vibration vibration);
static native boolean vibratorSupportsExternalControl();
@@ -1334,7 +1335,8 @@
// Input devices don't support prebaked effect, so skip trying it with them.
if (!usingInputDeviceVibrators) {
long duration = vibratorPerformEffect(prebaked.getId(),
- prebaked.getEffectStrength(), vib);
+ prebaked.getEffectStrength(), vib,
+ hasCapability(IVibrator.CAP_PERFORM_CALLBACK));
long timeout = duration;
if (hasCapability(IVibrator.CAP_PERFORM_CALLBACK)) {
timeout *= ASYNC_TIMEOUT_MULTIPLIER;
diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java
index 7ccb284..7aaf9be 100644
--- a/services/core/java/com/android/server/adb/AdbService.java
+++ b/services/core/java/com/android/server/adb/AdbService.java
@@ -144,6 +144,18 @@
public File getAdbTempKeysFile() {
return mDebuggingManager.getAdbTempKeysFile();
}
+
+ @Override
+ public void startAdbdForTransport(byte transportType) {
+ FgThread.getHandler().sendMessage(obtainMessage(
+ AdbService::setAdbdEnabledForTransport, AdbService.this, true, transportType));
+ }
+
+ @Override
+ public void stopAdbdForTransport(byte transportType) {
+ FgThread.getHandler().sendMessage(obtainMessage(
+ AdbService::setAdbdEnabledForTransport, AdbService.this, false, transportType));
+ }
}
private void initAdbState() {
@@ -437,6 +449,19 @@
}
}
+ private void setAdbdEnabledForTransport(boolean enable, byte transportType) {
+ if (transportType == AdbTransportType.USB) {
+ mIsAdbUsbEnabled = enable;
+ } else if (transportType == AdbTransportType.WIFI) {
+ mIsAdbWifiEnabled = enable;
+ }
+ if (enable) {
+ startAdbd();
+ } else {
+ stopAdbd();
+ }
+ }
+
private void setAdbEnabled(boolean enable, byte transportType) {
if (DEBUG) {
Slog.d(TAG, "setAdbEnabled(" + enable + "), mIsAdbUsbEnabled=" + mIsAdbUsbEnabled
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 8ebbce3..cfc8b45 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST;
@@ -23,6 +24,7 @@
import static android.os.Process.ROOT_UID;
import static android.os.Process.SHELL_UID;
import static android.os.Process.SYSTEM_UID;
+import static android.os.Process.ZYGOTE_POLICY_FLAG_EMPTY;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKGROUND_CHECK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOREGROUND_SERVICE;
@@ -692,8 +694,8 @@
if (!r.mAllowWhileInUsePermissionInFgs) {
r.mAllowWhileInUsePermissionInFgs =
- shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, callingUid,
- service, r, allowBackgroundActivityStarts);
+ shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, callingPid,
+ callingUid, service, r, allowBackgroundActivityStarts);
}
return cmp;
@@ -2077,9 +2079,9 @@
}
if (!s.mAllowWhileInUsePermissionInFgs) {
- final int callingUid = Binder.getCallingUid();
s.mAllowWhileInUsePermissionInFgs =
- shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, callingUid,
+ shouldAllowWhileInUsePermissionInFgsLocked(callingPackage,
+ Binder.getCallingPid(), Binder.getCallingUid(),
service, s, false);
}
@@ -2936,8 +2938,10 @@
// Not running -- get it started, and enqueue this service record
// to be executed when the app comes up.
if (app == null && !permissionsReviewRequired) {
+ // TODO (chriswailes): Change the Zygote policy flags based on if the launch-for-service
+ // was initiated from a notification tap or not.
if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
- hostingRecord, false, isolated, false)) == null) {
+ hostingRecord, ZYGOTE_POLICY_FLAG_EMPTY, false, isolated, false)) == null) {
String msg = "Unable to launch app "
+ r.appInfo.packageName + "/"
+ r.appInfo.uid + " for service "
@@ -3500,8 +3504,11 @@
}
}
- // If unbound while waiting to start, remove the pending service
- mPendingServices.remove(s);
+ // If unbound while waiting to start and there is no connection left in this service,
+ // remove the pending service
+ if (s.getConnections().isEmpty()) {
+ mPendingServices.remove(s);
+ }
if ((c.flags&Context.BIND_AUTO_CREATE) != 0) {
boolean hasAutoCreate = s.hasAutoCreateConnections();
@@ -4839,7 +4846,8 @@
* @return true if allow, false otherwise.
*/
private boolean shouldAllowWhileInUsePermissionInFgsLocked(String callingPackage,
- int callingUid, Intent intent, ServiceRecord r, boolean allowBackgroundActivityStarts) {
+ int callingPid, int callingUid, Intent intent, ServiceRecord r,
+ boolean allowBackgroundActivityStarts) {
// Is the background FGS start restriction turned on?
if (!mAm.mConstants.mFlagBackgroundFgsStartRestrictionEnabled) {
return true;
@@ -4849,13 +4857,6 @@
return true;
}
- // Is the service in a whitelist?
- final boolean hasAllowBackgroundActivityStartsToken = r.app != null
- ? r.app.mAllowBackgroundActivityStartsTokens.contains(r) : false;
- if (hasAllowBackgroundActivityStartsToken) {
- return true;
- }
-
boolean isCallerSystem = false;
final int callingAppId = UserHandle.getAppId(callingUid);
switch (callingAppId) {
@@ -4874,6 +4875,24 @@
return true;
}
+ if (r.app != null) {
+ ActiveInstrumentation instr = r.app.getActiveInstrumentation();
+ if (instr != null && instr.mHasBackgroundActivityStartsPermission) {
+ return true;
+ }
+ }
+
+ final boolean hasAllowBackgroundActivityStartsToken = r.app != null
+ ? !r.app.mAllowBackgroundActivityStartsTokens.isEmpty() : false;
+ if (hasAllowBackgroundActivityStartsToken) {
+ return true;
+ }
+
+ if (mAm.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
+ == PERMISSION_GRANTED) {
+ return true;
+ }
+
// Is the calling UID at PROCESS_STATE_TOP or above?
final boolean isCallingUidTopApp = appIsTopLocked(callingUid);
if (isCallingUidTopApp) {
@@ -4901,35 +4920,7 @@
}
// TODO: remove this toast after feature development is done
- private void showWhileInUsePermissionInFgsBlockedNotificationLocked(String callingPackage,
- String detailInfo) {
- final Context context = mAm.mContext;
- final String title = "Foreground Service While-in-use Permission Restricted";
- final String content = "App affected:" + callingPackage + ", please file a bug report";
- Notification.Builder n =
- new Notification.Builder(context,
- SystemNotificationChannels.ALERTS)
- .setSmallIcon(R.drawable.stat_sys_vitals)
- .setWhen(0)
- .setColor(context.getColor(
- com.android.internal.R.color.system_notification_accent_color))
- .setTicker(title)
- .setContentTitle(title)
- .setContentText(content)
- .setStyle(new Notification.BigTextStyle().bigText(detailInfo));
- final NotificationManager notificationManager =
- (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
- notificationManager.notifyAsUser(null,
- SystemMessageProto.SystemMessage.NOTE_FOREGROUND_SERVICE_WHILE_IN_USE_PERMISSION,
- n.build(), UserHandle.ALL);
- }
-
- // TODO: remove this toast after feature development is done
- // show a toast message to ask user to file a bugreport so we know how many apps are impacted by
- // the new background started foreground service while-in-use permission restriction.
- void showWhileInUseDebugNotificationLocked(int uid, int op, int mode) {
- StringBuilder packageNameBuilder = new StringBuilder();
- StringBuilder detailInfoBuilder = new StringBuilder();
+ void showWhileInUseDebugToastLocked(int uid, int op, int mode) {
for (int i = mAm.mProcessList.mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord pr = mAm.mProcessList.mLruProcesses.get(i);
if (pr.uid != uid) {
@@ -4946,18 +4937,8 @@
+ " affected while-in-use permission:"
+ AppOpsManager.opToPublicName(op);
Slog.wtf(TAG, msg);
- packageNameBuilder.append(r.mRecentCallingPackage + " ");
- detailInfoBuilder.append(msg);
- detailInfoBuilder.append("\n");
}
}
}
-
- final String callingPackageStr = packageNameBuilder.toString();
- if (mAm.mConstants.mFlagForegroundServiceStartsLoggingEnabled
- && !callingPackageStr.isEmpty()) {
- showWhileInUsePermissionInFgsBlockedNotificationLocked(callingPackageStr,
- detailInfoBuilder.toString());
- }
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index cea3bb8..fbcb010 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -65,6 +65,10 @@
import static android.os.Process.SIGNAL_USR1;
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.THREAD_PRIORITY_FOREGROUND;
+import static android.os.Process.ZYGOTE_POLICY_FLAG_BATCH_LAUNCH;
+import static android.os.Process.ZYGOTE_POLICY_FLAG_EMPTY;
+import static android.os.Process.ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE;
+import static android.os.Process.ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS;
import static android.os.Process.ZYGOTE_PROCESS;
import static android.os.Process.getTotalMemory;
import static android.os.Process.isThreadInProcess;
@@ -3054,7 +3058,7 @@
info.targetSdkVersion = Build.VERSION.SDK_INT;
ProcessRecord proc = mProcessList.startProcessLocked(processName, info /* info */,
false /* knownToBeDead */, 0 /* intentFlags */,
- sNullHostingRecord /* hostingRecord */,
+ sNullHostingRecord /* hostingRecord */, ZYGOTE_POLICY_FLAG_EMPTY,
true /* allowWhileBooting */, true /* isolated */,
uid, true /* keepIfLarge */, abiOverride, entryPoint, entryPointArgs,
crashHandler);
@@ -3065,12 +3069,12 @@
@GuardedBy("this")
final ProcessRecord startProcessLocked(String processName,
ApplicationInfo info, boolean knownToBeDead, int intentFlags,
- HostingRecord hostingRecord, boolean allowWhileBooting,
+ HostingRecord hostingRecord, int zygotePolicyFlags, boolean allowWhileBooting,
boolean isolated, boolean keepIfLarge) {
return mProcessList.startProcessLocked(processName, info, knownToBeDead, intentFlags,
- hostingRecord, allowWhileBooting, isolated, 0 /* isolatedUid */, keepIfLarge,
- null /* ABI override */, null /* entryPoint */, null /* entryPointArgs */,
- null /* crashHandler */);
+ hostingRecord, zygotePolicyFlags, allowWhileBooting, isolated, 0 /* isolatedUid */,
+ keepIfLarge, null /* ABI override */, null /* entryPoint */,
+ null /* entryPointArgs */, null /* crashHandler */);
}
boolean isAllowedWhileBooting(ApplicationInfo ai) {
@@ -4953,7 +4957,8 @@
} catch (RemoteException e) {
app.resetPackageList(mProcessStats);
mProcessList.startProcessLocked(app,
- new HostingRecord("link fail", processName));
+ new HostingRecord("link fail", processName),
+ ZYGOTE_POLICY_FLAG_EMPTY);
return false;
}
@@ -5372,7 +5377,9 @@
for (int ip=0; ip<NP; ip++) {
if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "Starting process on hold: "
+ procs.get(ip));
- mProcessList.startProcessLocked(procs.get(ip), new HostingRecord("on-hold"));
+ mProcessList.startProcessLocked(procs.get(ip),
+ new HostingRecord("on-hold"),
+ ZYGOTE_POLICY_FLAG_BATCH_LAUNCH);
}
}
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL) {
@@ -6515,14 +6522,6 @@
}
@Override
- public void resizeDockedStack(Rect dockedBounds, Rect tempDockedTaskBounds,
- Rect tempDockedTaskInsetBounds,
- Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds) {
- mActivityTaskManager.resizeDockedStack(dockedBounds, tempDockedTaskBounds,
- tempDockedTaskInsetBounds, tempOtherTaskBounds, tempOtherTaskInsetBounds);
- }
-
- @Override
public void positionTaskInStack(int taskId, int stackId, int position) {
mActivityTaskManager.positionTaskInStack(taskId, stackId, position);
}
@@ -7232,8 +7231,9 @@
proc = startProcessLocked(cpi.processName,
cpr.appInfo, false, 0,
new HostingRecord("content provider",
- new ComponentName(cpi.applicationInfo.packageName,
- cpi.name)), false, false, false);
+ new ComponentName(cpi.applicationInfo.packageName,
+ cpi.name)),
+ ZYGOTE_POLICY_FLAG_EMPTY, false, false, false);
checkTime(startTime, "getContentProviderImpl: after start process");
if (proc == null) {
Slog.w(TAG, "Unable to launch app "
@@ -7267,10 +7267,8 @@
}
checkTime(startTime, "getContentProviderImpl: done!");
- grantImplicitAccess(userId, null /*intent*/,
- UserHandle.getAppId(Binder.getCallingUid()),
- UserHandle.getAppId(cpi.applicationInfo.uid)
- );
+ grantImplicitAccess(userId, null /*intent*/, callingUid,
+ UserHandle.getAppId(cpi.applicationInfo.uid));
}
// Wait for the provider to be published...
@@ -7793,7 +7791,8 @@
.getPersistentApplications(STOCK_PM_FLAGS | matchFlags).getList();
for (ApplicationInfo app : apps) {
if (!"android".equals(app.packageName)) {
- addAppLocked(app, null, false, null /* ABI override */);
+ addAppLocked(app, null, false, null /* ABI override */,
+ ZYGOTE_POLICY_FLAG_BATCH_LAUNCH);
}
}
} catch (RemoteException ex) {
@@ -8064,23 +8063,25 @@
@GuardedBy("this")
final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
- String abiOverride) {
+ String abiOverride, int zygotePolicyFlags) {
return addAppLocked(info, customProcess, isolated, false /* disableHiddenApiChecks */,
- false /* mountExtStorageFull */, abiOverride);
+ false /* mountExtStorageFull */, abiOverride, zygotePolicyFlags);
}
@GuardedBy("this")
final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
- boolean disableHiddenApiChecks, boolean mountExtStorageFull, String abiOverride) {
+ boolean disableHiddenApiChecks, boolean mountExtStorageFull, String abiOverride,
+ int zygotePolicyFlags) {
return addAppLocked(info, customProcess, isolated, disableHiddenApiChecks,
- false /* disableTestApiChecks */, mountExtStorageFull, abiOverride);
+ false /* disableTestApiChecks */, mountExtStorageFull, abiOverride,
+ zygotePolicyFlags);
}
// TODO: Move to ProcessList?
@GuardedBy("this")
final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
boolean disableHiddenApiChecks, boolean disableTestApiChecks,
- boolean mountExtStorageFull, String abiOverride) {
+ boolean mountExtStorageFull, String abiOverride, int zygotePolicyFlags) {
ProcessRecord app;
if (!isolated) {
app = getProcessRecordLocked(customProcess != null ? customProcess : info.processName,
@@ -8115,7 +8116,8 @@
mPersistentStartingProcesses.add(app);
mProcessList.startProcessLocked(app, new HostingRecord("added application",
customProcess != null ? customProcess : app.processName),
- disableHiddenApiChecks, disableTestApiChecks, mountExtStorageFull, abiOverride);
+ zygotePolicyFlags, disableHiddenApiChecks, disableTestApiChecks,
+ mountExtStorageFull, abiOverride);
}
return app;
@@ -14622,7 +14624,8 @@
mProcessList.addProcessNameLocked(app);
app.pendingStart = false;
mProcessList.startProcessLocked(app,
- new HostingRecord("restart", app.processName));
+ new HostingRecord("restart", app.processName),
+ ZYGOTE_POLICY_FLAG_EMPTY);
return true;
} else if (app.pid > 0 && app.pid != MY_PID) {
// Goodbye!
@@ -14985,7 +14988,7 @@
ProcessRecord proc = startProcessLocked(app.processName, app,
false, 0,
new HostingRecord("backup", hostingName),
- false, false, false);
+ ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS, false, false, false);
if (proc == null) {
Slog.e(TAG, "Unable to start backup agent process " + r);
return false;
@@ -16624,7 +16627,8 @@
}
ProcessRecord app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks,
- disableTestApiChecks, mountExtStorageFull, abiOverride);
+ disableTestApiChecks, mountExtStorageFull, abiOverride,
+ ZYGOTE_POLICY_FLAG_EMPTY);
app.setActiveInstrumentation(activeInstr);
activeInstr.mFinished = false;
activeInstr.mSourceUid = callingUid;
@@ -18021,7 +18025,8 @@
mProcessList.mRemovedProcesses.remove(i);
if (app.isPersistent()) {
- addAppLocked(app.info, null, false, null /* ABI override */);
+ addAppLocked(app.info, null, false, null /* ABI override */,
+ ZYGOTE_POLICY_FLAG_BATCH_LAUNCH);
}
}
}
@@ -19228,8 +19233,8 @@
// preempted by other processes before attaching the process of top app.
startProcessLocked(processName, info, knownToBeDead, 0 /* intentFlags */,
new HostingRecord(hostingType, hostingName, isTop),
- false /* allowWhileBooting */, false /* isolated */,
- true /* keepIfLarge */);
+ ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE, false /* allowWhileBooting */,
+ false /* isolated */, true /* keepIfLarge */);
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
@@ -19347,7 +19352,7 @@
@Override
public void showWhileInUseDebugToast(int uid, int op, int mode) {
synchronized (ActivityManagerService.this) {
- ActivityManagerService.this.mServices.showWhileInUseDebugNotificationLocked(
+ ActivityManagerService.this.mServices.showWhileInUseDebugToastLocked(
uid, op, mode);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index bf79729..59f64ac 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -1763,6 +1763,12 @@
}
private boolean switchUserAndWaitForComplete(int userId) throws RemoteException {
+ UserInfo currentUser = mInterface.getCurrentUser();
+ if (currentUser != null && userId == currentUser.id) {
+ // Already switched to the correct user, exit early.
+ return true;
+ }
+
// Register switch observer.
final CountDownLatch switchLatch = new CountDownLatch(1);
mInterface.registerUserSwitchObserver(
@@ -1777,13 +1783,18 @@
// Switch.
boolean switched = mInterface.switchUser(userId);
+ if (!switched) {
+ // Switching failed, don't wait for the user switch observer.
+ return false;
+ }
// Wait.
try {
- switchLatch.await(USER_OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ switched = switchLatch.await(USER_OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
- getErrPrintWriter().println("Thread interrupted unexpectedly.");
+ getErrPrintWriter().println("Error: Thread interrupted unexpectedly.");
}
+
return switched;
}
@@ -1815,7 +1826,7 @@
if (switched) {
return 0;
} else {
- pw.printf("Failed to switch to user %d\n", userId);
+ pw.printf("Error: Failed to switch to user %d\n", userId);
return 1;
}
}
@@ -2570,8 +2581,6 @@
switch (op) {
case "move-task":
return runStackMoveTask(pw);
- case "resize-docked-stack":
- return runStackResizeDocked(pw);
case "positiontask":
return runStackPositionTask(pw);
case "list":
@@ -2646,17 +2655,6 @@
return 0;
}
- int runStackResizeDocked(PrintWriter pw) throws RemoteException {
- final Rect bounds = getBounds();
- final Rect taskBounds = getBounds();
- if (bounds == null || taskBounds == null) {
- getErrPrintWriter().println("Error: invalid input bounds");
- return -1;
- }
- mTaskInterface.resizeDockedStack(bounds, taskBounds, null, null, null);
- return 0;
- }
-
int runStackPositionTask(PrintWriter pw) throws RemoteException {
String taskIdStr = getNextArgRequired();
int taskId = Integer.parseInt(taskIdStr);
diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java
index 60aba27..cba6b92 100644
--- a/services/core/java/com/android/server/am/AppExitInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java
@@ -708,7 +708,7 @@
void dumpHistoryProcessExitInfo(PrintWriter pw, String packageName) {
pw.println("ACTIVITY MANAGER LRU PROCESSES (dumpsys activity exit-info)");
- SimpleDateFormat sdf = new SimpleDateFormat();
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
synchronized (mLock) {
pw.println("Last Timestamp of Persistence Into Persistent Storage: "
+ sdf.format(new Date(mLastAppExitInfoPersistTimestamp)));
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 119394f..dbad562 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -17,7 +17,6 @@
package com.android.server.am;
import android.app.ActivityManager;
-import android.app.job.JobProtoEnums;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.content.ContentResolver;
import android.content.Context;
@@ -468,26 +467,18 @@
}
/** A scheduled job was started. */
- public void noteJobStart(String name, int uid, int standbyBucket, int jobid) {
+ public void noteJobStart(String name, int uid) {
enforceCallingPermission();
synchronized (mStats) {
mStats.noteJobStartLocked(name, uid);
- FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
- uid, null, name,
- FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__STARTED,
- JobProtoEnums.STOP_REASON_UNKNOWN, standbyBucket, jobid);
}
}
/** A scheduled job was finished. */
- public void noteJobFinish(String name, int uid, int stopReason, int standbyBucket, int jobid) {
+ public void noteJobFinish(String name, int uid, int stopReason) {
enforceCallingPermission();
synchronized (mStats) {
mStats.noteJobFinishLocked(name, uid, stopReason);
- FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
- uid, null, name,
- FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__FINISHED, stopReason,
- standbyBucket, jobid);
}
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 26ef707..3aec53a 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -16,6 +16,9 @@
package com.android.server.am;
+import static android.os.Process.ZYGOTE_POLICY_FLAG_EMPTY;
+import static android.os.Process.ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE;
+
import static com.android.server.am.ActivityManagerDebugConfig.*;
import android.app.ActivityManager;
@@ -1593,7 +1596,9 @@
+ receiverUid);
}
- if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
+ final boolean isActivityCapable =
+ (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0);
+ if (isActivityCapable) {
scheduleTempWhitelistLocked(receiverUid,
brOptions.getTemporaryAppWhitelistDuration(), r);
}
@@ -1648,6 +1653,7 @@
info.activityInfo.applicationInfo, true,
r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
new HostingRecord("broadcast", r.curComponent),
+ isActivityCapable ? ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE : ZYGOTE_POLICY_FLAG_EMPTY,
(r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))
== null) {
// Ah, this recipient is unavailable. Finish it if necessary,
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index eec68dc..be48374 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -911,10 +911,12 @@
pid = proc.pid;
name = proc.processName;
- if (proc.curAdj <= ProcessList.CACHED_APP_MIN_ADJ) {
+ if (proc.curAdj < ProcessList.CACHED_APP_MIN_ADJ
+ || proc.shouldNotFreeze) {
if (DEBUG_FREEZER) {
Slog.d(TAG_AM, "Skipping freeze for process " + pid
- + " " + name + " (not cached)");
+ + " " + name + " curAdj = " + proc.curAdj
+ + ", shouldNotFreeze = " + proc.shouldNotFreeze);
}
return;
}
diff --git a/services/core/java/com/android/server/am/InstrumentationReporter.java b/services/core/java/com/android/server/am/InstrumentationReporter.java
index fdbb73e..7294308 100644
--- a/services/core/java/com/android/server/am/InstrumentationReporter.java
+++ b/services/core/java/com/android/server/am/InstrumentationReporter.java
@@ -18,6 +18,7 @@
import android.app.IInstrumentationWatcher;
import android.content.ComponentName;
+import android.os.Binder;
import android.os.Bundle;
import android.os.Process;
import android.os.RemoteException;
@@ -109,6 +110,8 @@
mName = name;
mResultCode = resultCode;
mResults = results;
+
+ Binder.allowBlocking(mWatcher.asBinder());
}
}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index c239feb1..3fd1b78 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1109,6 +1109,7 @@
app.adjTarget = null;
app.empty = false;
app.setCached(false);
+ app.shouldNotFreeze = false;
final int appUid = app.info.uid;
final int logUid = mService.mCurOomAdjUid;
@@ -1542,23 +1543,24 @@
}
boolean trackedProcState = false;
- if ((cr.flags& Context.BIND_WAIVE_PRIORITY) == 0) {
- ProcessRecord client = cr.binding.client;
- if (computeClients) {
- computeOomAdjLocked(client, cachedAdj, topApp, doingAll, now,
- cycleReEval, true);
- } else {
- client.setCurRawAdj(client.setAdj);
- client.setCurRawProcState(client.setProcState);
- }
+ ProcessRecord client = cr.binding.client;
+ if (computeClients) {
+ computeOomAdjLocked(client, cachedAdj, topApp, doingAll, now,
+ cycleReEval, true);
+ } else {
+ client.setCurRawAdj(client.setAdj);
+ client.setCurRawProcState(client.setProcState);
+ }
+
+ int clientAdj = client.getCurRawAdj();
+ int clientProcState = client.getCurRawProcState();
+
+ if ((cr.flags & Context.BIND_WAIVE_PRIORITY) == 0) {
if (shouldSkipDueToCycle(app, client, procState, adj, cycleReEval)) {
continue;
}
- int clientAdj = client.getCurRawAdj();
- int clientProcState = client.getCurRawProcState();
-
if (clientProcState == PROCESS_STATE_FOREGROUND_SERVICE) {
procStateFromFGSClient = true;
}
@@ -1762,6 +1764,19 @@
+ ProcessList.makeProcStateString(procState));
}
}
+ } else { // BIND_WAIVE_PRIORITY == true
+ // BIND_WAIVE_PRIORITY bindings are special when it comes to the
+ // freezer. Processes bound via WPRI are expected to be running,
+ // but they are not promoted in the LRU list to keep them out of
+ // cached. As a result, they can freeze based on oom_adj alone.
+ // Normally, bindToDeath would fire when a cached app would die
+ // in the background, but nothing will fire when a running process
+ // pings a frozen process. Accordingly, any cached app that is
+ // bound by an unfrozen app via a WPRI binding has to remain
+ // unfrozen.
+ if (clientAdj < ProcessList.CACHED_APP_MIN_ADJ) {
+ app.shouldNotFreeze = true;
+ }
}
if ((cr.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
app.treatLikeActivity = true;
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 22559c4..5d1b0e3 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -25,6 +25,7 @@
import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
+import static android.os.Process.ZYGOTE_POLICY_FLAG_EMPTY;
import static android.os.Process.getFreeMemory;
import static android.os.Process.getTotalMemory;
import static android.os.Process.killProcessQuiet;
@@ -80,13 +81,11 @@
import android.os.DropBoxManager;
import android.os.Handler;
import android.os.IBinder;
-import android.os.IVold;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
@@ -1640,7 +1639,7 @@
*/
@GuardedBy("mService")
boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
- boolean disableHiddenApiChecks, boolean disableTestApiChecks,
+ int zygotePolicyFlags, boolean disableHiddenApiChecks, boolean disableTestApiChecks,
boolean mountExtStorageFull, String abiOverride) {
if (app.pendingStart) {
return true;
@@ -1733,8 +1732,7 @@
}
// Run the app in safe mode if its manifest requests so or the
// system is booted in safe mode.
- if ((app.info.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0 ||
- mService.mSafeMode == true) {
+ if ((app.info.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0 || mService.mSafeMode) {
runtimeFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;
}
if ((app.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_PROFILEABLE_BY_SHELL) != 0) {
@@ -1846,8 +1844,8 @@
final String entryPoint = "android.app.ActivityThread";
return startProcessLocked(hostingRecord, entryPoint, app, uid, gids,
- runtimeFlags, mountExternal, seInfo, requiredAbi, instructionSet, invokeWith,
- startTime);
+ runtimeFlags, zygotePolicyFlags, mountExternal, seInfo, requiredAbi,
+ instructionSet, invokeWith, startTime);
} catch (RuntimeException e) {
Slog.e(ActivityManagerService.TAG, "Failure starting process " + app.processName, e);
@@ -1864,9 +1862,8 @@
}
@GuardedBy("mService")
- boolean startProcessLocked(HostingRecord hostingRecord,
- String entryPoint,
- ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
+ boolean startProcessLocked(HostingRecord hostingRecord, String entryPoint, ProcessRecord app,
+ int uid, int[] gids, int runtimeFlags, int zygotePolicyFlags, int mountExternal,
String seInfo, String requiredAbi, String instructionSet, String invokeWith,
long startTime) {
app.pendingStart = true;
@@ -1895,15 +1892,15 @@
if (DEBUG_PROCESSES) Slog.i(TAG_PROCESSES,
"Posting procStart msg for " + app.toShortString());
mService.mProcStartHandler.post(() -> handleProcessStart(
- app, entryPoint, gids, runtimeFlags, mountExternal, requiredAbi,
- instructionSet, invokeWith, startSeq));
+ app, entryPoint, gids, runtimeFlags, zygotePolicyFlags, mountExternal,
+ requiredAbi, instructionSet, invokeWith, startSeq));
return true;
} else {
try {
final Process.ProcessStartResult startResult = startProcess(hostingRecord,
entryPoint, app,
- uid, gids, runtimeFlags, mountExternal, seInfo, requiredAbi, instructionSet,
- invokeWith, startTime);
+ uid, gids, runtimeFlags, zygotePolicyFlags, mountExternal, seInfo,
+ requiredAbi, instructionSet, invokeWith, startTime);
handleProcessStartedLocked(app, startResult.pid, startResult.usingWrapper,
startSeq, false);
} catch (RuntimeException e) {
@@ -1923,8 +1920,8 @@
* <p>Note: this function doesn't hold the global AM lock intentionally.</p>
*/
private void handleProcessStart(final ProcessRecord app, final String entryPoint,
- final int[] gids, final int runtimeFlags, final int mountExternal,
- final String requiredAbi, final String instructionSet,
+ final int[] gids, final int runtimeFlags, int zygotePolicyFlags,
+ final int mountExternal, final String requiredAbi, final String instructionSet,
final String invokeWith, final long startSeq) {
// If there is a precede instance of the process, wait for its death with a timeout.
// Use local reference since we are not using locks here
@@ -1959,8 +1956,10 @@
}
try {
final Process.ProcessStartResult startResult = startProcess(app.hostingRecord,
- entryPoint, app, app.startUid, gids, runtimeFlags, mountExternal,
- app.seInfo, requiredAbi, instructionSet, invokeWith, app.startTime);
+ entryPoint, app, app.startUid, gids, runtimeFlags, zygotePolicyFlags,
+ mountExternal, app.seInfo, requiredAbi, instructionSet, invokeWith,
+ app.startTime);
+
synchronized (mService) {
handleProcessStartedLocked(app, startResult, startSeq);
}
@@ -2113,9 +2112,9 @@
}
private Process.ProcessStartResult startProcess(HostingRecord hostingRecord, String entryPoint,
- ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
- String seInfo, String requiredAbi, String instructionSet, String invokeWith,
- long startTime) {
+ ProcessRecord app, int uid, int[] gids, int runtimeFlags, int zygotePolicyFlags,
+ int mountExternal, String seInfo, String requiredAbi, String instructionSet,
+ String invokeWith, long startTime) {
try {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
app.processName);
@@ -2163,14 +2162,15 @@
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, null, app.info.packageName,
- /*useUsapPool=*/ false, isTopApp, app.mDisabledCompatChanges,
- pkgDataInfoMap, new String[]{PROC_START_SEQ_IDENT + app.startSeq});
+ /*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, isTopApp,
+ app.mDisabledCompatChanges, pkgDataInfoMap,
+ new String[]{PROC_START_SEQ_IDENT + app.startSeq});
} else {
startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
- app.info.dataDir, invokeWith, app.info.packageName, isTopApp,
- app.mDisabledCompatChanges, pkgDataInfoMap,
+ app.info.dataDir, invokeWith, app.info.packageName, zygotePolicyFlags,
+ isTopApp, app.mDisabledCompatChanges, pkgDataInfoMap,
new String[]{PROC_START_SEQ_IDENT + app.startSeq});
}
checkSlow(startTime, "startProcess: returned from zygote!");
@@ -2181,14 +2181,14 @@
}
@GuardedBy("mService")
- final void startProcessLocked(ProcessRecord app, HostingRecord hostingRecord) {
- startProcessLocked(app, hostingRecord, null /* abiOverride */);
+ void startProcessLocked(ProcessRecord app, HostingRecord hostingRecord, int zygotePolicyFlags) {
+ startProcessLocked(app, hostingRecord, zygotePolicyFlags, null /* abiOverride */);
}
@GuardedBy("mService")
final boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
- String abiOverride) {
- return startProcessLocked(app, hostingRecord,
+ int zygotePolicyFlags, String abiOverride) {
+ return startProcessLocked(app, hostingRecord, zygotePolicyFlags,
false /* disableHiddenApiChecks */, false /* disableTestApiChecks */,
false /* mountExtStorageFull */, abiOverride);
}
@@ -2196,8 +2196,9 @@
@GuardedBy("mService")
final ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
boolean knownToBeDead, int intentFlags, HostingRecord hostingRecord,
- boolean allowWhileBooting, boolean isolated, int isolatedUid, boolean keepIfLarge,
- String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) {
+ int zygotePolicyFlags, boolean allowWhileBooting, boolean isolated, int isolatedUid,
+ boolean keepIfLarge, String abiOverride, String entryPoint, String[] entryPointArgs,
+ Runnable crashHandler) {
long startTime = SystemClock.uptimeMillis();
ProcessRecord app;
if (!isolated) {
@@ -2308,7 +2309,8 @@
}
checkSlow(startTime, "startProcess: stepping in to startProcess");
- final boolean success = startProcessLocked(app, hostingRecord, abiOverride);
+ final boolean success =
+ startProcessLocked(app, hostingRecord, zygotePolicyFlags, abiOverride);
checkSlow(startTime, "startProcess: done starting proc!");
return success ? app : null;
}
@@ -2332,6 +2334,19 @@
if (sb == null) sb = new StringBuilder();
sb.append("seq=" + app.startSeq + ",expected=" + expectedStartSeq + ";");
}
+ try {
+ AppGlobals.getPackageManager().checkPackageStartable(app.info.packageName, app.userId);
+ } catch (RemoteException e) {
+ // unexpected; ignore
+ } catch (SecurityException e) {
+ if (mService.mConstants.FLAG_PROCESS_START_ASYNC) {
+ if (sb == null) sb = new StringBuilder();
+ sb.append("Package is frozen;");
+ } else {
+ // we're not being started async and so should throw to the caller.
+ throw e;
+ }
+ }
return sb == null ? null : sb.toString();
}
@@ -2631,7 +2646,8 @@
mService.handleAppDiedLocked(app, willRestart, allowRestart);
if (willRestart) {
removeLruProcessLocked(app);
- mService.addAppLocked(app.info, null, false, null /* ABI override */);
+ mService.addAppLocked(app.info, null, false, null /* ABI override */,
+ ZYGOTE_POLICY_FLAG_EMPTY);
}
} else {
mRemovedProcesses.add(app);
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index c2f03ec..f2ca1da 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -167,6 +167,7 @@
int lastCompactAction; // The most recent compaction action performed for this app.
boolean frozen; // True when the process is frozen.
long freezeUnfreezeTime; // Last time the app was (un)frozen, 0 for never
+ boolean shouldNotFreeze; // True if a process has a WPRI binding from an unfrozen process
private int mCurSchedGroup; // Currently desired scheduling class
int setSchedGroup; // Last set to background scheduling class
int trimMemoryLevel; // Last selected memory trimming level
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 5b8a6d9..c15360b 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -4060,9 +4060,11 @@
private void readOp(XmlPullParser parser, @NonNull UidState uidState,
@NonNull String pkgName, boolean isPrivileged) throws NumberFormatException,
XmlPullParserException, IOException {
- Op op = new Op(uidState, pkgName,
- Integer.parseInt(parser.getAttributeValue(null, "n")),
- uidState.uid);
+ int opCode = Integer.parseInt(parser.getAttributeValue(null, "n"));
+ if (isIgnoredAppOp(opCode)) {
+ return;
+ }
+ Op op = new Op(uidState, pkgName, opCode, uidState.uid);
final int mode = XmlUtils.readIntAttribute(parser, "m",
AppOpsManager.opToDefaultMode(op.op));
@@ -4096,6 +4098,16 @@
ops.put(op.op, op);
}
+ //TODO(b/149995538): Remove once this has reached all affected devices
+ private static boolean isIgnoredAppOp(int op) {
+ switch (op) {
+ case AppOpsManager.OP_MANAGE_EXTERNAL_STORAGE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
void writeState() {
synchronized (mFile) {
FileOutputStream stream;
diff --git a/services/core/java/com/android/server/backup/PeopleBackupHelper.java b/services/core/java/com/android/server/backup/PeopleBackupHelper.java
index e58a051..555e006 100644
--- a/services/core/java/com/android/server/backup/PeopleBackupHelper.java
+++ b/services/core/java/com/android/server/backup/PeopleBackupHelper.java
@@ -50,7 +50,7 @@
if (DEBUG) {
Slog.d(TAG, "Handling backup of " + key);
}
- return ps.backupConversationInfos(mUserId);
+ return ps.getBackupPayload(mUserId);
}
@Override
@@ -63,6 +63,6 @@
if (DEBUG) {
Slog.d(TAG, "Handling restore of " + key);
}
- ps.restoreConversationInfos(mUserId, key, payload);
+ ps.restore(mUserId, payload);
}
}
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 7f7c9c4..441d9d9 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -16,6 +16,7 @@
package com.android.server.compat;
+import android.app.compat.ChangeIdStateCache;
import android.compat.Compatibility.ChangeConfig;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -80,6 +81,7 @@
void addChange(CompatChange change) {
synchronized (mChanges) {
mChanges.put(change.getId(), change);
+ invalidateCache();
}
}
@@ -172,6 +174,7 @@
addChange(c);
}
c.addPackageOverride(packageName, enabled);
+ invalidateCache();
}
return alreadyKnown;
}
@@ -228,6 +231,7 @@
// Should never occur, since validator is in the same process.
throw new RuntimeException("Unable to call override validator!", e);
}
+ invalidateCache();
}
return overrideExists;
}
@@ -250,6 +254,7 @@
addOverride(changeId, packageName, false);
}
+ invalidateCache();
}
}
@@ -279,6 +284,7 @@
throw new RuntimeException("Unable to call override validator!", e);
}
}
+ invalidateCache();
}
}
@@ -377,6 +383,7 @@
config.initConfigFromLib(Environment.buildPath(
apex.apexDirectory, "etc", "compatconfig"));
}
+ config.invalidateCache();
return config;
}
@@ -406,4 +413,8 @@
IOverrideValidator getOverrideValidator() {
return mOverrideValidator;
}
+
+ private void invalidateCache() {
+ ChangeIdStateCache.invalidate();
+ }
}
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 7cb8458..612fd39 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -178,6 +178,8 @@
float maxRefreshRate = Float.POSITIVE_INFINITY;
int lowestConsideredPriority = Vote.MIN_PRIORITY;
while (lowestConsideredPriority <= Vote.MAX_PRIORITY) {
+ minRefreshRate = 0f;
+ maxRefreshRate = Float.POSITIVE_INFINITY;
int height = Vote.INVALID_SIZE;
int width = Vote.INVALID_SIZE;
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index e578ac1..05a757b 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -181,6 +181,7 @@
private int mState = Display.STATE_UNKNOWN;
private float mBrightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT;
private int mDefaultModeId;
+ private int mDefaultConfigGroup;
private int mActiveModeId;
private boolean mActiveModeInvalid;
private DisplayModeDirector.DesiredDisplayModeSpecs mDisplayModeSpecs =
@@ -332,15 +333,18 @@
// For a new display, we need to initialize the default mode ID.
if (mDefaultModeId == NO_DISPLAY_MODE_ID) {
mDefaultModeId = activeRecord.mMode.getModeId();
+ mDefaultConfigGroup = configs[activeConfigId].configGroup;
} else if (modesAdded && mActiveModeId != activeRecord.mMode.getModeId()) {
Slog.d(TAG, "New display modes are added and the active mode has changed, "
+ "use active mode as default mode.");
mActiveModeId = activeRecord.mMode.getModeId();
mDefaultModeId = activeRecord.mMode.getModeId();
- } else if (findDisplayConfigIdLocked(mDefaultModeId) < 0) {
+ mDefaultConfigGroup = configs[activeConfigId].configGroup;
+ } else if (findDisplayConfigIdLocked(mDefaultModeId, mDefaultConfigGroup) < 0) {
Slog.w(TAG, "Default display mode no longer available, using currently"
+ " active mode as default.");
mDefaultModeId = activeRecord.mMode.getModeId();
+ mDefaultConfigGroup = configs[activeConfigId].configGroup;
}
// Determine whether the display mode specs' base mode is still there.
@@ -754,7 +758,16 @@
// a valid mode.
return;
}
- int baseConfigId = findDisplayConfigIdLocked(displayModeSpecs.baseModeId);
+
+ // Find the config Id based on the desired mode specs. In case there is more than one
+ // config matching the mode spec, prefer the one that is in the default config group.
+ // For now the default config group is taken from the active config when we got the
+ // hotplug event for the display. In the future we might want to change the default
+ // config based on vendor requirements.
+ // Note: We prefer the default config group over the current one as this is the config
+ // group the vendor prefers.
+ int baseConfigId = findDisplayConfigIdLocked(displayModeSpecs.baseModeId,
+ mDefaultConfigGroup);
if (baseConfigId < 0) {
// When a display is hotplugged, it's possible for a mode to be removed that was
// previously valid. Because of the way display changes are propagated through the
@@ -906,17 +919,26 @@
pw.print("mSupportedColorModes=" + mSupportedColorModes.toString());
}
- private int findDisplayConfigIdLocked(int modeId) {
+ private int findDisplayConfigIdLocked(int modeId, int configGroup) {
+ int matchingConfigId = SurfaceControl.DisplayConfig.INVALID_DISPLAY_CONFIG_ID;
DisplayModeRecord record = mSupportedModes.get(modeId);
if (record != null) {
for (int i = 0; i < mDisplayConfigs.length; i++) {
SurfaceControl.DisplayConfig config = mDisplayConfigs[i];
if (record.hasMatchingMode(config)) {
- return i;
+ if (matchingConfigId
+ == SurfaceControl.DisplayConfig.INVALID_DISPLAY_CONFIG_ID) {
+ matchingConfigId = i;
+ }
+
+ // Prefer to return a config that matches the configGroup
+ if (config.configGroup == configGroup) {
+ return i;
+ }
}
}
}
- return -1;
+ return matchingConfigId;
}
private int findMatchingModeIdLocked(int configId) {
diff --git a/services/core/java/com/android/server/incremental/IncrementalManagerService.java b/services/core/java/com/android/server/incremental/IncrementalManagerService.java
index a8121cc..1320e6c 100644
--- a/services/core/java/com/android/server/incremental/IncrementalManagerService.java
+++ b/services/core/java/com/android/server/incremental/IncrementalManagerService.java
@@ -26,9 +26,7 @@
import android.content.pm.IDataLoaderStatusListener;
import android.os.Bundle;
import android.os.RemoteException;
-import android.os.ResultReceiver;
import android.os.ServiceManager;
-import android.os.ShellCallback;
import android.os.incremental.IIncrementalManager;
import android.util.Slog;
@@ -147,14 +145,6 @@
// TODO(b/136132412): implement this
}
- @Override
- public void onShellCommand(@NonNull FileDescriptor in, @NonNull FileDescriptor out,
- FileDescriptor err, @NonNull String[] args, ShellCallback callback,
- @NonNull ResultReceiver resultReceiver) {
- (new IncrementalManagerShellCommand(mContext)).exec(
- this, in, out, err, args, callback, resultReceiver);
- }
-
private static native long nativeStartService();
private static native void nativeSystemReady(long nativeInstance);
diff --git a/services/core/java/com/android/server/incremental/IncrementalManagerShellCommand.java b/services/core/java/com/android/server/incremental/IncrementalManagerShellCommand.java
deleted file mode 100644
index 6a5e963..0000000
--- a/services/core/java/com/android/server/incremental/IncrementalManagerShellCommand.java
+++ /dev/null
@@ -1,313 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package com.android.server.incremental;
-
-import static android.content.pm.PackageInstaller.LOCATION_DATA_APP;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.IIntentReceiver;
-import android.content.IIntentSender;
-import android.content.Intent;
-import android.content.IntentSender;
-import android.content.pm.DataLoaderParams;
-import android.content.pm.InstallationFile;
-import android.content.pm.PackageInstaller;
-import android.content.pm.PackageManager;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
-import android.os.ShellCommand;
-import android.util.Slog;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Defines actions to handle adb commands like "adb abb incremental ...".
- */
-public final class IncrementalManagerShellCommand extends ShellCommand {
- private static final String TAG = "IncrementalShellCommand";
- // Assuming the adb data loader is always installed on the device
- private static final String LOADER_PACKAGE_NAME = "com.android.incremental.nativeadb";
- private static final String LOADER_CLASS_NAME =
- LOADER_PACKAGE_NAME + ".NativeAdbDataLoaderService";
- private final @NonNull Context mContext;
-
- private static final int ERROR_INVALID_ARGUMENTS = -1;
- private static final int ERROR_DATA_LOADER_INIT = -2;
- private static final int ERROR_COMMAND_EXECUTION = -3;
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({ERROR_INVALID_ARGUMENTS, ERROR_DATA_LOADER_INIT, ERROR_COMMAND_EXECUTION})
- public @interface IncrementalShellCommandErrorCode {
- }
-
- IncrementalManagerShellCommand(@NonNull Context context) {
- mContext = context;
- }
-
- @Override
- public int onCommand(@Nullable String cmd) {
- if (cmd == null) {
- return handleDefaultCommands(null);
- }
- switch (cmd) {
- case "install-start":
- return runInstallStart();
- case "install-finish":
- return runInstallFinish();
- default:
- return handleDefaultCommands(cmd);
- }
- }
-
- @Override
- public void onHelp() {
- PrintWriter pw = getOutPrintWriter();
- pw.println("Incremental Service Commands:");
- pw.println(" help");
- pw.println(" Print this help text.");
- pw.println(" install-start");
- pw.println(" Opens an installation session");
- pw.println(" install-finish SESSION_ID --file NAME:SIZE:INDEX --file NAME:SIZE:INDEX ...");
- pw.println(" Commits an installation session specified by session ID for an APK ");
- pw.println(" or a bundle of splits. Configures lib dirs or OBB files if specified.");
- }
-
- private int runInstallStart() {
- final PrintWriter pw = getOutPrintWriter();
- final PackageInstaller packageInstaller =
- mContext.getPackageManager().getPackageInstaller();
- if (packageInstaller == null) {
- pw.println("Failed to get PackageInstaller.");
- return ERROR_COMMAND_EXECUTION;
- }
-
- final Map<String, ParcelFileDescriptor> fds = getShellFileDescriptors();
- if (fds == null) {
- pw.println("File names and sizes don't match.");
- return ERROR_DATA_LOADER_INIT;
- }
- // dup FDs before closing them
- final Map<String, ParcelFileDescriptor> dataLoaderDynamicArgs = new HashMap<>();
- for (Map.Entry<String, ParcelFileDescriptor> nfd : fds.entrySet()) {
- try {
- dataLoaderDynamicArgs.put(nfd.getKey(), nfd.getValue().dup());
- } catch (IOException ignored) {
- pw.println("Failed to dup shell file descriptor");
- return ERROR_DATA_LOADER_INIT;
- } finally {
- try {
- nfd.getValue().close();
- } catch (IOException ignored) {
- }
- }
- }
-
- final DataLoaderParams params = DataLoaderParams.forIncremental(
- new ComponentName(LOADER_PACKAGE_NAME, LOADER_CLASS_NAME), "",
- dataLoaderDynamicArgs);
- PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(
- PackageInstaller.SessionParams.MODE_FULL_INSTALL);
- sessionParams.installFlags |= PackageManager.INSTALL_ALL_USERS;
- // Replace existing if same package is already installed
- sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
- sessionParams.setDataLoaderParams(params);
-
- try {
- int sessionId = packageInstaller.createSession(sessionParams);
- pw.println("Successfully opened session: sessionId = " + sessionId);
- } catch (IOException ex) {
- pw.println("Failed to create session.");
- return ERROR_COMMAND_EXECUTION;
- }
- return 0;
- }
-
- private int runInstallFinish() {
- final PrintWriter pw = getOutPrintWriter();
- final int sessionId = parseInt(getNextArgRequired());
- final List<InstallationFile> installationFiles = parseFileArgs(pw);
- if (installationFiles == null) {
- pw.println("Must specify at least one file to install.");
- return ERROR_INVALID_ARGUMENTS;
- }
- final int numFiles = installationFiles.size();
- if (numFiles == 0) {
- pw.println("Must specify at least one file to install.");
- return ERROR_INVALID_ARGUMENTS;
- }
-
- final PackageInstaller packageInstaller = mContext.getPackageManager()
- .getPackageInstaller();
- if (packageInstaller == null) {
- pw.println("Failed to get PackageInstaller.");
- return ERROR_COMMAND_EXECUTION;
- }
-
- final LocalIntentReceiver localReceiver = new LocalIntentReceiver();
- boolean success = false;
-
- PackageInstaller.Session session = null;
- try {
- session = packageInstaller.openSession(sessionId);
- for (int i = 0; i < numFiles; i++) {
- InstallationFile file = installationFiles.get(i);
- session.addFile(file.getLocation(), file.getName(), file.getLengthBytes(),
- file.getMetadata(), file.getSignature());
- }
- session.commit(localReceiver.getIntentSender());
- final Intent result = localReceiver.getResult();
- final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE);
- if (status == PackageInstaller.STATUS_SUCCESS) {
- success = true;
- pw.println("Success");
- return 0;
- } else {
- pw.println("Failure ["
- + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
- return ERROR_COMMAND_EXECUTION;
- }
- } catch (Exception e) {
- e.printStackTrace(pw);
- return ERROR_COMMAND_EXECUTION;
- } finally {
- if (!success) {
- try {
- if (session != null) {
- session.abandon();
- }
- } catch (Exception ignore) {
- }
- }
- }
- }
-
- private static class LocalIntentReceiver {
- private final LinkedBlockingQueue<Intent> mResult = new LinkedBlockingQueue<>();
-
- private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
- @Override
- public void send(int code, Intent intent, String resolvedType,
- IBinder whitelistToken,
- IIntentReceiver finishedReceiver, String requiredPermission,
- Bundle options) {
- try {
- mResult.offer(intent, 5, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
- };
-
- public IntentSender getIntentSender() {
- return new IntentSender((IIntentSender) mLocalSender);
- }
-
- public Intent getResult() {
- try {
- return mResult.take();
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
- }
-
- /** Helpers. */
- private Map<String, ParcelFileDescriptor> getShellFileDescriptors() {
- Map<String, ParcelFileDescriptor> fds = new HashMap<>();
- final FileDescriptor outFd = getOutFileDescriptor();
- final FileDescriptor inFd = getInFileDescriptor();
- try {
- fds.put("inFd", ParcelFileDescriptor.dup(inFd));
- fds.put("outFd", ParcelFileDescriptor.dup(outFd));
- return fds;
- } catch (Exception ex) {
- Slog.e(TAG, "Failed to dup FDs");
- return null;
- }
- }
-
- private long parseLong(String arg) {
- long result = -1;
- try {
- result = Long.parseLong(arg);
- } catch (NumberFormatException e) {
- }
- return result;
- }
-
- private int parseInt(String arg) {
- int result = -1;
- try {
- result = Integer.parseInt(arg);
- } catch (NumberFormatException e) {
- }
- return result;
- }
-
- private List<InstallationFile> parseFileArgs(PrintWriter pw) {
- List<InstallationFile> fileList = new ArrayList<>();
- String opt;
- while ((opt = getNextOption()) != null) {
- switch (opt) {
- case "--file": {
- final String fileArgs = getNextArgRequired();
- final String[] args = fileArgs.split(":");
- if (args.length != 3) {
- pw.println("Invalid file args: " + fileArgs);
- return null;
- }
- final String name = args[0];
- final long size = parseLong(args[1]);
- if (size < 0) {
- pw.println("Invalid file size in: " + fileArgs);
- return null;
- }
- final long index = parseLong(args[2]);
- if (index < 0) {
- pw.println("Invalid file index in: " + fileArgs);
- return null;
- }
- final byte[] metadata = String.valueOf(index).getBytes(
- StandardCharsets.UTF_8);
- fileList.add(
- new InstallationFile(LOCATION_DATA_APP, name, size, metadata, null));
- break;
- }
- default:
- break;
- }
- }
- return fileList;
- }
-}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index f24699a..68e6c18 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -44,12 +44,6 @@
}
/**
- * Called by the power manager to tell the input method manager whether it
- * should start watching for wake events.
- */
- public abstract void setInteractive(boolean interactive);
-
- /**
* Hides the current input method, if visible.
*/
public abstract void hideCurrentInputMethod(@SoftInputShowHideReason int reason);
@@ -103,10 +97,6 @@
private static final InputMethodManagerInternal NOP =
new InputMethodManagerInternal() {
@Override
- public void setInteractive(boolean interactive) {
- }
-
- @Override
public void hideCurrentInputMethod(@SoftInputShowHideReason int reason) {
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index e3c545c..eab3393 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -846,6 +846,14 @@
private final WeakHashMap<IBinder, IBinder> mShowRequestWindowMap = new WeakHashMap<>();
/**
+ * Map of generated token to windowToken that is requesting
+ * {@link InputMethodManager#hideSoftInputFromWindow(IBinder, int)}.
+ * This map tracks origin of hideSoftInput requests.
+ */
+ @GuardedBy("mMethodMap")
+ private final WeakHashMap<IBinder, IBinder> mHideRequestWindowMap = new WeakHashMap<>();
+
+ /**
* A ring buffer to store the history of {@link StartInputInfo}.
*/
private static final class StartInputHistory {
@@ -1064,7 +1072,7 @@
== AccessibilityService.SHOW_MODE_HIDDEN;
if (mAccessibilityRequestingNoSoftKeyboard) {
final boolean showRequested = mShowRequested;
- hideCurrentInputLocked(0, null,
+ hideCurrentInputLocked(mCurFocusedWindow, 0, null,
SoftInputShowHideReason.HIDE_SETTINGS_ON_CHANGE);
mShowRequested = showRequested;
} else if (mShowRequested) {
@@ -1695,7 +1703,9 @@
// TODO: Is it really possible that switchUserLocked() happens before system ready?
if (mSystemReady) {
- hideCurrentInputLocked(0, null, SoftInputShowHideReason.HIDE_SWITCH_USER);
+ hideCurrentInputLocked(
+ mCurFocusedWindow, 0, null, SoftInputShowHideReason.HIDE_SWITCH_USER);
+
resetCurrentMethodAndClient(UnbindReason.SWITCH_USER);
buildInputMethodListLocked(initialUserSwitch);
if (TextUtils.isEmpty(mSettings.getSelectedInputMethod())) {
@@ -3040,7 +3050,7 @@
}
@Override
- public boolean hideSoftInput(IInputMethodClient client, int flags,
+ public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken, int flags,
ResultReceiver resultReceiver) {
int uid = Binder.getCallingUid();
synchronized (mMethodMap) {
@@ -3068,7 +3078,7 @@
}
if (DEBUG) Slog.v(TAG, "Client requesting input be hidden");
- return hideCurrentInputLocked(flags, resultReceiver,
+ return hideCurrentInputLocked(windowToken, flags, resultReceiver,
SoftInputShowHideReason.HIDE_SOFT_INPUT);
} finally {
Binder.restoreCallingIdentity(ident);
@@ -3076,7 +3086,7 @@
}
}
- boolean hideCurrentInputLocked(int flags, ResultReceiver resultReceiver,
+ boolean hideCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
if ((flags&InputMethodManager.HIDE_IMPLICIT_ONLY) != 0
&& (mShowExplicitlyRequested || mShowForced)) {
@@ -3100,12 +3110,14 @@
(mImeWindowVis & InputMethodService.IME_ACTIVE) != 0);
boolean res;
if (shouldHideSoftInput) {
+ final Binder hideInputToken = new Binder();
+ mHideRequestWindowMap.put(hideInputToken, windowToken);
// The IME will report its visible state again after the following message finally
// delivered to the IME process as an IPC. Hence the inconsistency between
// IMMS#mInputShown and IMMS#mImeWindowVis should be resolved spontaneously in
// the final state.
- executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOO(MSG_HIDE_SOFT_INPUT,
- reason, mCurMethod, resultReceiver));
+ executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOOO(MSG_HIDE_SOFT_INPUT,
+ reason, mCurMethod, resultReceiver, hideInputToken));
res = true;
} else {
res = false;
@@ -3242,7 +3254,8 @@
Slog.w(TAG, "If you need to impersonate a foreground user/profile from"
+ " a background user, use EditorInfo.targetInputMethodUser with"
+ " INTERACT_ACROSS_USERS_FULL permission.");
- hideCurrentInputLocked(0, null, SoftInputShowHideReason.HIDE_INVALID_USER);
+ hideCurrentInputLocked(
+ mCurFocusedWindow, 0, null, SoftInputShowHideReason.HIDE_INVALID_USER);
return InputBindResult.INVALID_USER;
}
@@ -3305,7 +3318,8 @@
// be behind any soft input window, so hide the
// soft input window if it is shown.
if (DEBUG) Slog.v(TAG, "Unspecified window will hide input");
- hideCurrentInputLocked(InputMethodManager.HIDE_NOT_ALWAYS, null,
+ hideCurrentInputLocked(
+ mCurFocusedWindow, InputMethodManager.HIDE_NOT_ALWAYS, null,
SoftInputShowHideReason.HIDE_UNSPECIFIED_WINDOW);
// If focused display changed, we should unbind current method
@@ -3342,13 +3356,13 @@
case LayoutParams.SOFT_INPUT_STATE_HIDDEN:
if ((softInputMode & LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
if (DEBUG) Slog.v(TAG, "Window asks to hide input going forward");
- hideCurrentInputLocked(0, null,
+ hideCurrentInputLocked(mCurFocusedWindow, 0, null,
SoftInputShowHideReason.HIDE_STATE_HIDDEN_FORWARD_NAV);
}
break;
case LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN:
if (DEBUG) Slog.v(TAG, "Window asks to hide input");
- hideCurrentInputLocked(0, null,
+ hideCurrentInputLocked(mCurFocusedWindow, 0, null,
SoftInputShowHideReason.HIDE_ALWAYS_HIDDEN_STATE);
break;
case LayoutParams.SOFT_INPUT_STATE_VISIBLE:
@@ -3832,7 +3846,7 @@
// Send it to window manager to hide IME from IME target window.
// TODO(b/139861270): send to mCurClient.client once IMMS is aware of
// actual IME target.
- mWindowManagerInternal.hideIme(mCurClient.selfReportedDisplayId);
+ mWindowManagerInternal.hideIme(mHideRequestWindowMap.get(windowToken));
}
} else {
// Send to window manager to show IME after IME layout finishes.
@@ -3872,7 +3886,10 @@
}
long ident = Binder.clearCallingIdentity();
try {
- hideCurrentInputLocked(flags, null, SoftInputShowHideReason.HIDE_MY_SOFT_INPUT);
+ hideCurrentInputLocked(
+ mLastImeTargetWindow, flags, null,
+ SoftInputShowHideReason.HIDE_MY_SOFT_INPUT);
+
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -3969,11 +3986,11 @@
args.recycle();
return true;
case MSG_SHOW_SOFT_INPUT:
- args = (SomeArgs)msg.obj;
+ args = (SomeArgs) msg.obj;
try {
final @SoftInputShowHideReason int reason = msg.arg2;
if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".showSoftInput("
- + msg.arg1 + ", " + args.arg2 + ") for reason: "
+ + args.arg3 + ", " + msg.arg1 + ", " + args.arg2 + ") for reason: "
+ InputMethodDebug.softInputDisplayReasonToString(reason));
((IInputMethod) args.arg1).showSoftInput(
(IBinder) args.arg3, msg.arg1, (ResultReceiver) args.arg2);
@@ -3986,13 +4003,14 @@
args.recycle();
return true;
case MSG_HIDE_SOFT_INPUT:
- args = (SomeArgs)msg.obj;
+ args = (SomeArgs) msg.obj;
try {
final @SoftInputShowHideReason int reason = msg.arg1;
if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".hideSoftInput(0, "
- + args.arg2 + ") for reason: "
+ + args.arg3 + ", " + args.arg2 + ") for reason: "
+ InputMethodDebug.softInputDisplayReasonToString(reason));
- ((IInputMethod)args.arg1).hideSoftInput(0, (ResultReceiver)args.arg2);
+ ((IInputMethod)args.arg1).hideSoftInput(
+ (IBinder) args.arg3, 0, (ResultReceiver)args.arg2);
mSoftInputShowHideHistory.addEntry(
new SoftInputShowHideHistory.Entry(mCurClient,
InputMethodDebug.objToString(mCurFocusedWindow),
@@ -4004,7 +4022,8 @@
case MSG_HIDE_CURRENT_INPUT_METHOD:
synchronized (mMethodMap) {
final @SoftInputShowHideReason int reason = (int) msg.obj;
- hideCurrentInputLocked(0, null, reason);
+ hideCurrentInputLocked(mCurFocusedWindow, 0, null, reason);
+
}
return true;
case MSG_INITIALIZE_IME:
@@ -4094,9 +4113,6 @@
+ ((ClientState)msg.obj).uid);
}
return true;
- case MSG_SET_INTERACTIVE:
- handleSetInteractive(msg.arg1 != 0);
- return true;
case MSG_REPORT_FULLSCREEN_MODE: {
final boolean fullscreen = msg.arg1 != 0;
final ClientState clientState = (ClientState)msg.obj;
@@ -4171,20 +4187,6 @@
return false;
}
- private void handleSetInteractive(final boolean interactive) {
- synchronized (mMethodMap) {
- mIsInteractive = interactive;
- updateSystemUiLocked(interactive ? mImeWindowVis : 0, mBackDisposition);
-
- // Inform the current client of the change in active status
- if (mCurClient != null && mCurClient.client != null) {
- executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIIO(
- MSG_SET_ACTIVE, mIsInteractive ? 1 : 0, mInFullscreenMode ? 1 : 0,
- mCurClient));
- }
- }
- }
-
private boolean chooseNewDefaultIMELocked() {
final InputMethodInfo imi = InputMethodUtils.getMostApplicableDefaultIME(
mSettings.getEnabledInputMethodListLocked());
@@ -4781,13 +4783,6 @@
}
@Override
- public void setInteractive(boolean interactive) {
- // Do everything in handler so as not to block the caller.
- mService.mHandler.obtainMessage(MSG_SET_INTERACTIVE, interactive ? 1 : 0, 0)
- .sendToTarget();
- }
-
- @Override
public void hideCurrentInputMethod(@SoftInputShowHideReason int reason) {
mService.mHandler.removeMessages(MSG_HIDE_CURRENT_INPUT_METHOD);
mService.mHandler.obtainMessage(MSG_HIDE_CURRENT_INPUT_METHOD, reason).sendToTarget();
@@ -5409,7 +5404,7 @@
final String nextIme;
final List<InputMethodInfo> nextEnabledImes;
if (userId == mSettings.getCurrentUserId()) {
- hideCurrentInputLocked(0, null,
+ hideCurrentInputLocked(mCurFocusedWindow, 0, null,
SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND);
unbindCurrentMethodLocked();
// Reset the current IME
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 1aff23b0..0e6c0db 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -170,11 +170,6 @@
LocalServices.addService(InputMethodManagerInternal.class,
new InputMethodManagerInternal() {
@Override
- public void setInteractive(boolean interactive) {
- reportNotSupported();
- }
-
- @Override
public void hideCurrentInputMethod(@SoftInputShowHideReason int reason) {
reportNotSupported();
}
@@ -1500,7 +1495,8 @@
@BinderThread
@Override
public boolean hideSoftInput(
- IInputMethodClient client, int flags, ResultReceiver resultReceiver) {
+ IInputMethodClient client, IBinder windowToken, int flags,
+ ResultReceiver resultReceiver) {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final int userId = UserHandle.getUserId(callingUid);
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index fd8e159..b4ec359 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -79,8 +79,10 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/** Implementation of {@link AppIntegrityManagerService}. */
public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
@@ -101,12 +103,14 @@
private static final String TAG = "AppIntegrityManagerServiceImpl";
private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
- private static final String PACKAGE_INSTALLER = "com.google.android.packageinstaller";
private static final String BASE_APK_FILE = "base.apk";
private static final String ALLOWED_INSTALLERS_METADATA_NAME = "allowed-installers";
private static final String ALLOWED_INSTALLER_DELIMITER = ",";
private static final String INSTALLER_PACKAGE_CERT_DELIMITER = "\\|";
+ private static final Set<String> PACKAGE_INSTALLER = new HashSet<>(
+ Arrays.asList("com.google.android.packageinstaller", "com.android.packageinstaller"));
+
// Access to files inside mRulesDir is protected by mRulesLock;
private final Context mContext;
private final Handler mHandler;
@@ -114,8 +118,6 @@
private final RuleEvaluationEngine mEvaluationEngine;
private final IntegrityFileManager mIntegrityFileManager;
- private final boolean mCheckIntegrityForRuleProviders;
-
/** Create an instance of {@link AppIntegrityManagerServiceImpl}. */
public static AppIntegrityManagerServiceImpl create(Context context) {
HandlerThread handlerThread = new HandlerThread("AppIntegrityManagerServiceHandler");
@@ -126,13 +128,7 @@
LocalServices.getService(PackageManagerInternal.class),
RuleEvaluationEngine.getRuleEvaluationEngine(),
IntegrityFileManager.getInstance(),
- handlerThread.getThreadHandler(),
- Settings.Global.getInt(
- context.getContentResolver(),
- Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER,
- 0)
- == 1
- );
+ handlerThread.getThreadHandler());
}
@VisibleForTesting
@@ -141,14 +137,12 @@
PackageManagerInternal packageManagerInternal,
RuleEvaluationEngine evaluationEngine,
IntegrityFileManager integrityFileManager,
- Handler handler,
- boolean checkIntegrityForRuleProviders) {
+ Handler handler) {
mContext = context;
mPackageManagerInternal = packageManagerInternal;
mEvaluationEngine = evaluationEngine;
mIntegrityFileManager = integrityFileManager;
mHandler = handler;
- mCheckIntegrityForRuleProviders = checkIntegrityForRuleProviders;
IntentFilter integrityVerificationFilter = new IntentFilter();
integrityVerificationFilter.addAction(ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION);
@@ -259,7 +253,7 @@
String installerPackageName = getInstallerPackageName(intent);
// Skip integrity verification if the verifier is doing the install.
- if (!mCheckIntegrityForRuleProviders
+ if (!integrityCheckIncludesRuleProvider()
&& isRuleProvider(installerPackageName)) {
Slog.i(TAG, "Verifier doing the install. Skipping integrity check.");
mPackageManagerInternal.setIntegrityVerificationResult(
@@ -271,8 +265,6 @@
List<String> installerCertificates =
getInstallerCertificateFingerprint(installerPackageName);
- Slog.w(TAG, appCertificates.toString());
-
AppInstallMetadata.Builder builder = new AppInstallMetadata.Builder();
builder.setPackageName(getPackageNameNormalized(packageName));
@@ -376,7 +368,7 @@
// A common way for apps to install packages is to send an intent to PackageInstaller. In
// that case, the installer will always show up as PackageInstaller which is not what we
// want.
- if (installer.equals(PACKAGE_INSTALLER)) {
+ if (PACKAGE_INSTALLER.contains(installer)) {
int originatingUid = intent.getIntExtra(EXTRA_ORIGINATING_UID, -1);
if (originatingUid < 0) {
Slog.e(TAG, "Installer is package installer but originating UID not found.");
@@ -631,4 +623,12 @@
return getAllowedRuleProviders().stream()
.anyMatch(ruleProvider -> ruleProvider.equals(installerPackageName));
}
+
+ private boolean integrityCheckIncludesRuleProvider() {
+ return Settings.Global.getInt(
+ mContext.getContentResolver(),
+ Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER,
+ 0)
+ == 1;
+ }
}
diff --git a/services/core/java/com/android/server/location/GnssConfiguration.java b/services/core/java/com/android/server/location/GnssConfiguration.java
index b3546dc..a3523f2 100644
--- a/services/core/java/com/android/server/location/GnssConfiguration.java
+++ b/services/core/java/com/android/server/location/GnssConfiguration.java
@@ -80,7 +80,7 @@
// Represents an HAL interface version. Instances of this class are created in the JNI layer
// and returned through native methods.
- private static class HalInterfaceVersion {
+ static class HalInterfaceVersion {
final int mMajor;
final int mMinor;
@@ -206,6 +206,10 @@
native_set_satellite_blacklist(constellations, svids);
}
+ HalInterfaceVersion getHalInterfaceVersion() {
+ return native_get_gnss_configuration_version();
+ }
+
interface SetCarrierProperty {
boolean set(int value);
}
@@ -232,8 +236,7 @@
logConfigurations();
- final HalInterfaceVersion gnssConfigurationIfaceVersion =
- native_get_gnss_configuration_version();
+ final HalInterfaceVersion gnssConfigurationIfaceVersion = getHalInterfaceVersion();
if (gnssConfigurationIfaceVersion != null) {
// Set to a range checked value.
if (isConfigEsExtensionSecSupported(gnssConfigurationIfaceVersion)
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 5c2bf26..58e332a 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -807,10 +807,15 @@
locationRequest.setProvider(provider);
- // Ignore location settings if in emergency mode.
- if (isUserEmergency && mNIHandler.getInEmergency()) {
- locationRequest.setLocationSettingsIgnored(true);
- durationMillis *= EMERGENCY_LOCATION_UPDATE_DURATION_MULTIPLIER;
+ // Ignore location settings if in emergency mode. This is only allowed for
+ // isUserEmergency request (introduced in HAL v2.0), or DBH request in HAL v1.1.
+ if (mNIHandler.getInEmergency()) {
+ GnssConfiguration.HalInterfaceVersion halVersion =
+ mGnssConfiguration.getHalInterfaceVersion();
+ if (isUserEmergency || (halVersion.mMajor < 2 && !independentFromGnss)) {
+ locationRequest.setLocationSettingsIgnored(true);
+ durationMillis *= EMERGENCY_LOCATION_UPDATE_DURATION_MULTIPLIER;
+ }
}
Log.i(TAG,
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 15dfab9..6faf674 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -50,7 +50,6 @@
import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.DeviceStateCache;
import android.app.admin.PasswordMetrics;
-import android.app.backup.BackupManager;
import android.app.trust.IStrongAuthTracker;
import android.app.trust.TrustManager;
import android.content.BroadcastReceiver;
@@ -112,10 +111,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
-import com.android.internal.util.Preconditions;
import com.android.internal.widget.ICheckCredentialProgressCallback;
import com.android.internal.widget.ILockSettings;
import com.android.internal.widget.LockPatternUtils;
@@ -701,7 +698,7 @@
// Serial number is never reusued, so we can use it as a distinguisher for user Id reuse.
int serialNumber = mUserManager.getUserSerialNumber(userId);
- int storedSerialNumber = getIntUnchecked(USER_SERIAL_NUMBER_KEY, -1, userId);
+ int storedSerialNumber = mStorage.getInt(USER_SERIAL_NUMBER_KEY, -1, userId);
if (storedSerialNumber != serialNumber) {
// If LockSettingsStorage does not have a copy of the serial number, it could be either
// this is a user created before the serial number recording logic is introduced, or
@@ -710,7 +707,7 @@
if (storedSerialNumber != -1) {
removeUser(userId, /* unknownUser */ true);
}
- setIntUnchecked(USER_SERIAL_NUMBER_KEY, serialNumber, userId);
+ mStorage.setInt(USER_SERIAL_NUMBER_KEY, serialNumber, userId);
}
}
@@ -1069,7 +1066,7 @@
private boolean getSeparateProfileChallengeEnabledInternal(int userId) {
synchronized (mSeparateChallengeLock) {
- return getBooleanUnchecked(SEPARATE_PROFILE_CHALLENGE_KEY, false, userId);
+ return mStorage.getBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, false, userId);
}
}
@@ -1122,94 +1119,49 @@
@Override
public void setBoolean(String key, boolean value, int userId) {
checkWritePermission(userId);
- setStringUnchecked(key, userId, value ? "1" : "0");
+ mStorage.setBoolean(key, value, userId);
}
@Override
public void setLong(String key, long value, int userId) {
checkWritePermission(userId);
- setLongUnchecked(key, value, userId);
- }
-
- private void setLongUnchecked(String key, long value, int userId) {
- setStringUnchecked(key, userId, Long.toString(value));
- }
-
- private void setIntUnchecked(String key, int value, int userId) {
- setStringUnchecked(key, userId, Integer.toString(value));
+ mStorage.setLong(key, value, userId);
}
@Override
public void setString(String key, String value, int userId) {
checkWritePermission(userId);
- setStringUnchecked(key, userId, value);
- }
-
- private void setStringUnchecked(String key, int userId, String value) {
- Preconditions.checkArgument(userId != USER_FRP, "cannot store lock settings for FRP user");
-
- mStorage.writeKeyValue(key, value, userId);
- if (ArrayUtils.contains(SETTINGS_TO_BACKUP, key)) {
- BackupManager.dataChanged("com.android.providers.settings");
- }
+ mStorage.setString(key, value, userId);
}
@Override
public boolean getBoolean(String key, boolean defaultValue, int userId) {
checkReadPermission(key, userId);
- return getBooleanUnchecked(key, defaultValue, userId);
- }
-
- private boolean getBooleanUnchecked(String key, boolean defaultValue, int userId) {
- String value = getStringUnchecked(key, null, userId);
- return TextUtils.isEmpty(value) ?
- defaultValue : (value.equals("1") || value.equals("true"));
+ if (Settings.Secure.LOCK_PATTERN_ENABLED.equals(key)) {
+ return getCredentialTypeInternal(userId) == CREDENTIAL_TYPE_PATTERN;
+ }
+ return mStorage.getBoolean(key, defaultValue, userId);
}
@Override
public long getLong(String key, long defaultValue, int userId) {
checkReadPermission(key, userId);
- return getLongUnchecked(key, defaultValue, userId);
- }
-
- private long getLongUnchecked(String key, long defaultValue, int userId) {
- String value = getStringUnchecked(key, null, userId);
- return TextUtils.isEmpty(value) ? defaultValue : Long.parseLong(value);
- }
-
- private int getIntUnchecked(String key, int defaultValue, int userId) {
- String value = getStringUnchecked(key, null, userId);
- return TextUtils.isEmpty(value) ? defaultValue : Integer.parseInt(value);
+ return mStorage.getLong(key, defaultValue, userId);
}
@Override
public String getString(String key, String defaultValue, int userId) {
checkReadPermission(key, userId);
- return getStringUnchecked(key, defaultValue, userId);
- }
-
- private String getStringUnchecked(String key, String defaultValue, int userId) {
- if (Settings.Secure.LOCK_PATTERN_ENABLED.equals(key)) {
- return getCredentialTypeInternal(userId) == CREDENTIAL_TYPE_PATTERN ? "1" : "0";
- }
- if (userId == USER_FRP) {
- return null;
- }
-
- if (LockPatternUtils.LEGACY_LOCK_PATTERN_ENABLED.equals(key)) {
- key = Settings.Secure.LOCK_PATTERN_ENABLED;
- }
-
- return mStorage.readKeyValue(key, defaultValue, userId);
+ return mStorage.getString(key, defaultValue, userId);
}
private void setKeyguardStoredQuality(int quality, int userId) {
if (DEBUG) Slog.d(TAG, "setKeyguardStoredQuality: user=" + userId + " quality=" + quality);
- setLongUnchecked(LockPatternUtils.PASSWORD_TYPE_KEY, quality, userId);
+ mStorage.setLong(LockPatternUtils.PASSWORD_TYPE_KEY, quality, userId);
}
private int getKeyguardStoredQuality(int userId) {
- return (int) getLongUnchecked(LockPatternUtils.PASSWORD_TYPE_KEY,
+ return (int) mStorage.getLong(LockPatternUtils.PASSWORD_TYPE_KEY,
DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userId);
}
@@ -2493,13 +2445,6 @@
SEPARATE_PROFILE_CHALLENGE_KEY
};
- private static final String[] SETTINGS_TO_BACKUP = new String[] {
- Secure.LOCK_SCREEN_OWNER_INFO_ENABLED,
- Secure.LOCK_SCREEN_OWNER_INFO,
- Secure.LOCK_PATTERN_VISIBLE,
- LockPatternUtils.LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS
- };
-
private class GateKeeperDiedRecipient implements IBinder.DeathRecipient {
@Override
public void binderDied() {
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index fec0189..81d07cc 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -18,8 +18,12 @@
import static android.content.Context.USER_SERVICE;
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+import static com.android.internal.widget.LockPatternUtils.USER_FRP;
+
import android.annotation.Nullable;
import android.app.admin.DevicePolicyManager;
+import android.app.backup.BackupManager;
import android.content.ContentValues;
import android.content.Context;
import android.content.pm.UserInfo;
@@ -29,6 +33,8 @@
import android.os.Environment;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.Settings;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Slog;
@@ -87,6 +93,13 @@
private static final Object DEFAULT = new Object();
+ private static final String[] SETTINGS_TO_BACKUP = new String[] {
+ Settings.Secure.LOCK_SCREEN_OWNER_INFO_ENABLED,
+ Settings.Secure.LOCK_SCREEN_OWNER_INFO,
+ Settings.Secure.LOCK_PATTERN_VISIBLE,
+ LockPatternUtils.LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS
+ };
+
private final DatabaseHelper mOpenHelper;
private final Context mContext;
private final Cache mCache = new Cache();
@@ -136,10 +149,12 @@
mOpenHelper.setCallback(callback);
}
+ @VisibleForTesting(visibility = PACKAGE)
public void writeKeyValue(String key, String value, int userId) {
writeKeyValue(mOpenHelper.getWritableDatabase(), key, value, userId);
}
+ @VisibleForTesting
public void writeKeyValue(SQLiteDatabase db, String key, String value, int userId) {
ContentValues cv = new ContentValues();
cv.put(COLUMN_KEY, key);
@@ -159,6 +174,7 @@
}
+ @VisibleForTesting
public String readKeyValue(String key, String defaultValue, int userId) {
int version;
synchronized (mCache) {
@@ -184,6 +200,28 @@
return result == DEFAULT ? defaultValue : (String) result;
}
+ @VisibleForTesting
+ public void removeKey(String key, int userId) {
+ removeKey(mOpenHelper.getWritableDatabase(), key, userId);
+ }
+
+ private void removeKey(SQLiteDatabase db, String key, int userId) {
+ ContentValues cv = new ContentValues();
+ cv.put(COLUMN_KEY, key);
+ cv.put(COLUMN_USERID, userId);
+
+ db.beginTransaction();
+ try {
+ db.delete(TABLE, COLUMN_KEY + "=? AND " + COLUMN_USERID + "=?",
+ new String[] {key, Integer.toString(userId)});
+ db.setTransactionSuccessful();
+ mCache.removeKey(key, userId);
+ } finally {
+ db.endTransaction();
+ }
+
+ }
+
public void prefetchUser(int userId) {
int version;
synchronized (mCache) {
@@ -537,6 +575,55 @@
}
}
+ public void setBoolean(String key, boolean value, int userId) {
+ setString(key, value ? "1" : "0", userId);
+ }
+
+ public void setLong(String key, long value, int userId) {
+ setString(key, Long.toString(value), userId);
+ }
+
+ public void setInt(String key, int value, int userId) {
+ setString(key, Integer.toString(value), userId);
+ }
+
+ public void setString(String key, String value, int userId) {
+ Preconditions.checkArgument(userId != USER_FRP, "cannot store lock settings for FRP user");
+
+ writeKeyValue(key, value, userId);
+ if (ArrayUtils.contains(SETTINGS_TO_BACKUP, key)) {
+ BackupManager.dataChanged("com.android.providers.settings");
+ }
+ }
+
+ public boolean getBoolean(String key, boolean defaultValue, int userId) {
+ String value = getString(key, null, userId);
+ return TextUtils.isEmpty(value)
+ ? defaultValue : (value.equals("1") || value.equals("true"));
+ }
+
+ public long getLong(String key, long defaultValue, int userId) {
+ String value = getString(key, null, userId);
+ return TextUtils.isEmpty(value) ? defaultValue : Long.parseLong(value);
+ }
+
+ public int getInt(String key, int defaultValue, int userId) {
+ String value = getString(key, null, userId);
+ return TextUtils.isEmpty(value) ? defaultValue : Integer.parseInt(value);
+ }
+
+ public String getString(String key, String defaultValue, int userId) {
+ if (userId == USER_FRP) {
+ return null;
+ }
+
+ if (LockPatternUtils.LEGACY_LOCK_PATTERN_ENABLED.equals(key)) {
+ key = Settings.Secure.LOCK_PATTERN_ENABLED;
+ }
+
+ return readKeyValue(key, defaultValue, userId);
+ }
+
@VisibleForTesting
void closeDatabase() {
mOpenHelper.close();
@@ -764,6 +851,10 @@
putIfUnchanged(CacheKey.TYPE_KEY_VALUE, key, value, userId, version);
}
+ void removeKey(String key, int userId) {
+ remove(CacheKey.TYPE_KEY_VALUE, key, userId);
+ }
+
byte[] peekFile(String fileName) {
return copyOf((byte[]) peek(CacheKey.TYPE_FILE, fileName, -1 /* userId */));
}
@@ -788,6 +879,9 @@
return contains(CacheKey.TYPE_FETCHED, "", userId);
}
+ private synchronized void remove(int type, String key, int userId) {
+ mCache.remove(mCacheKey.set(type, key, userId));
+ }
private synchronized void put(int type, String key, Object value, int userId) {
// Create a new CachKey here because it may be saved in the map if the key is absent.
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index c9e356e..351dd6e 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -16,6 +16,8 @@
package com.android.server.locksettings;
+import static android.os.UserHandle.USER_SYSTEM;
+
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.Context;
@@ -24,6 +26,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserManager;
+import android.provider.Settings;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
@@ -40,6 +43,26 @@
private static final String TAG = "RebootEscrowManager";
/**
+ * Used in the database storage to indicate the boot count at which the reboot escrow was
+ * previously armed.
+ */
+ @VisibleForTesting
+ public static final String REBOOT_ESCROW_ARMED_KEY = "reboot_escrow_armed_count";
+
+ /**
+ * Number of boots until we consider the escrow data to be stale for the purposes of metrics.
+ * <p>
+ * If the delta between the current boot number and the boot number stored when the mechanism
+ * was armed is under this number and the escrow mechanism fails, we report it as a failure of
+ * the mechanism.
+ * <p>
+ * If the delta over this number and escrow fails, we will not report the metric as failed
+ * since there most likely was some other issue if the device rebooted several times before
+ * getting to the escrow restore code.
+ */
+ private static final int BOOT_COUNT_TOLERANCE = 5;
+
+ /**
* Used to track when the reboot escrow is wanted. Should stay true once escrow is requested
* unless clearRebootEscrow is called. This will allow all the active users to be unlocked
* after reboot.
@@ -74,6 +97,7 @@
interface Callbacks {
boolean isUserSecure(int userId);
+
void onRebootEscrowRestored(byte spVersion, byte[] syntheticPassword, int userId);
}
@@ -92,7 +116,8 @@
return (UserManager) mContext.getSystemService(Context.USER_SERVICE);
}
- public @Nullable IRebootEscrow getRebootEscrow() {
+ @Nullable
+ public IRebootEscrow getRebootEscrow() {
try {
return IRebootEscrow.Stub.asInterface(ServiceManager.getService(
"android.hardware.rebootescrow.IRebootEscrow/default"));
@@ -101,6 +126,15 @@
}
return null;
}
+
+ public int getBootCount() {
+ return Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.BOOT_COUNT,
+ 0);
+ }
+
+ public void reportMetric(boolean success) {
+ FrameworkStatsLog.write(FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED, success);
+ }
}
RebootEscrowManager(Context context, Callbacks callbacks, LockSettingsStorage storage) {
@@ -135,7 +169,7 @@
for (UserInfo user : users) {
mStorage.removeRebootEscrow(user.id);
}
- FrameworkStatsLog.write(FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED, false);
+ onEscrowRestoreComplete(false);
return;
}
@@ -143,8 +177,19 @@
for (UserInfo user : rebootEscrowUsers) {
allUsersUnlocked &= restoreRebootEscrowForUser(user.id, escrowKey);
}
- FrameworkStatsLog.write(FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED,
- allUsersUnlocked);
+ onEscrowRestoreComplete(allUsersUnlocked);
+ }
+
+ private void onEscrowRestoreComplete(boolean success) {
+ int previousBootCount = mStorage.getInt(REBOOT_ESCROW_ARMED_KEY, 0, USER_SYSTEM);
+ mStorage.removeKey(REBOOT_ESCROW_ARMED_KEY, USER_SYSTEM);
+
+ int bootCountDelta = mInjector.getBootCount() - previousBootCount;
+ if (bootCountDelta > BOOT_COUNT_TOLERANCE) {
+ return;
+ }
+
+ mInjector.reportMetric(success);
}
private RebootEscrowKey getAndClearRebootEscrowKey() {
@@ -267,6 +312,8 @@
return;
}
+ mStorage.removeKey(REBOOT_ESCROW_ARMED_KEY, USER_SYSTEM);
+
try {
rebootEscrow.storeKey(new byte[32]);
} catch (RemoteException e) {
@@ -308,6 +355,11 @@
} catch (RemoteException e) {
Slog.e(TAG, "Failed escrow secret to RebootEscrow HAL", e);
}
+
+ if (armedRebootEscrow) {
+ mStorage.setInt(REBOOT_ESCROW_ARMED_KEY, mInjector.getBootCount(), USER_SYSTEM);
+ }
+
return armedRebootEscrow;
}
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
index dbaf824..377e731 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
@@ -84,14 +84,12 @@
// Current version of the database files schema
private int mCurrentVersion;
private final WriteBufferRunnable mWriteBufferRunnable;
- private final FileAttrProvider mFileAttrProvider;
// Object containing posted notifications that have not yet been written to disk
@VisibleForTesting
NotificationHistory mBuffer;
- public NotificationHistoryDatabase(Context context, Handler fileWriteHandler, File dir,
- FileAttrProvider fileAttrProvider) {
+ public NotificationHistoryDatabase(Context context, Handler fileWriteHandler, File dir) {
mContext = context;
mAlarmManager = context.getSystemService(AlarmManager.class);
mCurrentVersion = DEFAULT_CURRENT_VERSION;
@@ -101,7 +99,6 @@
mHistoryFiles = new LinkedList<>();
mBuffer = new NotificationHistory();
mWriteBufferRunnable = new WriteBufferRunnable();
- mFileAttrProvider = fileAttrProvider;
IntentFilter deletionFilter = new IntentFilter(ACTION_HISTORY_DELETION);
deletionFilter.addDataScheme(SCHEME_DELETION);
@@ -131,8 +128,8 @@
}
// Sort with newest files first
- Arrays.sort(files, (lhs, rhs) -> Long.compare(mFileAttrProvider.getCreationTime(rhs),
- mFileAttrProvider.getCreationTime(lhs)));
+ Arrays.sort(files, (lhs, rhs) -> Long.compare(Long.parseLong(rhs.getName()),
+ Long.parseLong(lhs.getName())));
for (File file : files) {
mHistoryFiles.addLast(new AtomicFile(file));
@@ -255,10 +252,9 @@
for (int i = mHistoryFiles.size() - 1; i >= 0; i--) {
final AtomicFile currentOldestFile = mHistoryFiles.get(i);
- final long creationTime =
- mFileAttrProvider.getCreationTime(currentOldestFile.getBaseFile());
+ final long creationTime = Long.parseLong(currentOldestFile.getBaseFile().getName());
if (DEBUG) {
- Slog.d(TAG, "Pruning " + currentOldestFile.getBaseFile().getName()
+ Slog.d(TAG, "File " + currentOldestFile.getBaseFile().getName()
+ " created on " + creationTime);
}
if (creationTime <= retentionBoundary.getTimeInMillis()) {
@@ -469,24 +465,4 @@
}
}
}
-
- public static final class NotificationHistoryFileAttrProvider implements
- NotificationHistoryDatabase.FileAttrProvider {
- final static String TAG = "NotifHistoryFileDate";
-
- public long getCreationTime(File file) {
- try {
- BasicFileAttributes attr = Files.readAttributes(FileSystems.getDefault().getPath(
- file.getAbsolutePath()), BasicFileAttributes.class);
- return attr.creationTime().to(TimeUnit.MILLISECONDS);
- } catch (Exception e) {
- Slog.w(TAG, "Cannot read creation data for file; using file name");
- return Long.valueOf(file.getName());
- }
- }
- }
-
- interface FileAttrProvider {
- long getCreationTime(File file);
- }
}
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryDatabaseFactory.java b/services/core/java/com/android/server/notification/NotificationHistoryDatabaseFactory.java
index b4940a5..d9e0d79 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryDatabaseFactory.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryDatabaseFactory.java
@@ -31,11 +31,10 @@
}
public static NotificationHistoryDatabase create(@NonNull Context context,
- @NonNull Handler handler, @NonNull File rootDir,
- @NonNull NotificationHistoryDatabase.FileAttrProvider fileAttrProvider) {
+ @NonNull Handler handler, @NonNull File rootDir) {
if(sTestingNotificationHistoryDb != null) {
return sTestingNotificationHistoryDb;
}
- return new NotificationHistoryDatabase(context, handler, rootDir, fileAttrProvider);
+ return new NotificationHistoryDatabase(context, handler, rootDir);
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryManager.java b/services/core/java/com/android/server/notification/NotificationHistoryManager.java
index 88e0dc6..ab37f67 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryManager.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryManager.java
@@ -38,7 +38,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.IoThread;
-import com.android.server.notification.NotificationHistoryDatabase.NotificationHistoryFileAttrProvider;
import java.io.File;
import java.util.ArrayList;
@@ -291,7 +290,7 @@
final File historyDir = new File(Environment.getDataSystemCeDirectory(userId),
DIRECTORY_PER_USER);
userHistory = NotificationHistoryDatabaseFactory.create(mContext, IoThread.getHandler(),
- historyDir, new NotificationHistoryFileAttrProvider());
+ historyDir);
if (mUserUnlockedStates.get(userId)) {
try {
userHistory.init();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index c9c6d51..c40f1b6 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -51,6 +51,7 @@
import static android.content.Context.BIND_AUTO_CREATE;
import static android.content.Context.BIND_FOREGROUND_SERVICE;
import static android.content.Context.BIND_NOT_PERCEPTIBLE;
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED;
import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
import static android.content.pm.PackageManager.FEATURE_LEANBACK;
@@ -569,17 +570,17 @@
public StatusBarNotification[] getArray(int count, boolean includeSnoozed) {
if (count == 0) count = mBufferSize;
- final StatusBarNotification[] a
- = new StatusBarNotification[Math.min(count, mBuffer.size())];
+ List<StatusBarNotification> a = new ArrayList();
Iterator<Pair<StatusBarNotification, Integer>> iter = descendingIterator();
int i=0;
while (iter.hasNext() && i < count) {
Pair<StatusBarNotification, Integer> pair = iter.next();
if (pair.second != REASON_SNOOZED || includeSnoozed) {
- a[i++] = pair.first;
+ i++;
+ a.add(pair.first);
}
}
- return a;
+ return a.toArray(new StatusBarNotification[a.size()]);
}
}
@@ -2067,19 +2068,16 @@
@Override
public void onStart() {
- SnoozeHelper snoozeHelper = new SnoozeHelper(getContext(), new SnoozeHelper.Callback() {
- @Override
- public void repost(int userId, NotificationRecord r) {
- try {
- if (DBG) {
- Slog.d(TAG, "Reposting " + r.getKey());
- }
- enqueueNotificationInternal(r.getSbn().getPackageName(), r.getSbn().getOpPkg(),
- r.getSbn().getUid(), r.getSbn().getInitialPid(), r.getSbn().getTag(),
- r.getSbn().getId(), r.getSbn().getNotification(), userId);
- } catch (Exception e) {
- Slog.e(TAG, "Cannot un-snooze notification", e);
+ SnoozeHelper snoozeHelper = new SnoozeHelper(getContext(), (userId, r, muteOnReturn) -> {
+ try {
+ if (DBG) {
+ Slog.d(TAG, "Reposting " + r.getKey());
}
+ enqueueNotificationInternal(r.getSbn().getPackageName(), r.getSbn().getOpPkg(),
+ r.getSbn().getUid(), r.getSbn().getInitialPid(), r.getSbn().getTag(),
+ r.getSbn().getId(), r.getSbn().getNotification(), userId, true);
+ } catch (Exception e) {
+ Slog.e(TAG, "Cannot un-snooze notification", e);
}
}, mUserProfiles);
@@ -2699,8 +2697,13 @@
CharSequence title = null;
if (n.extras != null) {
title = n.extras.getCharSequence(Notification.EXTRA_TITLE);
+ if (title == null) {
+ title = n.extras.getCharSequence(Notification.EXTRA_TITLE_BIG);
+ }
}
- return title == null? null : String.valueOf(title);
+ return title == null ? getContext().getResources().getString(
+ com.android.internal.R.string.notification_history_title_placeholder)
+ : String.valueOf(title);
}
/**
@@ -3448,16 +3451,10 @@
ArrayList<ConversationChannelWrapper> conversations =
mPreferencesHelper.getConversations(onlyImportant);
for (ConversationChannelWrapper conversation : conversations) {
- LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery()
- .setPackage(conversation.getPkg())
- .setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED)
- .setShortcutIds(Arrays.asList(
- conversation.getNotificationChannel().getConversationId()));
- List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(
- query, UserHandle.of(UserHandle.getUserId(conversation.getUid())));
- if (shortcuts != null && !shortcuts.isEmpty()) {
- conversation.setShortcutInfo(shortcuts.get(0));
- }
+ conversation.setShortcutInfo(getShortcutInfo(
+ conversation.getNotificationChannel().getConversationId(),
+ conversation.getPkg(),
+ UserHandle.of(UserHandle.getUserId(conversation.getUid()))));
}
return new ParceledListSlice<>(conversations);
}
@@ -3477,16 +3474,10 @@
ArrayList<ConversationChannelWrapper> conversations =
mPreferencesHelper.getConversations(pkg, uid);
for (ConversationChannelWrapper conversation : conversations) {
- LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery()
- .setPackage(pkg)
- .setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED)
- .setShortcutIds(Arrays.asList(
- conversation.getNotificationChannel().getConversationId()));
- List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(
- query, UserHandle.of(UserHandle.getUserId(uid)));
- if (shortcuts != null && !shortcuts.isEmpty()) {
- conversation.setShortcutInfo(shortcuts.get(0));
- }
+ conversation.setShortcutInfo(getShortcutInfo(
+ conversation.getNotificationChannel().getConversationId(),
+ pkg,
+ UserHandle.of(UserHandle.getUserId(uid))));
}
return new ParceledListSlice<>(conversations);
}
@@ -3994,7 +3985,7 @@
synchronized (mNotificationLock) {
final ManagedServiceInfo info =
mAssistants.checkServiceTokenLocked(token);
- unsnoozeNotificationInt(key, info);
+ unsnoozeNotificationInt(key, info, false);
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -4017,7 +4008,7 @@
if (!info.isSystem) {
throw new SecurityException("Not allowed to unsnooze before deadline");
}
- unsnoozeNotificationInt(key, info);
+ unsnoozeNotificationInt(key, info, true);
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -5536,6 +5527,13 @@
void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
final int callingPid, final String tag, final int id, final Notification notification,
int incomingUserId) {
+ enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification,
+ incomingUserId, false);
+ }
+
+ void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
+ final int callingPid, final String tag, final int id, final Notification notification,
+ int incomingUserId, boolean postSilently) {
if (DBG) {
Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
+ " notification=" + notification);
@@ -5616,6 +5614,7 @@
user, null, System.currentTimeMillis());
final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
r.setIsAppImportanceLocked(mPreferencesHelper.getIsAppImportanceLocked(pkg, callingUid));
+ r.setPostSilently(postSilently);
if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
final boolean fgServiceShown = channel.isFgServiceShown();
@@ -5646,6 +5645,8 @@
}
}
+ r.setShortcutInfo(getShortcutInfo(notification.getShortcutId(), pkg, user));
+
if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r,
r.getSbn().getOverrideGroupKey() != null)) {
return;
@@ -5959,20 +5960,33 @@
return false;
}
+ private ShortcutInfo getShortcutInfo(String shortcutId, String packageName, UserHandle user) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (shortcutId == null || packageName == null || user == null) {
+ return null;
+ }
+ LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery();
+ if (packageName != null) {
+ query.setPackage(packageName);
+ }
+ if (shortcutId != null) {
+ query.setShortcutIds(Arrays.asList(shortcutId));
+ }
+ query.setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_CACHED);
+ List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(query, user);
+ ShortcutInfo shortcutInfo = shortcuts != null && shortcuts.size() > 0
+ ? shortcuts.get(0)
+ : null;
+ return shortcutInfo;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
private boolean hasValidShortcutInfo(String shortcutId, String packageName, UserHandle user) {
- LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery();
- if (packageName != null) {
- query.setPackage(packageName);
- }
- if (shortcutId != null) {
- query.setShortcutIds(Arrays.asList(shortcutId));
- }
- query.setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED);
- List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(query, user);
- ShortcutInfo shortcutInfo = shortcuts != null && shortcuts.size() > 0
- ? shortcuts.get(0)
- : null;
- return shortcutInfo != null;
+ ShortcutInfo shortcutInfo = getShortcutInfo(shortcutId, packageName, user);
+ return shortcutInfo != null && shortcutInfo.isLongLived();
}
private void logBubbleError(String key, String failureMessage) {
@@ -7036,6 +7050,11 @@
return true;
}
+ // Suppressed because a user manually unsnoozed something (or similar)
+ if (record.shouldPostSilently()) {
+ return true;
+ }
+
// muted by listener
final String disableEffects = disableNotificationEffects(record);
if (disableEffects != null) {
@@ -8050,13 +8069,12 @@
mHandler.post(new SnoozeNotificationRunnable(key, duration, snoozeCriterionId));
}
- void unsnoozeNotificationInt(String key, ManagedServiceInfo listener) {
+ void unsnoozeNotificationInt(String key, ManagedServiceInfo listener, boolean muteOnReturn) {
String listenerName = listener == null ? null : listener.component.toShortString();
if (DBG) {
Slog.d(TAG, String.format("unsnooze event(%s, %s)", key, listenerName));
}
- mSnoozeHelper.cleanupPersistedContext(key);
- mSnoozeHelper.repost(key);
+ mSnoozeHelper.repost(key, muteOnReturn);
handleSavePolicyFile();
}
@@ -8571,7 +8589,8 @@
record.getSmartReplies(),
record.canBubble(),
record.isInterruptive(),
- record.isConversation()
+ record.isConversation(),
+ record.getShortcutInfo()
);
rankings.add(ranking);
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index f92e1fc..3f24b38 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -35,6 +35,7 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.ShortcutInfo;
import android.graphics.Bitmap;
import android.media.AudioAttributes;
import android.media.AudioSystem;
@@ -166,6 +167,7 @@
private boolean mAllowBubble;
private Light mLight;
private boolean mIsNotConversationOverride;
+ private ShortcutInfo mShortcutInfo;
/**
* This list contains system generated smart actions from NAS, app-generated smart actions are
* stored in Notification.actions with isContextual() set to true.
@@ -185,6 +187,7 @@
private boolean mSuggestionsGeneratedByAssistant;
private boolean mEditChoicesBeforeSending;
private boolean mHasSeenSmartReplies;
+ private boolean mPostSilently;
/**
* Whether this notification (and its channels) should be considered user locked. Used in
* conjunction with user sentiment calculation.
@@ -854,6 +857,17 @@
return mHidden;
}
+ /**
+ * Override of all alerting information on the channel and notification. Used when notifications
+ * are reposted in response to direct user action and thus don't need to alert.
+ */
+ public void setPostSilently(boolean postSilently) {
+ mPostSilently = postSilently;
+ }
+
+ public boolean shouldPostSilently() {
+ return mPostSilently;
+ }
public void setSuppressedVisualEffects(int effects) {
mSuppressedVisualEffects = effects;
@@ -1338,14 +1352,24 @@
return hasCustomRemoteView && !hasDecoratedStyle;
}
- /** Whether this notification is a conversation notification. */
+ public void setShortcutInfo(ShortcutInfo shortcutInfo) {
+ mShortcutInfo = shortcutInfo;
+ }
+
+ public ShortcutInfo getShortcutInfo() {
+ return mShortcutInfo;
+ }
+
+ /**
+ * Whether this notification is a conversation notification.
+ */
public boolean isConversation() {
Notification notification = getNotification();
if (mChannel.isDemoted()
|| !Notification.MessagingStyle.class.equals(notification.getNotificationStyle())) {
return false;
}
- if (notification.getShortcutId() == null
+ if (mShortcutInfo == null
&& !FeatureFlagUtils.isEnabled(
mContext, FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ)) {
return false;
@@ -1353,7 +1377,6 @@
if (mIsNotConversationOverride) {
return false;
}
- // STOPSHIP b/137397357: Check shortcut to make a further decision
return true;
}
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index d60c291..9a9e733 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -106,6 +106,8 @@
private ArrayMap<String, Integer> mUsers = new ArrayMap<>();
private Callback mCallback;
+ private final Object mLock = new Object();
+
public SnoozeHelper(Context context, Callback callback,
ManagedServices.UserProfiles userProfiles) {
mContext = context;
@@ -122,41 +124,52 @@
}
void cleanupPersistedContext(String key){
- int userId = mUsers.get(key);
- String pkg = mPackages.get(key);
- synchronized (mPersistedSnoozedNotificationsWithContext) {
- removeRecord(pkg, key, userId, mPersistedSnoozedNotificationsWithContext);
+ synchronized (mLock) {
+ int userId = mUsers.get(key);
+ String pkg = mPackages.get(key);
+ removeRecordLocked(pkg, key, userId, mPersistedSnoozedNotificationsWithContext);
}
}
- //This function has a side effect of removing the time from the list of persisted notifications.
- //IT IS NOT IDEMPOTENT!
@NonNull
protected Long getSnoozeTimeForUnpostedNotification(int userId, String pkg, String key) {
- Long time;
- synchronized (mPersistedSnoozedNotifications) {
- time = removeRecord(pkg, key, userId, mPersistedSnoozedNotifications);
+ Long time = null;
+ synchronized (mLock) {
+ ArrayMap<String, Long> snoozed =
+ mPersistedSnoozedNotifications.get(getPkgKey(userId, pkg));
+ if (snoozed != null) {
+ time = snoozed.get(key);
+ }
}
if (time == null) {
- return 0L;
+ time = 0L;
}
return time;
}
protected String getSnoozeContextForUnpostedNotification(int userId, String pkg, String key) {
- synchronized (mPersistedSnoozedNotificationsWithContext) {
- return removeRecord(pkg, key, userId, mPersistedSnoozedNotificationsWithContext);
+ synchronized (mLock) {
+ ArrayMap<String, String> snoozed =
+ mPersistedSnoozedNotificationsWithContext.get(getPkgKey(userId, pkg));
+ if (snoozed != null) {
+ return snoozed.get(key);
+ }
}
+ return null;
}
protected boolean isSnoozed(int userId, String pkg, String key) {
- return mSnoozedNotifications.containsKey(getPkgKey(userId, pkg))
- && mSnoozedNotifications.get(getPkgKey(userId, pkg)).containsKey(key);
+ synchronized (mLock) {
+ return mSnoozedNotifications.containsKey(getPkgKey(userId, pkg))
+ && mSnoozedNotifications.get(getPkgKey(userId, pkg)).containsKey(key);
+ }
}
protected Collection<NotificationRecord> getSnoozed(int userId, String pkg) {
- if (mSnoozedNotifications.containsKey(getPkgKey(userId, pkg))) {
- return mSnoozedNotifications.get(getPkgKey(userId, pkg)).values();
+ synchronized (mLock) {
+ if (mSnoozedNotifications.containsKey(getPkgKey(userId, pkg))) {
+ return mSnoozedNotifications.get(getPkgKey(userId, pkg)).values();
+ }
}
return Collections.EMPTY_LIST;
}
@@ -165,14 +178,16 @@
ArrayList<NotificationRecord> getNotifications(String pkg,
String groupKey, Integer userId) {
ArrayList<NotificationRecord> records = new ArrayList<>();
- ArrayMap<String, NotificationRecord> allRecords =
- mSnoozedNotifications.get(getPkgKey(userId, pkg));
- if (allRecords != null) {
- for (int i = 0; i < allRecords.size(); i++) {
- NotificationRecord r = allRecords.valueAt(i);
- String currentGroupKey = r.getSbn().getGroup();
- if (Objects.equals(currentGroupKey, groupKey)) {
- records.add(r);
+ synchronized (mLock) {
+ ArrayMap<String, NotificationRecord> allRecords =
+ mSnoozedNotifications.get(getPkgKey(userId, pkg));
+ if (allRecords != null) {
+ for (int i = 0; i < allRecords.size(); i++) {
+ NotificationRecord r = allRecords.valueAt(i);
+ String currentGroupKey = r.getSbn().getGroup();
+ if (Objects.equals(currentGroupKey, groupKey)) {
+ records.add(r);
+ }
}
}
}
@@ -180,30 +195,34 @@
}
protected NotificationRecord getNotification(String key) {
- if (!mUsers.containsKey(key) || !mPackages.containsKey(key)) {
- Slog.w(TAG, "Snoozed data sets no longer agree for " + key);
- return null;
+ synchronized (mLock) {
+ if (!mUsers.containsKey(key) || !mPackages.containsKey(key)) {
+ Slog.w(TAG, "Snoozed data sets no longer agree for " + key);
+ return null;
+ }
+ int userId = mUsers.get(key);
+ String pkg = mPackages.get(key);
+ ArrayMap<String, NotificationRecord> snoozed =
+ mSnoozedNotifications.get(getPkgKey(userId, pkg));
+ if (snoozed == null) {
+ return null;
+ }
+ return snoozed.get(key);
}
- int userId = mUsers.get(key);
- String pkg = mPackages.get(key);
- ArrayMap<String, NotificationRecord> snoozed =
- mSnoozedNotifications.get(getPkgKey(userId, pkg));
- if (snoozed == null) {
- return null;
- }
- return snoozed.get(key);
}
protected @NonNull List<NotificationRecord> getSnoozed() {
- // caller filters records based on the current user profiles and listener access, so just
- // return everything
- List<NotificationRecord> snoozed= new ArrayList<>();
- for (String userPkgKey : mSnoozedNotifications.keySet()) {
- ArrayMap<String, NotificationRecord> snoozedRecords =
- mSnoozedNotifications.get(userPkgKey);
- snoozed.addAll(snoozedRecords.values());
+ synchronized (mLock) {
+ // caller filters records based on the current user profiles and listener access, so just
+ // return everything
+ List<NotificationRecord> snoozed = new ArrayList<>();
+ for (String userPkgKey : mSnoozedNotifications.keySet()) {
+ ArrayMap<String, NotificationRecord> snoozedRecords =
+ mSnoozedNotifications.get(userPkgKey);
+ snoozed.addAll(snoozedRecords.values());
+ }
+ return snoozed;
}
- return snoozed;
}
/**
@@ -216,9 +235,9 @@
snooze(record);
scheduleRepost(pkg, key, userId, duration);
- Long activateAt = SystemClock.elapsedRealtime() + duration;
- synchronized (mPersistedSnoozedNotifications) {
- storeRecord(pkg, key, userId, mPersistedSnoozedNotifications, activateAt);
+ Long activateAt = System.currentTimeMillis() + duration;
+ synchronized (mLock) {
+ storeRecordLocked(pkg, key, userId, mPersistedSnoozedNotifications, activateAt);
}
}
@@ -228,8 +247,8 @@
protected void snooze(NotificationRecord record, String contextId) {
int userId = record.getUser().getIdentifier();
if (contextId != null) {
- synchronized (mPersistedSnoozedNotificationsWithContext) {
- storeRecord(record.getSbn().getPackageName(), record.getKey(),
+ synchronized (mLock) {
+ storeRecordLocked(record.getSbn().getPackageName(), record.getKey(),
userId, mPersistedSnoozedNotificationsWithContext, contextId);
}
}
@@ -241,25 +260,26 @@
if (DEBUG) {
Slog.d(TAG, "Snoozing " + record.getKey());
}
- storeRecord(record.getSbn().getPackageName(), record.getKey(),
- userId, mSnoozedNotifications, record);
+ synchronized (mLock) {
+ storeRecordLocked(record.getSbn().getPackageName(), record.getKey(),
+ userId, mSnoozedNotifications, record);
+ }
}
- private <T> void storeRecord(String pkg, String key, Integer userId,
+ private <T> void storeRecordLocked(String pkg, String key, Integer userId,
ArrayMap<String, ArrayMap<String, T>> targets, T object) {
+ mPackages.put(key, pkg);
+ mUsers.put(key, userId);
ArrayMap<String, T> keyToValue = targets.get(getPkgKey(userId, pkg));
if (keyToValue == null) {
keyToValue = new ArrayMap<>();
}
keyToValue.put(key, object);
targets.put(getPkgKey(userId, pkg), keyToValue);
-
- mPackages.put(key, pkg);
- mUsers.put(key, userId);
}
- private <T> T removeRecord(String pkg, String key, Integer userId,
+ private <T> T removeRecordLocked(String pkg, String key, Integer userId,
ArrayMap<String, ArrayMap<String, T>> targets) {
T object = null;
ArrayMap<String, T> keyToValue = targets.get(getPkgKey(userId, pkg));
@@ -274,15 +294,17 @@
}
protected boolean cancel(int userId, String pkg, String tag, int id) {
- ArrayMap<String, NotificationRecord> recordsForPkg =
- mSnoozedNotifications.get(getPkgKey(userId, pkg));
- if (recordsForPkg != null) {
- final Set<Map.Entry<String, NotificationRecord>> records = recordsForPkg.entrySet();
- for (Map.Entry<String, NotificationRecord> record : records) {
- final StatusBarNotification sbn = record.getValue().getSbn();
- if (Objects.equals(sbn.getTag(), tag) && sbn.getId() == id) {
- record.getValue().isCanceled = true;
- return true;
+ synchronized (mLock) {
+ ArrayMap<String, NotificationRecord> recordsForPkg =
+ mSnoozedNotifications.get(getPkgKey(userId, pkg));
+ if (recordsForPkg != null) {
+ final Set<Map.Entry<String, NotificationRecord>> records = recordsForPkg.entrySet();
+ for (Map.Entry<String, NotificationRecord> record : records) {
+ final StatusBarNotification sbn = record.getValue().getSbn();
+ if (Objects.equals(sbn.getTag(), tag) && sbn.getId() == id) {
+ record.getValue().isCanceled = true;
+ return true;
+ }
}
}
}
@@ -290,125 +312,149 @@
}
protected void cancel(int userId, boolean includeCurrentProfiles) {
- if (mSnoozedNotifications.size() == 0) {
- return;
- }
- IntArray userIds = new IntArray();
- userIds.add(userId);
- if (includeCurrentProfiles) {
- userIds = mUserProfiles.getCurrentProfileIds();
- }
- for (ArrayMap<String, NotificationRecord> snoozedRecords : mSnoozedNotifications.values()) {
- for (NotificationRecord r : snoozedRecords.values()) {
- if (userIds.binarySearch(r.getUserId()) >= 0) {
- r.isCanceled = true;
+ synchronized (mLock) {
+ if (mSnoozedNotifications.size() == 0) {
+ return;
+ }
+ IntArray userIds = new IntArray();
+ userIds.add(userId);
+ if (includeCurrentProfiles) {
+ userIds = mUserProfiles.getCurrentProfileIds();
+ }
+ for (ArrayMap<String, NotificationRecord> snoozedRecords : mSnoozedNotifications.values()) {
+ for (NotificationRecord r : snoozedRecords.values()) {
+ if (userIds.binarySearch(r.getUserId()) >= 0) {
+ r.isCanceled = true;
+ }
}
}
}
}
protected boolean cancel(int userId, String pkg) {
- ArrayMap<String, NotificationRecord> records =
- mSnoozedNotifications.get(getPkgKey(userId, pkg));
- if (records == null) {
- return false;
+ synchronized (mLock) {
+ ArrayMap<String, NotificationRecord> records =
+ mSnoozedNotifications.get(getPkgKey(userId, pkg));
+ if (records == null) {
+ return false;
+ }
+ int N = records.size();
+ for (int i = 0; i < N; i++) {
+ records.valueAt(i).isCanceled = true;
+ }
+ return true;
}
- int N = records.size();
- for (int i = 0; i < N; i++) {
- records.valueAt(i).isCanceled = true;
- }
- return true;
}
/**
* Updates the notification record so the most up to date information is shown on re-post.
*/
protected void update(int userId, NotificationRecord record) {
- ArrayMap<String, NotificationRecord> records =
- mSnoozedNotifications.get(getPkgKey(userId, record.getSbn().getPackageName()));
- if (records == null) {
- return;
- }
- records.put(record.getKey(), record);
- }
-
- protected void repost(String key) {
- Integer userId = mUsers.get(key);
- if (userId != null) {
- repost(key, userId);
+ synchronized (mLock) {
+ ArrayMap<String, NotificationRecord> records =
+ mSnoozedNotifications.get(getPkgKey(userId, record.getSbn().getPackageName()));
+ if (records == null) {
+ return;
+ }
+ records.put(record.getKey(), record);
}
}
- protected void repost(String key, int userId) {
- final String pkg = mPackages.remove(key);
- ArrayMap<String, NotificationRecord> records =
- mSnoozedNotifications.get(getPkgKey(userId, pkg));
- if (records == null) {
- return;
+ protected void repost(String key, boolean muteOnReturn) {
+ synchronized (mLock) {
+ Integer userId = mUsers.get(key);
+ if (userId != null) {
+ repost(key, userId, muteOnReturn);
+ }
}
- final NotificationRecord record = records.remove(key);
- mPackages.remove(key);
- mUsers.remove(key);
+ }
+
+ protected void repost(String key, int userId, boolean muteOnReturn) {
+ NotificationRecord record;
+ synchronized (mLock) {
+ final String pkg = mPackages.remove(key);
+ mUsers.remove(key);
+ removeRecordLocked(pkg, key, userId, mPersistedSnoozedNotifications);
+ removeRecordLocked(pkg, key, userId, mPersistedSnoozedNotificationsWithContext);
+ ArrayMap<String, NotificationRecord> records =
+ mSnoozedNotifications.get(getPkgKey(userId, pkg));
+ if (records == null) {
+ return;
+ }
+ record = records.remove(key);
+
+ }
if (record != null && !record.isCanceled) {
- final PendingIntent pi = createPendingIntent(pkg, record.getKey(), userId);
+ final PendingIntent pi = createPendingIntent(
+ record.getSbn().getPackageName(), record.getKey(), userId);
mAm.cancel(pi);
MetricsLogger.action(record.getLogMaker()
.setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED)
.setType(MetricsProto.MetricsEvent.TYPE_OPEN));
- mCallback.repost(userId, record);
+ mCallback.repost(userId, record, muteOnReturn);
}
}
protected void repostGroupSummary(String pkg, int userId, String groupKey) {
- ArrayMap<String, NotificationRecord> recordsByKey
- = mSnoozedNotifications.get(getPkgKey(userId, pkg));
- if (recordsByKey == null) {
- return;
- }
-
- String groupSummaryKey = null;
- int N = recordsByKey.size();
- for (int i = 0; i < N; i++) {
- final NotificationRecord potentialGroupSummary = recordsByKey.valueAt(i);
- if (potentialGroupSummary.getSbn().isGroup()
- && potentialGroupSummary.getNotification().isGroupSummary()
- && groupKey.equals(potentialGroupSummary.getGroupKey())) {
- groupSummaryKey = potentialGroupSummary.getKey();
- break;
+ synchronized (mLock) {
+ ArrayMap<String, NotificationRecord> recordsByKey
+ = mSnoozedNotifications.get(getPkgKey(userId, pkg));
+ if (recordsByKey == null) {
+ return;
}
- }
- if (groupSummaryKey != null) {
- NotificationRecord record = recordsByKey.remove(groupSummaryKey);
- mPackages.remove(groupSummaryKey);
- mUsers.remove(groupSummaryKey);
+ String groupSummaryKey = null;
+ int N = recordsByKey.size();
+ for (int i = 0; i < N; i++) {
+ final NotificationRecord potentialGroupSummary = recordsByKey.valueAt(i);
+ if (potentialGroupSummary.getSbn().isGroup()
+ && potentialGroupSummary.getNotification().isGroupSummary()
+ && groupKey.equals(potentialGroupSummary.getGroupKey())) {
+ groupSummaryKey = potentialGroupSummary.getKey();
+ break;
+ }
+ }
- if (record != null && !record.isCanceled) {
- MetricsLogger.action(record.getLogMaker()
- .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED)
- .setType(MetricsProto.MetricsEvent.TYPE_OPEN));
- mCallback.repost(userId, record);
+ if (groupSummaryKey != null) {
+ NotificationRecord record = recordsByKey.remove(groupSummaryKey);
+ mPackages.remove(groupSummaryKey);
+ mUsers.remove(groupSummaryKey);
+
+ if (record != null && !record.isCanceled) {
+ Runnable runnable = () -> {
+ MetricsLogger.action(record.getLogMaker()
+ .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED)
+ .setType(MetricsProto.MetricsEvent.TYPE_OPEN));
+ mCallback.repost(userId, record, false);
+ };
+ runnable.run();
+ }
}
}
}
protected void clearData(int userId, String pkg) {
- ArrayMap<String, NotificationRecord> records =
- mSnoozedNotifications.get(getPkgKey(userId, pkg));
- if (records == null) {
- return;
- }
- for (int i = records.size() - 1; i >= 0; i--) {
- final NotificationRecord r = records.removeAt(i);
- if (r != null) {
- mPackages.remove(r.getKey());
- mUsers.remove(r.getKey());
- final PendingIntent pi = createPendingIntent(pkg, r.getKey(), userId);
- mAm.cancel(pi);
- MetricsLogger.action(r.getLogMaker()
- .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED)
- .setType(MetricsProto.MetricsEvent.TYPE_DISMISS));
+ synchronized (mLock) {
+ ArrayMap<String, NotificationRecord> records =
+ mSnoozedNotifications.get(getPkgKey(userId, pkg));
+ if (records == null) {
+ return;
+ }
+ for (int i = records.size() - 1; i >= 0; i--) {
+ final NotificationRecord r = records.removeAt(i);
+ if (r != null) {
+ mPackages.remove(r.getKey());
+ mUsers.remove(r.getKey());
+ Runnable runnable = () -> {
+ final PendingIntent pi = createPendingIntent(pkg, r.getKey(), userId);
+ mAm.cancel(pi);
+ MetricsLogger.action(r.getLogMaker()
+ .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED)
+ .setType(MetricsProto.MetricsEvent.TYPE_DISMISS));
+ };
+ runnable.run();
+ }
}
}
}
@@ -425,93 +471,102 @@
}
public void scheduleRepostsForPersistedNotifications(long currentTime) {
- for (ArrayMap<String, Long> snoozed : mPersistedSnoozedNotifications.values()) {
- for (int i = 0; i < snoozed.size(); i++) {
- String key = snoozed.keyAt(i);
- Long time = snoozed.valueAt(i);
- String pkg = mPackages.get(key);
- Integer userId = mUsers.get(key);
- if (time == null || pkg == null || userId == null) {
- Slog.w(TAG, "data out of sync: " + time + "|" + pkg + "|" + userId);
- continue;
- }
- if (time != null && time > currentTime) {
- scheduleRepostAtTime(pkg, key, userId, time);
+ synchronized (mLock) {
+ for (ArrayMap<String, Long> snoozed : mPersistedSnoozedNotifications.values()) {
+ for (int i = 0; i < snoozed.size(); i++) {
+ String key = snoozed.keyAt(i);
+ Long time = snoozed.valueAt(i);
+ String pkg = mPackages.get(key);
+ Integer userId = mUsers.get(key);
+ if (time == null || pkg == null || userId == null) {
+ Slog.w(TAG, "data out of sync: " + time + "|" + pkg + "|" + userId);
+ continue;
+ }
+ if (time != null && time > currentTime) {
+ scheduleRepostAtTime(pkg, key, userId, time);
+ }
}
}
-
}
}
private void scheduleRepost(String pkg, String key, int userId, long duration) {
- scheduleRepostAtTime(pkg, key, userId, SystemClock.elapsedRealtime() + duration);
+ scheduleRepostAtTime(pkg, key, userId, System.currentTimeMillis() + duration);
}
private void scheduleRepostAtTime(String pkg, String key, int userId, long time) {
- long identity = Binder.clearCallingIdentity();
- try {
- final PendingIntent pi = createPendingIntent(pkg, key, userId);
- mAm.cancel(pi);
- if (DEBUG) Slog.d(TAG, "Scheduling evaluate for " + new Date(time));
- mAm.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, time, pi);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ Runnable runnable = () -> {
+ long identity = Binder.clearCallingIdentity();
+ try {
+ final PendingIntent pi = createPendingIntent(pkg, key, userId);
+ mAm.cancel(pi);
+ if (DEBUG) Slog.d(TAG, "Scheduling evaluate for " + new Date(time));
+ mAm.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, time, pi);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ };
+ runnable.run();
}
public void dump(PrintWriter pw, NotificationManagerService.DumpFilter filter) {
- pw.println("\n Snoozed notifications:");
- for (String userPkgKey : mSnoozedNotifications.keySet()) {
- pw.print(INDENT);
- pw.println("key: " + userPkgKey);
- ArrayMap<String, NotificationRecord> snoozedRecords =
- mSnoozedNotifications.get(userPkgKey);
- Set<String> snoozedKeys = snoozedRecords.keySet();
- for (String key : snoozedKeys) {
+ synchronized (mLock) {
+ pw.println("\n Snoozed notifications:");
+ for (String userPkgKey : mSnoozedNotifications.keySet()) {
pw.print(INDENT);
- pw.print(INDENT);
- pw.print(INDENT);
- pw.println(key);
+ pw.println("key: " + userPkgKey);
+ ArrayMap<String, NotificationRecord> snoozedRecords =
+ mSnoozedNotifications.get(userPkgKey);
+ Set<String> snoozedKeys = snoozedRecords.keySet();
+ for (String key : snoozedKeys) {
+ pw.print(INDENT);
+ pw.print(INDENT);
+ pw.print(INDENT);
+ pw.println(key);
+ }
}
- }
- pw.println("\n Pending snoozed notifications");
- for (String userPkgKey : mPersistedSnoozedNotifications.keySet()) {
- pw.print(INDENT);
- pw.println("key: " + userPkgKey);
- ArrayMap<String, Long> snoozedRecords =
- mPersistedSnoozedNotifications.get(userPkgKey);
- if (snoozedRecords == null) {
- continue;
- }
- Set<String> snoozedKeys = snoozedRecords.keySet();
- for (String key : snoozedKeys) {
+ pw.println("\n Pending snoozed notifications");
+ for (String userPkgKey : mPersistedSnoozedNotifications.keySet()) {
pw.print(INDENT);
- pw.print(INDENT);
- pw.print(INDENT);
- pw.print(key);
- pw.print(INDENT);
- pw.println(snoozedRecords.get(key));
+ pw.println("key: " + userPkgKey);
+ ArrayMap<String, Long> snoozedRecords =
+ mPersistedSnoozedNotifications.get(userPkgKey);
+ if (snoozedRecords == null) {
+ continue;
+ }
+ Set<String> snoozedKeys = snoozedRecords.keySet();
+ for (String key : snoozedKeys) {
+ pw.print(INDENT);
+ pw.print(INDENT);
+ pw.print(INDENT);
+ pw.print(key);
+ pw.print(INDENT);
+ pw.println(snoozedRecords.get(key));
+ }
}
}
}
protected void writeXml(XmlSerializer out) throws IOException {
- final long currentTime = System.currentTimeMillis();
- out.startTag(null, XML_TAG_NAME);
- writeXml(out, mPersistedSnoozedNotifications, XML_SNOOZED_NOTIFICATION,
- value -> {
- if (value < currentTime) {
- return;
- }
- out.attribute(null, XML_SNOOZED_NOTIFICATION_TIME,
- value.toString());
- });
- writeXml(out, mPersistedSnoozedNotificationsWithContext, XML_SNOOZED_NOTIFICATION_CONTEXT,
- value -> {
- out.attribute(null, XML_SNOOZED_NOTIFICATION_CONTEXT_ID,
- value);
- });
- out.endTag(null, XML_TAG_NAME);
+ synchronized (mLock) {
+ final long currentTime = System.currentTimeMillis();
+ out.startTag(null, XML_TAG_NAME);
+ writeXml(out, mPersistedSnoozedNotifications, XML_SNOOZED_NOTIFICATION,
+ value -> {
+ if (value < currentTime) {
+ return;
+ }
+ out.attribute(null, XML_SNOOZED_NOTIFICATION_TIME,
+ value.toString());
+ });
+ writeXml(out, mPersistedSnoozedNotificationsWithContext,
+ XML_SNOOZED_NOTIFICATION_CONTEXT,
+ value -> {
+ out.attribute(null, XML_SNOOZED_NOTIFICATION_CONTEXT_ID,
+ value);
+ });
+ out.endTag(null, XML_TAG_NAME);
+ }
}
private interface Inserter<T> {
@@ -522,32 +577,35 @@
ArrayMap<String, ArrayMap<String, T>> targets, String tag,
Inserter<T> attributeInserter)
throws IOException {
- synchronized (targets) {
- final int M = targets.size();
- for (int i = 0; i < M; i++) {
- // T is a String (snoozed until context) or Long (snoozed until time)
- ArrayMap<String, T> keyToValue = targets.valueAt(i);
- for (int j = 0; j < keyToValue.size(); j++) {
- String key = keyToValue.keyAt(j);
- T value = keyToValue.valueAt(j);
- String pkg = mPackages.get(key);
- Integer userId = mUsers.get(key);
+ final int M = targets.size();
+ for (int i = 0; i < M; i++) {
+ // T is a String (snoozed until context) or Long (snoozed until time)
+ ArrayMap<String, T> keyToValue = targets.valueAt(i);
+ for (int j = 0; j < keyToValue.size(); j++) {
+ String key = keyToValue.keyAt(j);
+ T value = keyToValue.valueAt(j);
+ String pkg = mPackages.get(key);
+ Integer userId = mUsers.get(key);
- out.startTag(null, tag);
-
- attributeInserter.insert(value);
-
- out.attribute(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL,
- XML_SNOOZED_NOTIFICATION_VERSION);
- out.attribute(null, XML_SNOOZED_NOTIFICATION_KEY, key);
-
-
- out.attribute(null, XML_SNOOZED_NOTIFICATION_PKG, pkg);
- out.attribute(null, XML_SNOOZED_NOTIFICATION_USER_ID,
- String.valueOf(userId));
-
- out.endTag(null, tag);
+ if (pkg == null || userId == null) {
+ Slog.w(TAG, "pkg " + pkg + " or user " + userId + " missing for " + key);
+ continue;
}
+
+ out.startTag(null, tag);
+
+ attributeInserter.insert(value);
+
+ out.attribute(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL,
+ XML_SNOOZED_NOTIFICATION_VERSION);
+ out.attribute(null, XML_SNOOZED_NOTIFICATION_KEY, key);
+
+
+ out.attribute(null, XML_SNOOZED_NOTIFICATION_PKG, pkg);
+ out.attribute(null, XML_SNOOZED_NOTIFICATION_USER_ID,
+ String.valueOf(userId));
+
+ out.endTag(null, tag);
}
}
}
@@ -575,16 +633,18 @@
final Long time = XmlUtils.readLongAttribute(
parser, XML_SNOOZED_NOTIFICATION_TIME, 0);
if (time > currentTime) { //only read new stuff
- synchronized (mPersistedSnoozedNotifications) {
- storeRecord(pkg, key, userId, mPersistedSnoozedNotifications, time);
+ synchronized (mLock) {
+ storeRecordLocked(
+ pkg, key, userId, mPersistedSnoozedNotifications, time);
}
}
}
if (tag.equals(XML_SNOOZED_NOTIFICATION_CONTEXT)) {
final String creationId = parser.getAttributeValue(
null, XML_SNOOZED_NOTIFICATION_CONTEXT_ID);
- synchronized (mPersistedSnoozedNotificationsWithContext) {
- storeRecord(pkg, key, userId, mPersistedSnoozedNotificationsWithContext,
+ synchronized (mLock) {
+ storeRecordLocked(
+ pkg, key, userId, mPersistedSnoozedNotificationsWithContext,
creationId);
}
}
@@ -601,7 +661,7 @@
}
protected interface Callback {
- void repost(int userId, NotificationRecord r);
+ void repost(int userId, NotificationRecord r, boolean muteOnReturn);
}
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@@ -612,7 +672,7 @@
}
if (REPOST_ACTION.equals(intent.getAction())) {
repost(intent.getStringExtra(EXTRA_KEY), intent.getIntExtra(EXTRA_USER_ID,
- UserHandle.USER_SYSTEM));
+ UserHandle.USER_SYSTEM), false);
}
}
};
diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java
index f3c9128..7c9be2d 100644
--- a/services/core/java/com/android/server/om/IdmapManager.java
+++ b/services/core/java/com/android/server/om/IdmapManager.java
@@ -21,17 +21,17 @@
import android.annotation.NonNull;
import android.content.om.OverlayInfo;
+import android.content.om.OverlayableInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.os.Build.VERSION_CODES;
-import android.os.IIdmap2;
+import android.os.OverlayablePolicy;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.util.Slog;
-import com.android.server.om.OverlayManagerServiceImpl.PackageManagerHelper;
-
import java.io.File;
+import java.io.IOException;
/**
* Handle the creation and deletion of idmap files.
@@ -55,11 +55,11 @@
VENDOR_IS_Q_OR_LATER = isQOrLater;
}
- private final PackageManagerHelper mPackageManager;
+ private final OverlayableInfoCallback mOverlayableCallback;
private final IdmapDaemon mIdmapDaemon;
- IdmapManager(final PackageManagerHelper packageManager) {
- mPackageManager = packageManager;
+ IdmapManager(final OverlayableInfoCallback verifyCallback) {
+ mOverlayableCallback = verifyCallback;
mIdmapDaemon = IdmapDaemon.getInstance();
}
@@ -148,40 +148,67 @@
private int calculateFulfilledPolicies(@NonNull final PackageInfo targetPackage,
@NonNull final PackageInfo overlayPackage, int userId) {
final ApplicationInfo ai = overlayPackage.applicationInfo;
- int fulfilledPolicies = IIdmap2.POLICY_PUBLIC;
+ int fulfilledPolicies = OverlayablePolicy.PUBLIC;
// Overlay matches target signature
- if (mPackageManager.signaturesMatching(targetPackage.packageName,
+ if (mOverlayableCallback.signaturesMatching(targetPackage.packageName,
overlayPackage.packageName, userId)) {
- fulfilledPolicies |= IIdmap2.POLICY_SIGNATURE;
+ fulfilledPolicies |= OverlayablePolicy.SIGNATURE;
+ }
+
+ // Overlay matches actor signature
+ if (matchesActorSignature(targetPackage, overlayPackage, userId)) {
+ fulfilledPolicies |= OverlayablePolicy.ACTOR_SIGNATURE;
}
// Vendor partition (/vendor)
if (ai.isVendor()) {
- return fulfilledPolicies | IIdmap2.POLICY_VENDOR_PARTITION;
+ return fulfilledPolicies | OverlayablePolicy.VENDOR_PARTITION;
}
// Product partition (/product)
if (ai.isProduct()) {
- return fulfilledPolicies | IIdmap2.POLICY_PRODUCT_PARTITION;
+ return fulfilledPolicies | OverlayablePolicy.PRODUCT_PARTITION;
}
// Odm partition (/odm)
if (ai.isOdm()) {
- return fulfilledPolicies | IIdmap2.POLICY_ODM_PARTITION;
+ return fulfilledPolicies | OverlayablePolicy.ODM_PARTITION;
}
// Oem partition (/oem)
if (ai.isOem()) {
- return fulfilledPolicies | IIdmap2.POLICY_OEM_PARTITION;
+ return fulfilledPolicies | OverlayablePolicy.OEM_PARTITION;
}
// System_ext partition (/system_ext) is considered as system
// Check this last since every partition except for data is scanned as system in the PMS.
if (ai.isSystemApp() || ai.isSystemExt()) {
- return fulfilledPolicies | IIdmap2.POLICY_SYSTEM_PARTITION;
+ return fulfilledPolicies | OverlayablePolicy.SYSTEM_PARTITION;
}
return fulfilledPolicies;
}
+
+ private boolean matchesActorSignature(@NonNull PackageInfo targetPackage,
+ @NonNull PackageInfo overlayPackage, int userId) {
+ String targetOverlayableName = overlayPackage.targetOverlayableName;
+ if (targetOverlayableName != null) {
+ try {
+ OverlayableInfo overlayableInfo = mOverlayableCallback.getOverlayableForTarget(
+ targetPackage.packageName, targetOverlayableName, userId);
+ if (overlayableInfo != null) {
+ String actorPackageName = OverlayActorEnforcer.getPackageNameForActor(
+ overlayableInfo.actor, mOverlayableCallback.getNamedActors()).first;
+ if (mOverlayableCallback.signaturesMatching(actorPackageName,
+ overlayPackage.packageName, userId)) {
+ return true;
+ }
+ }
+ } catch (IOException ignored) {
+ }
+ }
+
+ return false;
+ }
}
diff --git a/services/core/java/com/android/server/om/OverlayActorEnforcer.java b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
index 4c85603..40efb7c 100644
--- a/services/core/java/com/android/server/om/OverlayActorEnforcer.java
+++ b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
@@ -17,7 +17,6 @@
package com.android.server.om;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.om.OverlayInfo;
import android.content.om.OverlayableInfo;
import android.content.pm.ApplicationInfo;
@@ -46,7 +45,7 @@
// By default, the reason is not logged to prevent leaks of why it failed
private static final boolean DEBUG_REASON = false;
- private final VerifyCallback mVerifyCallback;
+ private final OverlayableInfoCallback mOverlayableCallback;
/**
* @return nullable actor result with {@link ActorState} failure status
@@ -80,8 +79,8 @@
return Pair.create(packageName, ActorState.ALLOWED);
}
- public OverlayActorEnforcer(@NonNull VerifyCallback verifyCallback) {
- mVerifyCallback = verifyCallback;
+ public OverlayActorEnforcer(@NonNull OverlayableInfoCallback overlayableCallback) {
+ mOverlayableCallback = overlayableCallback;
}
void enforceActor(@NonNull OverlayInfo overlayInfo, @NonNull String methodName,
@@ -117,7 +116,7 @@
return ActorState.ALLOWED;
}
- String[] callingPackageNames = mVerifyCallback.getPackagesForUid(callingUid);
+ String[] callingPackageNames = mOverlayableCallback.getPackagesForUid(callingUid);
if (ArrayUtils.isEmpty(callingPackageNames)) {
return ActorState.NO_PACKAGES_FOR_UID;
}
@@ -132,12 +131,12 @@
if (TextUtils.isEmpty(targetOverlayableName)) {
try {
- if (mVerifyCallback.doesTargetDefineOverlayable(targetPackageName, userId)) {
+ if (mOverlayableCallback.doesTargetDefineOverlayable(targetPackageName, userId)) {
return ActorState.MISSING_TARGET_OVERLAYABLE_NAME;
} else {
// If there's no overlayable defined, fallback to the legacy permission check
try {
- mVerifyCallback.enforcePermission(
+ mOverlayableCallback.enforcePermission(
android.Manifest.permission.CHANGE_OVERLAY_PACKAGES, methodName);
// If the previous method didn't throw, check passed
@@ -153,7 +152,7 @@
OverlayableInfo targetOverlayable;
try {
- targetOverlayable = mVerifyCallback.getOverlayableForTarget(targetPackageName,
+ targetOverlayable = mOverlayableCallback.getOverlayableForTarget(targetPackageName,
targetOverlayableName, userId);
} catch (IOException e) {
return ActorState.UNABLE_TO_GET_TARGET;
@@ -167,7 +166,7 @@
if (TextUtils.isEmpty(actor)) {
// If there's no actor defined, fallback to the legacy permission check
try {
- mVerifyCallback.enforcePermission(
+ mOverlayableCallback.enforcePermission(
android.Manifest.permission.CHANGE_OVERLAY_PACKAGES, methodName);
// If the previous method didn't throw, check passed
@@ -177,7 +176,7 @@
}
}
- Map<String, Map<String, String>> namedActors = mVerifyCallback.getNamedActors();
+ Map<String, Map<String, String>> namedActors = mOverlayableCallback.getNamedActors();
Pair<String, ActorState> actorUriPair = getPackageNameForActor(actor, namedActors);
ActorState actorUriState = actorUriPair.second;
if (actorUriState != ActorState.ALLOWED) {
@@ -185,7 +184,7 @@
}
String packageName = actorUriPair.first;
- PackageInfo packageInfo = mVerifyCallback.getPackageInfo(packageName, userId);
+ PackageInfo packageInfo = mOverlayableCallback.getPackageInfo(packageName, userId);
if (packageInfo == null) {
return ActorState.MISSING_APP_INFO;
}
@@ -211,7 +210,7 @@
* For easier logging/debugging, a set of all possible failure/success states when running
* enforcement.
*/
- enum ActorState {
+ public enum ActorState {
ALLOWED,
INVALID_ACTOR,
MISSING_NAMESPACE,
@@ -228,53 +227,4 @@
UNABLE_TO_GET_TARGET,
MISSING_LEGACY_PERMISSION
}
-
- /**
- * Delegate to the system for querying information about packages.
- */
- public interface VerifyCallback {
-
- /**
- * Read from the APK and AndroidManifest of a package to return the overlayable defined for
- * a given name.
- *
- * @throws IOException if the target can't be read
- */
- @Nullable
- OverlayableInfo getOverlayableForTarget(@NonNull String packageName,
- @Nullable String targetOverlayableName, int userId)
- throws IOException;
-
- /**
- * @see android.content.pm.PackageManager#getPackagesForUid(int)
- */
- @Nullable
- String[] getPackagesForUid(int uid);
-
- /**
- * @param userId user to filter package visibility by
- * @see android.content.pm.PackageManager#getPackageInfo(String, int)
- */
- @Nullable
- PackageInfo getPackageInfo(@NonNull String packageName, int userId);
-
- /**
- * @return map of system pre-defined, uniquely named actors; keys are namespace,
- * value maps actor name to package name
- */
- @NonNull
- Map<String, Map<String, String>> getNamedActors();
-
- /**
- * @return true if the target package has declared an overlayable
- */
- boolean doesTargetDefineOverlayable(String targetPackageName, int userId)
- throws RemoteException, IOException;
-
- /**
- * @throws SecurityException containing message if the caller doesn't have the given
- * permission
- */
- void enforcePermission(String permission, String message) throws SecurityException;
- }
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index f221285..c81f7cd 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -226,7 +226,7 @@
private final AtomicFile mSettingsFile;
- private final PackageManagerHelper mPackageManager;
+ private final PackageManagerHelperImpl mPackageManager;
private final UserManagerService mUserManager;
@@ -244,7 +244,7 @@
traceBegin(TRACE_TAG_RRO, "OMS#OverlayManagerService");
mSettingsFile = new AtomicFile(
new File(Environment.getDataSystemDirectory(), "overlays.xml"), "overlays");
- mPackageManager = new PackageManagerHelper(context);
+ mPackageManager = new PackageManagerHelperImpl(context);
mUserManager = UserManagerService.getInstance();
IdmapManager im = new IdmapManager(mPackageManager);
mSettings = new OverlayManagerSettings();
@@ -1053,14 +1053,8 @@
}
}
- /**
- * Delegate for {@link android.content.pm.PackageManager} and {@link PackageManagerInternal}
- * functionality, separated for easy testing.
- *
- * @hide
- */
- public static final class PackageManagerHelper implements
- OverlayManagerServiceImpl.PackageManagerHelper, OverlayActorEnforcer.VerifyCallback {
+ private static final class PackageManagerHelperImpl implements PackageManagerHelper,
+ OverlayableInfoCallback {
private final Context mContext;
private final IPackageManager mPackageManager;
@@ -1073,7 +1067,7 @@
// behind until all pending intents have been processed.
private final SparseArray<HashMap<String, PackageInfo>> mCache = new SparseArray<>();
- PackageManagerHelper(Context context) {
+ PackageManagerHelperImpl(Context context) {
mContext = context;
mPackageManager = getPackageManager();
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
@@ -1132,7 +1126,7 @@
@Nullable
@Override
public OverlayableInfo getOverlayableForTarget(@NonNull String packageName,
- @Nullable String targetOverlayableName, int userId)
+ @NonNull String targetOverlayableName, int userId)
throws IOException {
PackageInfo packageInfo = getPackageInfo(packageName, userId);
if (packageInfo == null) {
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 2493057..5734271 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -18,7 +18,6 @@
import static android.content.om.OverlayInfo.STATE_DISABLED;
import static android.content.om.OverlayInfo.STATE_ENABLED;
-import static android.content.om.OverlayInfo.STATE_ENABLED_IMMUTABLE;
import static android.content.om.OverlayInfo.STATE_MISSING_TARGET;
import static android.content.om.OverlayInfo.STATE_NO_IDMAP;
import static android.content.om.OverlayInfo.STATE_OVERLAY_IS_BEING_REPLACED;
@@ -806,11 +805,4 @@
**/
void onOverlaysChanged(@NonNull String targetPackage, int userId);
}
-
- interface PackageManagerHelper {
- PackageInfo getPackageInfo(@NonNull String packageName, int userId);
- boolean signaturesMatching(@NonNull String packageName1, @NonNull String packageName2,
- int userId);
- List<PackageInfo> getOverlayPackages(int userId);
- }
}
diff --git a/services/core/java/com/android/server/om/OverlayableInfoCallback.java b/services/core/java/com/android/server/om/OverlayableInfoCallback.java
new file mode 100644
index 0000000..6b81884
--- /dev/null
+++ b/services/core/java/com/android/server/om/OverlayableInfoCallback.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.om;
+
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.om.OverlayableInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+
+import com.android.server.pm.PackageManagerServiceUtils;
+
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * Delegate to the system for querying information about overlayables and packages.
+ */
+public interface OverlayableInfoCallback {
+
+ /**
+ * Read from the APK and AndroidManifest of a package to return the overlayable defined for
+ * a given name.
+ *
+ * @throws IOException if the target can't be read
+ */
+ @Nullable
+ OverlayableInfo getOverlayableForTarget(@NonNull String packageName,
+ @NonNull String targetOverlayableName, int userId)
+ throws IOException;
+
+ /**
+ * @see PackageManager#getPackagesForUid(int)
+ */
+ @Nullable
+ String[] getPackagesForUid(int uid);
+
+ /**
+ * @param userId user to filter package visibility by
+ * @see PackageManager#getPackageInfo(String, int)
+ */
+ @Nullable
+ PackageInfo getPackageInfo(@NonNull String packageName, int userId);
+
+ /**
+ * @return map of system pre-defined, uniquely named actors; keys are namespace,
+ * value maps actor name to package name
+ */
+ @NonNull
+ Map<String, Map<String, String>> getNamedActors();
+
+ /**
+ * @return true if the target package has declared an overlayable
+ */
+ boolean doesTargetDefineOverlayable(String targetPackageName, int userId)
+ throws RemoteException, IOException;
+
+ /**
+ * @throws SecurityException containing message if the caller doesn't have the given
+ * permission
+ */
+ void enforcePermission(String permission, String message) throws SecurityException;
+
+ /**
+ * @return true if {@link PackageManagerServiceUtils#compareSignatures} run on both packages
+ * in the system returns {@link PackageManager#SIGNATURE_MATCH}
+ */
+ boolean signaturesMatching(@NonNull String pkgName1, @NonNull String pkgName2, int userId);
+}
diff --git a/services/core/java/com/android/server/om/PackageManagerHelper.java b/services/core/java/com/android/server/om/PackageManagerHelper.java
new file mode 100644
index 0000000..ec9c5e6
--- /dev/null
+++ b/services/core/java/com/android/server/om/PackageManagerHelper.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.om;
+
+import android.annotation.NonNull;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+
+import java.util.List;
+
+/**
+ * Delegate for {@link PackageManager} and {@link PackageManagerInternal} functionality,
+ * separated for easy testing.
+ *
+ * @hide
+ */
+interface PackageManagerHelper {
+ PackageInfo getPackageInfo(@NonNull String packageName, int userId);
+ boolean signaturesMatching(@NonNull String pkgName1, @NonNull String pkgName2, int userId);
+ List<PackageInfo> getOverlayPackages(int userId);
+}
diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
index 1e2d52d..6c2d77c 100644
--- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
+++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
@@ -276,6 +276,11 @@
}
@Override
+ public void onScreenshotTaken(boolean success) throws RemoteException {
+ mListener.onScreenshotTaken(success);
+ }
+
+ @Override
public void binderDied() {
synchronized (mLock) {
if (!mDone) {
diff --git a/services/core/java/com/android/server/people/PeopleServiceInternal.java b/services/core/java/com/android/server/people/PeopleServiceInternal.java
index 31fa4d1..8b4740b 100644
--- a/services/core/java/com/android/server/people/PeopleServiceInternal.java
+++ b/services/core/java/com/android/server/people/PeopleServiceInternal.java
@@ -17,6 +17,7 @@
package com.android.server.people;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.os.CancellationSignal;
import android.service.appprediction.IPredictionService;
@@ -34,16 +35,17 @@
@NonNull CancellationSignal signal);
/**
- * The number conversation infos will be dynamic, based on the currently installed apps on the
- * device. All of which should be combined into a single blob to be backed up.
+ * Returns a backup payload that contains conversation infos. The number conversation infos will
+ * be dynamic, based on the currently installed apps on the device. All of which should be
+ * combined into a single blob to be backed up.
*/
- public abstract byte[] backupConversationInfos(@UserIdInt int userId);
+ @Nullable
+ public abstract byte[] getBackupPayload(@UserIdInt int userId);
/**
- * Multiple conversation infos may exist in the restore payload, child classes are required to
- * manage the restoration based on how individual conversation infos were originally combined
- * during backup.
+ * Restores conversation infos stored in payload blob. Multiple conversation infos may exist in
+ * the restore payload, child classes are required to manage the restoration based on how
+ * individual conversation infos were originally combined during backup.
*/
- public abstract void restoreConversationInfos(@UserIdInt int userId, @NonNull String key,
- @NonNull byte[] payload);
+ public abstract void restore(@UserIdInt int userId, @NonNull byte[] payload);
}
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index e86a42c..f497f11 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -57,6 +57,7 @@
import com.android.internal.util.ArrayUtils;
import com.android.server.IntentResolver;
import com.android.server.pm.parsing.PackageInfoUtils;
+import com.android.server.pm.parsing.PackageInfoUtils.CachedApplicationInfoGenerator;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.io.PrintWriter;
@@ -273,9 +274,7 @@
return null;
}
List<ProviderInfo> providerList = null;
-
- // Map from a package name to the corresponding app info.
- ArrayMap<String, ApplicationInfo> appInfos = null;
+ CachedApplicationInfoGenerator appInfoGenerator = null;
synchronized (mLock) {
for (int i = mProviders.mProviders.size() - 1; i >= 0; --i) {
final ParsedProvider p = mProviders.mProviders.valueAt(i);
@@ -304,26 +303,15 @@
&& (p.getMetaData() == null || !p.getMetaData().containsKey(metaDataKey))) {
continue;
}
-
- // Make sure we have AppInfo for this provider.
- final PackageUserState state = ps.readUserState(userId);
- ApplicationInfo appInfo =
- (appInfos == null) ? null : appInfos.get(pkg.getPackageName());
- if (appInfo == null) {
- appInfo = PackageInfoUtils.generateApplicationInfo(
- pkg, flags, state, userId, ps);
- if (appInfo == null) {
- // In this case, we should avoid calling generateApplicationInfo() for
- // the same package in subsequent iterations, but appInfo shouldn't be null
- // here, so we don't bother.
- continue;
- }
- if (appInfos == null) {
- appInfos = new ArrayMap<>(4);
- }
- appInfos.put(pkg.getPackageName(), appInfo);
+ if (appInfoGenerator == null) {
+ appInfoGenerator = new CachedApplicationInfoGenerator();
}
- // At this point, appInfo != null.
+ final PackageUserState state = ps.readUserState(userId);
+ final ApplicationInfo appInfo =
+ appInfoGenerator.generate(pkg, flags, state, userId, ps);
+ if (appInfo == null) {
+ continue;
+ }
final ProviderInfo info = PackageInfoUtils.generateProviderInfo(
pkg, p, flags, state, appInfo, userId, ps);
@@ -355,14 +343,20 @@
if (pkg == null) {
return null;
}
- return PackageInfoUtils.generateProviderInfo(pkg, p, flags,
- ps.readUserState(userId), userId, ps);
+ final PackageUserState state = ps.readUserState(userId);
+ ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(
+ pkg, flags, state, userId, ps);
+ if (appInfo == null) {
+ return null;
+ }
+ return PackageInfoUtils.generateProviderInfo(pkg, p, flags, state, appInfo, userId, ps);
}
}
void querySyncProviders(List<String> outNames, List<ProviderInfo> outInfo, boolean safeMode,
int userId) {
synchronized (mLock) {
+ CachedApplicationInfoGenerator appInfoGenerator = null;
for (int i = mProvidersByAuthority.size() - 1; i >= 0; --i) {
final ParsedProvider p = mProvidersByAuthority.valueAt(i);
if (!p.isSyncable()) {
@@ -384,9 +378,18 @@
if (safeMode && !pkg.isSystem()) {
continue;
}
- final ProviderInfo info =
- PackageInfoUtils.generateProviderInfo(pkg, p, 0,
- ps.readUserState(userId), userId, ps);
+ if (appInfoGenerator == null) {
+ appInfoGenerator = new CachedApplicationInfoGenerator();
+ }
+ final PackageUserState state = ps.readUserState(userId);
+ final ApplicationInfo appInfo =
+ appInfoGenerator.generate(pkg, 0, state, userId, ps);
+ if (appInfo == null) {
+ continue;
+ }
+
+ final ProviderInfo info = PackageInfoUtils.generateProviderInfo(
+ pkg, p, 0, state, appInfo, userId, ps);
if (info == null) {
continue;
}
@@ -1731,8 +1734,13 @@
if (userState.instantApp && ps.isUpdateAvailable()) {
return null;
}
+ final ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(
+ pkg, mFlags, userState, userId, ps);
+ if (appInfo == null) {
+ return null;
+ }
ProviderInfo pi = PackageInfoUtils.generateProviderInfo(pkg, provider, mFlags,
- userState, userId, ps);
+ userState, appInfo, userId, ps);
if (pi == null) {
return null;
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 41988d6..5cc5059 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1689,8 +1689,8 @@
}
mRelinquished = true;
- return new PackageManagerService.ActiveInstallSession(mPackageName, stageDir,
- localObserver, params, mInstallerUid, mInstallSource, user, mSigningDetails);
+ return new PackageManagerService.ActiveInstallSession(mPackageName, stageDir, localObserver,
+ sessionId, params, mInstallerUid, mInstallSource, user, mSigningDetails);
}
private static void maybeRenameFile(File from, File to) throws PackageManagerException {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 4eac79c..c34e45e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -213,7 +213,6 @@
import android.content.pm.dex.ArtManager;
import android.content.pm.dex.DexMetadataHelper;
import android.content.pm.dex.IArtManager;
-import android.content.pm.parsing.PackageInfoWithoutStateUtils;
import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedInstrumentation;
@@ -1967,6 +1966,7 @@
}
case ENABLE_ROLLBACK_TIMEOUT: {
final int enableRollbackToken = msg.arg1;
+ final int sessionId = msg.arg2;
final InstallParams params = mPendingEnableRollback.get(enableRollbackToken);
if (params != null) {
final InstallArgs args = params.mArgs;
@@ -1982,8 +1982,8 @@
Intent rollbackTimeoutIntent = new Intent(
Intent.ACTION_CANCEL_ENABLE_ROLLBACK);
rollbackTimeoutIntent.putExtra(
- PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN,
- enableRollbackToken);
+ PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_SESSION_ID,
+ sessionId);
rollbackTimeoutIntent.addFlags(
Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
mContext.sendBroadcastAsUser(rollbackTimeoutIntent, UserHandle.SYSTEM,
@@ -5459,7 +5459,13 @@
return null;
}
PackageUserState state = ps.readUserState(userId);
- return PackageInfoUtils.generateProviderInfo(pkg, p, flags, state, userId, ps);
+ final ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(
+ pkg, flags, state, userId, ps);
+ if (appInfo == null) {
+ return null;
+ }
+ return PackageInfoUtils.generateProviderInfo(
+ pkg, p, flags, state, appInfo, userId, ps);
}
}
return null;
@@ -6826,12 +6832,16 @@
final boolean isTargetHiddenFromInstantApp =
!isTargetVisibleToInstantApp
|| (matchExplicitlyVisibleOnly && !isTargetExplicitlyVisibleToInstantApp);
- final boolean blockResolution =
+ final boolean blockInstantResolution =
!isTargetSameInstantApp
&& ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp)
|| (matchVisibleToInstantAppOnly && isCallerInstantApp
&& isTargetHiddenFromInstantApp));
- if (!blockResolution) {
+ final boolean blockNormalResolution = !isTargetInstantApp && !isCallerInstantApp
+ && !resolveForStart && shouldFilterApplicationLocked(
+ getPackageSettingInternal(ai.applicationInfo.packageName,
+ Process.SYSTEM_UID), filterCallingUid, userId);
+ if (!blockInstantResolution && !blockNormalResolution) {
final ResolveInfo ri = new ResolveInfo();
ri.activityInfo = ai;
list.add(ri);
@@ -7215,9 +7225,17 @@
resolveInfos.set(i, installerInfo);
continue;
}
- // caller is a full app, don't need to apply any other filtering
+ // caller is a full app
if (ephemeralPkgName == null) {
- continue;
+ SettingBase callingSetting =
+ mSettings.getSettingLPr(UserHandle.getAppId(filterCallingUid));
+ PackageSetting resolvedSetting =
+ getPackageSettingInternal(info.activityInfo.packageName, 0);
+ if (resolveForStart
+ || !mAppsFilter.shouldFilterApplication(
+ filterCallingUid, callingSetting, resolvedSetting, userId)) {
+ continue;
+ }
} else if (ephemeralPkgName.equals(info.activityInfo.packageName)) {
// caller is same app; don't need to apply any other filtering
continue;
@@ -7907,12 +7925,17 @@
& ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
final boolean isTargetHiddenFromInstantApp =
(si.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0;
- final boolean blockResolution =
+ final boolean blockInstantResolution =
!isTargetSameInstantApp
&& ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp)
|| (matchVisibleToInstantAppOnly && isCallerInstantApp
&& isTargetHiddenFromInstantApp));
- if (!blockResolution) {
+
+ final boolean blockNormalResolution = !isTargetInstantApp && !isCallerInstantApp
+ && shouldFilterApplicationLocked(
+ getPackageSettingInternal(si.applicationInfo.packageName,
+ Process.SYSTEM_UID), callingUid, userId);
+ if (!blockInstantResolution && !blockNormalResolution) {
final ResolveInfo ri = new ResolveInfo();
ri.serviceInfo = si;
list.add(ri);
@@ -7931,8 +7954,7 @@
return Collections.emptyList();
}
return applyPostServiceResolutionFilter(
- resolveInfos,
- instantAppPkgName);
+ resolveInfos, instantAppPkgName, userId, callingUid);
}
final AndroidPackage pkg = mPackages.get(pkgName);
if (pkg != null) {
@@ -7943,20 +7965,26 @@
return Collections.emptyList();
}
return applyPostServiceResolutionFilter(
- resolveInfos,
- instantAppPkgName);
+ resolveInfos, instantAppPkgName, userId, callingUid);
}
return Collections.emptyList();
}
}
private List<ResolveInfo> applyPostServiceResolutionFilter(List<ResolveInfo> resolveInfos,
- String instantAppPkgName) {
- if (instantAppPkgName == null) {
- return resolveInfos;
- }
+ String instantAppPkgName, @UserIdInt int userId, int filterCallingUid) {
for (int i = resolveInfos.size() - 1; i >= 0; i--) {
final ResolveInfo info = resolveInfos.get(i);
+ if (instantAppPkgName == null) {
+ SettingBase callingSetting =
+ mSettings.getSettingLPr(UserHandle.getAppId(filterCallingUid));
+ PackageSetting resolvedSetting =
+ getPackageSettingInternal(info.serviceInfo.packageName, 0);
+ if (!mAppsFilter.shouldFilterApplication(
+ filterCallingUid, callingSetting, resolvedSetting, userId)) {
+ continue;
+ }
+ }
final boolean isEphemeralApp = info.serviceInfo.applicationInfo.isInstantApp();
// allow services that are defined in the provided package
if (isEphemeralApp && instantAppPkgName.equals(info.serviceInfo.packageName)) {
@@ -8039,7 +8067,11 @@
&& ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp)
|| (matchVisibleToInstantAppOnly && isCallerInstantApp
&& isTargetHiddenFromInstantApp));
- if (!blockResolution) {
+ final boolean blockNormalResolution = !isTargetInstantApp && !isCallerInstantApp
+ && shouldFilterApplicationLocked(
+ getPackageSettingInternal(pi.applicationInfo.packageName,
+ Process.SYSTEM_UID), callingUid, userId);
+ if (!blockResolution && !blockNormalResolution) {
final ResolveInfo ri = new ResolveInfo();
ri.providerInfo = pi;
list.add(ri);
@@ -8058,8 +8090,7 @@
return Collections.emptyList();
}
return applyPostContentProviderResolutionFilter(
- resolveInfos,
- instantAppPkgName);
+ resolveInfos, instantAppPkgName, userId, callingUid);
}
final AndroidPackage pkg = mPackages.get(pkgName);
if (pkg != null) {
@@ -8070,20 +8101,29 @@
return Collections.emptyList();
}
return applyPostContentProviderResolutionFilter(
- resolveInfos,
- instantAppPkgName);
+ resolveInfos, instantAppPkgName, userId, callingUid);
}
return Collections.emptyList();
}
}
private List<ResolveInfo> applyPostContentProviderResolutionFilter(
- List<ResolveInfo> resolveInfos, String instantAppPkgName) {
- if (instantAppPkgName == null) {
- return resolveInfos;
- }
+ List<ResolveInfo> resolveInfos, String instantAppPkgName,
+ @UserIdInt int userId, int callingUid) {
for (int i = resolveInfos.size() - 1; i >= 0; i--) {
final ResolveInfo info = resolveInfos.get(i);
+
+ if (instantAppPkgName == null) {
+ SettingBase callingSetting =
+ mSettings.getSettingLPr(UserHandle.getAppId(callingUid));
+ PackageSetting resolvedSetting =
+ getPackageSettingInternal(info.providerInfo.packageName, 0);
+ if (!mAppsFilter.shouldFilterApplication(
+ callingUid, callingSetting, resolvedSetting, userId)) {
+ continue;
+ }
+ }
+
final boolean isEphemeralApp = info.providerInfo.applicationInfo.isInstantApp();
// allow providers that are defined in the provided package
if (isEphemeralApp && instantAppPkgName.equals(info.providerInfo.packageName)) {
@@ -10945,17 +10985,22 @@
if (createNewPackage) {
final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
final boolean virtualPreload = (scanFlags & SCAN_AS_VIRTUAL_PRELOAD) != 0;
+
+ // Flags contain system values stored in the server variant of AndroidPackage,
+ // and so the server-side PackageInfoUtils is still called, even without a
+ // PackageSetting to pass in.
+ int pkgFlags = PackageInfoUtils.appInfoFlags(parsedPackage, null);
+ int pkgPrivateFlags = PackageInfoUtils.appInfoPrivateFlags(parsedPackage, null);
+
// REMOVE SharedUserSetting from method; update in a separate call
pkgSetting = Settings.createNewSetting(parsedPackage.getPackageName(),
originalPkgSetting, disabledPkgSetting, realPkgName, sharedUserSetting,
destCodeFile, destResourceFile, parsedPackage.getNativeLibraryRootDir(),
AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage),
AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage),
- parsedPackage.getVersionCode(),
- PackageInfoWithoutStateUtils.appInfoFlags(parsedPackage),
- PackageInfoWithoutStateUtils.appInfoPrivateFlags(parsedPackage),
- user, true /*allowInstall*/, instantApp,
- virtualPreload, UserManagerService.getInstance(), usesStaticLibraries,
+ parsedPackage.getVersionCode(), pkgFlags, pkgPrivateFlags, user,
+ true /*allowInstall*/, instantApp, virtualPreload,
+ UserManagerService.getInstance(), usesStaticLibraries,
parsedPackage.getUsesStaticLibrariesVersions(), parsedPackage.getMimeGroups());
} else {
// make a deep copy to avoid modifying any existing system state.
@@ -11774,14 +11819,22 @@
final String pkgName = pkg.getPackageName();
if (mCustomResolverComponentName != null &&
mCustomResolverComponentName.getPackageName().equals(pkg.getPackageName())) {
- setUpCustomResolverActivity(pkg);
+ setUpCustomResolverActivity(pkg, pkgSetting);
}
if (pkg.getPackageName().equals("android")) {
synchronized (mLock) {
// Set up information for our fall-back user intent resolution activity.
mPlatformPackage = pkg;
+
+ // The instance stored in PackageManagerService is special cased to be non-user
+ // specific, so initialize all the needed fields here.
mAndroidApplication = pkg.toAppInfoWithoutState();
+ mAndroidApplication.flags = PackageInfoUtils.appInfoFlags(pkg, pkgSetting);
+ mAndroidApplication.privateFlags =
+ PackageInfoUtils.appInfoPrivateFlags(pkg, pkgSetting);
+ mAndroidApplication.initForUser(UserHandle.USER_SYSTEM);
+
if (!mResolverReplaced) {
mResolveActivity.applicationInfo = mAndroidApplication;
mResolveActivity.name = ResolverActivity.class.getName();
@@ -11943,11 +11996,20 @@
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
- private void setUpCustomResolverActivity(AndroidPackage pkg) {
+ private void setUpCustomResolverActivity(AndroidPackage pkg, PackageSetting pkgSetting) {
synchronized (mLock) {
mResolverReplaced = true;
+
+ // The instance created in PackageManagerService is special cased to be non-user
+ // specific, so initialize all the needed fields here.
+ ApplicationInfo appInfo = pkg.toAppInfoWithoutState();
+ appInfo.flags = PackageInfoUtils.appInfoFlags(pkg, pkgSetting);
+ appInfo.privateFlags =
+ PackageInfoUtils.appInfoPrivateFlags(pkg, pkgSetting);
+ appInfo.initForUser(UserHandle.USER_SYSTEM);
+
// Set up information for custom user intent resolution activity.
- mResolveActivity.applicationInfo = pkg.toAppInfoWithoutState();
+ mResolveActivity.applicationInfo = appInfo;
mResolveActivity.name = mCustomResolverComponentName.getClassName();
mResolveActivity.packageName = pkg.getPackageName();
mResolveActivity.processName = pkg.getProcessName();
@@ -14237,6 +14299,7 @@
final long requiredInstalledVersionCode;
final boolean forceQueryableOverride;
final int mDataLoaderType;
+ final int mSessionId;
InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
int installFlags, InstallSource installSource, String volumeUuid,
@@ -14260,6 +14323,7 @@
this.requiredInstalledVersionCode = requiredInstalledVersionCode;
this.forceQueryableOverride = false;
this.mDataLoaderType = dataLoaderType;
+ this.mSessionId = -1;
}
InstallParams(ActiveInstallSession activeInstallSession) {
@@ -14295,6 +14359,7 @@
forceQueryableOverride = sessionParams.forceQueryableOverride;
mDataLoaderType = (sessionParams.dataLoaderParams != null)
? sessionParams.dataLoaderParams.getType() : DataLoaderType.NONE;
+ mSessionId = activeInstallSession.getSessionId();
}
@Override
@@ -14526,13 +14591,9 @@
PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN,
enableRollbackToken);
enableRollbackIntent.putExtra(
- PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_INSTALL_FLAGS,
- installFlags);
- enableRollbackIntent.putExtra(
- PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_USER,
- getRollbackUser().getIdentifier());
- enableRollbackIntent.setDataAndType(Uri.fromFile(new File(origin.resolvedPath)),
- PACKAGE_MIME_TYPE);
+ PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_SESSION_ID,
+ mSessionId);
+ enableRollbackIntent.setType(PACKAGE_MIME_TYPE);
enableRollbackIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// Allow the broadcast to be sent before boot complete.
@@ -14558,6 +14619,7 @@
final Message msg = mHandler.obtainMessage(
ENABLE_ROLLBACK_TIMEOUT);
msg.arg1 = enableRollbackToken;
+ msg.arg2 = mSessionId;
mHandler.sendMessageDelayed(msg, rollbackTimeout);
}
}, null, 0, null, null);
@@ -24567,6 +24629,7 @@
private final String mPackageName;
private final File mStagedDir;
private final IPackageInstallObserver2 mObserver;
+ private final int mSessionId;
private final PackageInstaller.SessionParams mSessionParams;
private final int mInstallerUid;
@NonNull private final InstallSource mInstallSource;
@@ -24574,11 +24637,12 @@
private final SigningDetails mSigningDetails;
ActiveInstallSession(String packageName, File stagedDir, IPackageInstallObserver2 observer,
- PackageInstaller.SessionParams sessionParams, int installerUid,
+ int sessionId, PackageInstaller.SessionParams sessionParams, int installerUid,
InstallSource installSource, UserHandle user, SigningDetails signingDetails) {
mPackageName = packageName;
mStagedDir = stagedDir;
mObserver = observer;
+ mSessionId = sessionId;
mSessionParams = sessionParams;
mInstallerUid = installerUid;
mInstallSource = Preconditions.checkNotNull(installSource);
@@ -24598,6 +24662,10 @@
return mObserver;
}
+ public int getSessionId() {
+ return mSessionId;
+ }
+
public PackageInstaller.SessionParams getSessionParams() {
return mSessionParams;
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 2453318..fa1da27 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -33,6 +33,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.compat.ChangeIdStateCache;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
@@ -441,6 +442,7 @@
private static void invalidatePackageCache() {
PackageManager.invalidatePackageInfoCache();
+ ChangeIdStateCache.invalidate();
}
PackageSetting getPackageLPr(String pkgName) {
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index f5ce080..d3f668c 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -48,7 +48,7 @@
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.Pair;
+import android.util.Slog;
import com.android.internal.util.ArrayUtils;
import com.android.server.pm.PackageSetting;
@@ -72,6 +72,7 @@
* @hide
**/
public class PackageInfoUtils {
+ private static final String TAG = PackageParser2.TAG;
/**
* @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
@@ -108,12 +109,9 @@
return null;
}
- PackageInfo info = PackageInfoWithoutStateUtils.generateWithoutComponents(pkg, gids, flags,
- firstInstallTime, lastUpdateTime, grantedPermissions, state, userId, apexInfo,
- applicationInfo);
- if (info == null) {
- return null;
- }
+ PackageInfo info = PackageInfoWithoutStateUtils.generateWithoutComponentsUnchecked(pkg,
+ gids, flags, firstInstallTime, lastUpdateTime, grantedPermissions, state, userId,
+ apexInfo, applicationInfo);
info.isStub = pkg.isStub();
info.coreApp = pkg.isCoreApp();
@@ -218,11 +216,8 @@
return null;
}
- ApplicationInfo info = PackageInfoWithoutStateUtils.generateApplicationInfo(pkg, flags,
- state, userId);
- if (info == null) {
- return null;
- }
+ ApplicationInfo info = PackageInfoWithoutStateUtils.generateApplicationInfoUnchecked(pkg,
+ flags, state, userId);
if (pkgSetting != null) {
// TODO(b/135203078): Remove PackageParser1/toAppInfoWithoutState and clean all this up
@@ -265,12 +260,13 @@
if (applicationInfo == null) {
applicationInfo = generateApplicationInfo(pkg, flags, state, userId, pkgSetting);
}
- ActivityInfo info = PackageInfoWithoutStateUtils.generateActivityInfo(pkg, a, flags, state,
- applicationInfo, userId);
- if (info == null) {
+
+ if (applicationInfo == null) {
return null;
}
+ ActivityInfo info =
+ PackageInfoWithoutStateUtils.generateActivityInfoUnchecked(a, applicationInfo);
assignSharedFieldsForComponentInfo(info, a, pkgSetting);
return info;
}
@@ -300,53 +296,39 @@
if (applicationInfo == null) {
applicationInfo = generateApplicationInfo(pkg, flags, state, userId, pkgSetting);
}
- ServiceInfo info = PackageInfoWithoutStateUtils.generateServiceInfo(pkg, s, flags, state,
- applicationInfo, userId);
- if (info == null) {
+ if (applicationInfo == null) {
return null;
}
+ ServiceInfo info =
+ PackageInfoWithoutStateUtils.generateServiceInfoUnchecked(s, applicationInfo);
assignSharedFieldsForComponentInfo(info, s, pkgSetting);
return info;
}
/**
* @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
- *
- * @deprecated use {@link #generateProviderInfo(
- * AndroidPackage, ParsedProvider, int, PackageUserState, ApplicationInfo, int, PackageSetting)}
- * instead and pass {@link ApplicationInfo} explicitly to avoid generating duplicate instances
- * of it.
- */
- @Nullable
- @Deprecated
- public static ProviderInfo generateProviderInfo(AndroidPackage pkg, ParsedProvider p,
- @PackageManager.ComponentInfoFlags int flags, PackageUserState state, int userId,
- @Nullable PackageSetting pkgSetting) {
- return generateProviderInfo(pkg, p, flags, state, null, userId, pkgSetting);
- }
-
- /**
- * @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
*/
@Nullable
public static ProviderInfo generateProviderInfo(AndroidPackage pkg, ParsedProvider p,
@PackageManager.ComponentInfoFlags int flags, PackageUserState state,
- @Nullable ApplicationInfo applicationInfo, int userId,
+ @NonNull ApplicationInfo applicationInfo, int userId,
@Nullable PackageSetting pkgSetting) {
if (p == null) return null;
if (!checkUseInstalledOrHidden(pkg, pkgSetting, state, flags)) {
return null;
}
- if (applicationInfo == null) {
+ if (applicationInfo == null || !pkg.getPackageName().equals(applicationInfo.packageName)) {
+ Slog.wtf(TAG, "AppInfo's package name is different. Expected=" + pkg.getPackageName()
+ + " actual=" + (applicationInfo == null ? "(null AppInfo)"
+ : applicationInfo.packageName));
applicationInfo = generateApplicationInfo(pkg, flags, state, userId, pkgSetting);
}
- ProviderInfo info = PackageInfoWithoutStateUtils.generateProviderInfo(pkg, p, flags, state,
- applicationInfo, userId);
- if (info == null) {
+ if (applicationInfo == null) {
return null;
}
-
+ ProviderInfo info = PackageInfoWithoutStateUtils.generateProviderInfoUnchecked(p, flags,
+ applicationInfo);
assignSharedFieldsForComponentInfo(info, p, pkgSetting);
return info;
}
@@ -486,4 +468,29 @@
| flag(pkg.isSignedWithPlatformKey(), ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY);
// @formatter:on
}
+
+ /**
+ * Wraps {@link PackageInfoUtils#generateApplicationInfo} with a cache.
+ */
+ public static class CachedApplicationInfoGenerator {
+ // Map from a package name to the corresponding app info.
+ private ArrayMap<String, ApplicationInfo> mCache = new ArrayMap<>();
+
+ /**
+ * {@link PackageInfoUtils#generateApplicationInfo} with a cache.
+ */
+ @Nullable
+ public ApplicationInfo generate(AndroidPackage pkg,
+ @PackageManager.ApplicationInfoFlags int flags, PackageUserState state, int userId,
+ @Nullable PackageSetting pkgSetting) {
+ ApplicationInfo appInfo = mCache.get(pkg.getPackageName());
+ if (appInfo != null) {
+ return appInfo;
+ }
+ appInfo = PackageInfoUtils.generateApplicationInfo(
+ pkg, flags, state, userId, pkgSetting);
+ mCache.put(pkg.getPackageName(), appInfo);
+ return appInfo;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/parsing/PackageParser2.java b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
index f99791a..d561b9c 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageParser2.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
@@ -42,7 +42,7 @@
*/
public class PackageParser2 {
- private static final String TAG = "PackageParser2";
+ static final String TAG = "PackageParser2";
private static final boolean LOG_PARSE_TIMINGS = Build.IS_DEBUGGABLE;
private static final int LOG_PARSE_TIMINGS_THRESHOLD_MS = 100;
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 48dd9e6..2feddb6 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -733,7 +733,7 @@
if (!TextUtils.isEmpty(contentCapturePackageName)) {
grantPermissionsToSystemPackage(contentCapturePackageName, userId,
PHONE_PERMISSIONS, SMS_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS,
- CONTACTS_PERMISSIONS);
+ CONTACTS_PERMISSIONS, STORAGE_PERMISSIONS);
}
// Atthention Service
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index f647b6a..4a85027 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -4471,6 +4471,9 @@
@Override
public void setCheckPermissionDelegate(CheckPermissionDelegate delegate) {
synchronized (mLock) {
+ if (delegate != null || mCheckPermissionDelegate != null) {
+ PackageManager.invalidatePackageInfoCache();
+ }
mCheckPermissionDelegate = delegate;
}
}
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 0b95be1..199cb49 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -413,7 +413,6 @@
// Start input as soon as we start waking up or going to sleep.
mInputManagerInternal.setInteractive(interactive);
- mInputMethodManagerInternal.setInteractive(interactive);
// Notify battery stats.
try {
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 8483c77..e6c23b6 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -4213,15 +4213,15 @@
.SCREEN_BRIGHTNESS_SETTING_LIMITS);
proto.write(
PowerServiceSettingsAndConfigurationDumpProto.ScreenBrightnessSettingLimitsProto
- .SETTING_MINIMUM,
+ .SETTING_MINIMUM_FLOAT,
mScreenBrightnessSettingMinimum);
proto.write(
PowerServiceSettingsAndConfigurationDumpProto.ScreenBrightnessSettingLimitsProto
- .SETTING_MAXIMUM,
+ .SETTING_MAXIMUM_FLOAT,
mScreenBrightnessSettingMaximum);
proto.write(
PowerServiceSettingsAndConfigurationDumpProto.ScreenBrightnessSettingLimitsProto
- .SETTING_DEFAULT,
+ .SETTING_DEFAULT_FLOAT,
mScreenBrightnessSettingDefault);
proto.end(screenBrightnessSettingLimitsToken);
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index 7b96777..885f561 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -159,13 +159,6 @@
@Nullable public final String mInstallerPackageName;
/**
- * This array holds all of the rollback tokens associated with package sessions included in
- * this rollback.
- */
- @GuardedBy("mLock")
- private final IntArray mTokens = new IntArray();
-
- /**
* Session ids for all packages in the install. For multi-package sessions, this is the list
* of child session ids. For normal sessions, this list is a single element with the normal
* session id.
@@ -769,26 +762,6 @@
}
/**
- * Adds a rollback token to be associated with this rollback. This may be used to
- * identify which rollback should be removed in case {@link PackageManager} sends an
- * {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK} intent.
- */
- void addToken(int token) {
- synchronized (mLock) {
- mTokens.add(token);
- }
- }
-
- /**
- * Returns true if this rollback is associated with the provided {@code token}.
- */
- boolean hasToken(int token) {
- synchronized (mLock) {
- return mTokens.indexOf(token) != -1;
- }
- }
-
- /**
* Returns true if this rollback contains the provided {@code packageSessionId}.
*/
boolean containsSessionId(int packageSessionId) {
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index f9981d0..42fada1 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -20,7 +20,6 @@
import android.annotation.AnyThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
@@ -201,18 +200,13 @@
if (Intent.ACTION_PACKAGE_ENABLE_ROLLBACK.equals(intent.getAction())) {
int token = intent.getIntExtra(
PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN, -1);
- int installFlags = intent.getIntExtra(
- PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_INSTALL_FLAGS, 0);
- int user = intent.getIntExtra(
- PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_USER, 0);
-
- File newPackageCodePath = new File(intent.getData().getPath());
+ int sessionId = intent.getIntExtra(
+ PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_SESSION_ID, -1);
queueSleepIfNeeded();
getHandler().post(() -> {
- boolean success =
- enableRollback(installFlags, newPackageCodePath, user, token);
+ boolean success = enableRollback(sessionId);
int ret = PackageManagerInternal.ENABLE_ROLLBACK_SUCCEEDED;
if (!success) {
ret = PackageManagerInternal.ENABLE_ROLLBACK_FAILED;
@@ -238,19 +232,16 @@
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_CANCEL_ENABLE_ROLLBACK.equals(intent.getAction())) {
- int token = intent.getIntExtra(
- PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN, -1);
+ int sessionId = intent.getIntExtra(
+ PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_SESSION_ID, -1);
if (LOCAL_LOGV) {
- Slog.v(TAG, "broadcast=ACTION_CANCEL_ENABLE_ROLLBACK token=" + token);
+ Slog.v(TAG, "broadcast=ACTION_CANCEL_ENABLE_ROLLBACK id=" + sessionId);
}
synchronized (mLock) {
- for (int i = 0; i < mRollbacks.size(); ++i) {
- Rollback rollback = mRollbacks.get(i);
- if (rollback.hasToken(token) && rollback.isEnabling()) {
- mRollbacks.remove(i);
- rollback.delete(mAppDataRollbackHelper);
- break;
- }
+ Rollback rollback = getRollbackForSessionLocked(sessionId);
+ if (rollback != null && rollback.isEnabling()) {
+ mRollbacks.remove(rollback);
+ rollback.delete(mAppDataRollbackHelper);
}
}
}
@@ -684,24 +675,6 @@
return mHandlerThread.getThreadHandler();
}
- // Returns true if <code>session</code> has installFlags and code path
- // matching the installFlags and new package code path given to
- // enableRollback.
- @WorkerThread
- private boolean sessionMatchesForEnableRollback(PackageInstaller.SessionInfo session,
- int installFlags, File newPackageCodePath) {
- if (session == null || session.resolvedBaseCodePath == null) {
- return false;
- }
-
- File packageCodePath = new File(session.resolvedBaseCodePath).getParentFile();
- if (newPackageCodePath.equals(packageCodePath) && installFlags == session.installFlags) {
- return true;
- }
-
- return false;
- }
-
@AnyThread
private Context getContextAsUser(UserHandle user) {
try {
@@ -716,58 +689,26 @@
* staged for install with rollback enabled. Called before the package has
* been installed.
*
- * @param installFlags information about what is being installed.
- * @param newPackageCodePath path to the package about to be installed.
- * @param user the user that owns the install session to enable rollback on.
- * @param token the distinct rollback token sent by package manager.
+ * @param sessionId the id of the install session
* @return true if enabling the rollback succeeds, false otherwise.
*/
@WorkerThread
- private boolean enableRollback(
- int installFlags, File newPackageCodePath, @UserIdInt int user, int token) {
+ private boolean enableRollback(int sessionId) {
if (LOCAL_LOGV) {
- Slog.v(TAG, "enableRollback user=" + user + " token=" + token
- + " path=" + newPackageCodePath.getAbsolutePath());
+ Slog.v(TAG, "enableRollback sessionId=" + sessionId);
}
- // Find the session id associated with this install.
- // TODO: It would be nice if package manager or package installer told
- // us the session directly, rather than have to search for it
- // ourselves.
-
- // getAllSessions only returns sessions for the associated user.
- // Create a context with the right user so we can find the matching
- // session.
- final Context context = getContextAsUser(UserHandle.of(user));
- if (context == null) {
- Slog.e(TAG, "Unable to create context for install session user.");
+ PackageInstaller installer = mContext.getPackageManager().getPackageInstaller();
+ PackageInstaller.SessionInfo packageSession = installer.getSessionInfo(sessionId);
+ if (packageSession == null) {
+ Slog.e(TAG, "Unable to find session for enabled rollback.");
return false;
}
- PackageInstaller.SessionInfo parentSession = null;
- PackageInstaller.SessionInfo packageSession = null;
- PackageInstaller installer = context.getPackageManager().getPackageInstaller();
- for (PackageInstaller.SessionInfo info : installer.getAllSessions()) {
- if (info.isMultiPackage()) {
- for (int childId : info.getChildSessionIds()) {
- PackageInstaller.SessionInfo child = installer.getSessionInfo(childId);
- if (sessionMatchesForEnableRollback(child, installFlags, newPackageCodePath)) {
- // TODO: Check we only have one matching session?
- parentSession = info;
- packageSession = child;
- break;
- }
- }
- } else if (sessionMatchesForEnableRollback(info, installFlags, newPackageCodePath)) {
- // TODO: Check we only have one matching session?
- parentSession = info;
- packageSession = info;
- break;
- }
- }
-
- if (parentSession == null || packageSession == null) {
- Slog.e(TAG, "Unable to find session for enabled rollback.");
+ PackageInstaller.SessionInfo parentSession = packageSession.hasParentSessionId()
+ ? installer.getSessionInfo(packageSession.getParentSessionId()) : packageSession;
+ if (parentSession == null) {
+ Slog.e(TAG, "Unable to find parent session for enabled rollback.");
return false;
}
@@ -804,7 +745,6 @@
newRollback = createNewRollbackLocked(parentSession);
}
}
- newRollback.addToken(token);
return enableRollbackForPackageSession(newRollback, packageSession);
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
index a641f06..1d31285 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
@@ -196,8 +196,8 @@
hidlModel.header.type = aidl2hidlSoundModelType(aidlModel.type);
hidlModel.header.uuid = aidl2hidlUuid(aidlModel.uuid);
hidlModel.header.vendorUuid = aidl2hidlUuid(aidlModel.vendorUuid);
- hidlModel.data = HidlMemoryUtil.byteArrayToHidlMemory(aidlModel.data,
- "SoundTrigger SoundModel");
+ hidlModel.data = HidlMemoryUtil.fileDescriptorToHidlMemory(aidlModel.data,
+ aidlModel.dataSize);
return hidlModel;
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/Dumpable.java b/services/core/java/com/android/server/soundtrigger_middleware/Dumpable.java
new file mode 100644
index 0000000..f9aa009
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/Dumpable.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+
+import java.io.PrintWriter;
+
+/**
+ * An interface of an object that can generate a dump.
+ */
+interface Dumpable {
+ /**
+ * Generate a human-readable dump into the given writer.
+ * @param pw The writer.
+ */
+ void dump(@NonNull PrintWriter pw);
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ObjectPrinter.java b/services/core/java/com/android/server/soundtrigger_middleware/ObjectPrinter.java
new file mode 100644
index 0000000..7f047f8
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ObjectPrinter.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * A collection of pretty-print utilities for data objects.
+ */
+class ObjectPrinter {
+ /** Default maximum elements to print in a collection. */
+ static public final int kDefaultMaxCollectionLength = 16;
+
+ /**
+ * Simple version of {@link #print(Object, boolean, int)} that prints an object, without
+ * recursing into sub-objects.
+ *
+ * @param obj The object to print.
+ * @return A string representing the object.
+ */
+ static String print(@Nullable Object obj) {
+ return print(obj, false, kDefaultMaxCollectionLength);
+ }
+
+ /**
+ * Pretty-prints an object.
+ *
+ * @param obj The object to print.
+ * @param deep Whether to pretty-print sub-objects (if false, just prints them
+ * with {@link Object#toString()}).
+ * @param maxCollectionLength Whenever encountering collections, maximum number of elements to
+ * print.
+ * @return A string representing the object.
+ */
+ static String print(@Nullable Object obj, boolean deep, int maxCollectionLength) {
+ StringBuilder builder = new StringBuilder();
+ print(builder, obj, deep, maxCollectionLength);
+ return builder.toString();
+ }
+
+ /**
+ * This version is suitable for use inside a toString() override of an object, e.g.:
+ * <pre><code>
+ * class MyObject {
+ * ...
+ * @Override
+ * String toString() {
+ * return ObjectPrinter.printPublicFields(this, ...);
+ * }
+ * }
+ * </code></pre>
+ *
+ * @param obj The object to print.
+ * @param deep Whether to pretty-print sub-objects (if false, just prints them
+ * with {@link Object#toString()}).
+ * @param maxCollectionLength Whenever encountering collections, maximum number of elements to
+ * print.
+ */
+ static String printPublicFields(@Nullable Object obj, boolean deep, int maxCollectionLength) {
+ StringBuilder builder = new StringBuilder();
+ printPublicFields(builder, obj, deep, maxCollectionLength);
+ return builder.toString();
+ }
+
+ /**
+ * A version of {@link #print(Object, boolean, int)} that uses a {@link StringBuilder}.
+ *
+ * @param builder StringBuilder to print into.
+ * @param obj The object to print.
+ * @param deep Whether to pretty-print sub-objects (if false, just prints them
+ * with {@link Object#toString()}).
+ * @param maxCollectionLength Whenever encountering collections, maximum number of elements to
+ * print.
+ */
+ static void print(@NonNull StringBuilder builder, @Nullable Object obj, boolean deep,
+ int maxCollectionLength) {
+ try {
+ if (obj == null) {
+ builder.append("null");
+ return;
+ }
+ if (obj instanceof Boolean) {
+ builder.append(obj.toString());
+ return;
+ }
+ if (obj instanceof Number) {
+ builder.append(obj.toString());
+ return;
+ }
+ if (obj instanceof Character) {
+ builder.append('\'');
+ builder.append(obj.toString());
+ builder.append('\'');
+ return;
+ }
+ if (obj instanceof String) {
+ builder.append('"');
+ builder.append(obj.toString());
+ builder.append('"');
+ return;
+ }
+
+ Class cls = obj.getClass();
+
+ if (Collection.class.isAssignableFrom(cls)) {
+ Collection collection = (Collection) obj;
+ builder.append("[ ");
+ int length = collection.size();
+ boolean isLong = false;
+ int i = 0;
+ for (Object child : collection) {
+ if (i > 0) {
+ builder.append(", ");
+ }
+ if (i >= maxCollectionLength) {
+ isLong = true;
+ break;
+ }
+ print(builder, child, deep, maxCollectionLength);
+ ++i;
+ }
+ if (isLong) {
+ builder.append("... (+");
+ builder.append(length - maxCollectionLength);
+ builder.append(" entries)");
+ }
+ builder.append(" ]");
+ return;
+ }
+
+ if (Map.class.isAssignableFrom(cls)) {
+ Map<?, ?> map = (Map<?, ?>) obj;
+ builder.append("< ");
+ int length = map.size();
+ boolean isLong = false;
+ int i = 0;
+ for (Map.Entry<?, ?> child : map.entrySet()) {
+ if (i > 0) {
+ builder.append(", ");
+ }
+ if (i >= maxCollectionLength) {
+ isLong = true;
+ break;
+ }
+ print(builder, child.getKey(), deep, maxCollectionLength);
+ builder.append(": ");
+ print(builder, child.getValue(), deep, maxCollectionLength);
+ ++i;
+ }
+ if (isLong) {
+ builder.append("... (+");
+ builder.append(length - maxCollectionLength);
+ builder.append(" entries)");
+ }
+ builder.append(" >");
+ return;
+ }
+
+ if (cls.isArray()) {
+ builder.append("[ ");
+ int length = Array.getLength(obj);
+ boolean isLong = false;
+ for (int i = 0; i < length; ++i) {
+ if (i > 0) {
+ builder.append(", ");
+ }
+ if (i >= maxCollectionLength) {
+ isLong = true;
+ break;
+ }
+ print(builder, Array.get(obj, i), deep, maxCollectionLength);
+ }
+ if (isLong) {
+ builder.append("... (+");
+ builder.append(length - maxCollectionLength);
+ builder.append(" entries)");
+ }
+ builder.append(" ]");
+ return;
+ }
+
+ if (!deep) {
+ builder.append(obj.toString());
+ return;
+ }
+ printPublicFields(builder, obj, deep, maxCollectionLength);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * A version of {@link #printPublicFields(Object, boolean, int)} that uses a {@link
+ * StringBuilder}.
+ *
+ * @param obj The object to print.
+ * @param deep Whether to pretty-print sub-objects (if false, just prints them
+ * with {@link Object#toString()}).
+ * @param maxCollectionLength Whenever encountering collections, maximum number of elements to
+ * print.
+ */
+ static void printPublicFields(@NonNull StringBuilder builder, @Nullable Object obj,
+ boolean deep,
+ int maxCollectionLength) {
+ try {
+ Class cls = obj.getClass();
+ builder.append("{ ");
+
+ boolean first = true;
+ for (Field fld : cls.getDeclaredFields()) {
+ int mod = fld.getModifiers();
+ if ((mod & Modifier.PUBLIC) != 0 && (mod & Modifier.STATIC) == 0) {
+ if (first) {
+ first = false;
+ } else {
+ builder.append(", ");
+ }
+ builder.append(fld.getName());
+ builder.append(": ");
+ print(builder, fld.get(obj), deep, maxCollectionLength);
+ }
+ }
+ builder.append(" }");
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
new file mode 100644
index 0000000..fa78cb0
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
@@ -0,0 +1,454 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.media.soundtrigger_middleware.ISoundTriggerCallback;
+import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
+import android.media.soundtrigger_middleware.ISoundTriggerModule;
+import android.media.soundtrigger_middleware.ModelParameterRange;
+import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
+import android.media.soundtrigger_middleware.PhraseSoundModel;
+import android.media.soundtrigger_middleware.RecognitionConfig;
+import android.media.soundtrigger_middleware.RecognitionEvent;
+import android.media.soundtrigger_middleware.SoundModel;
+import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.LinkedList;
+
+/**
+ * An ISoundTriggerMiddlewareService decorator, which adds logging of all API calls (and
+ * callbacks).
+ *
+ * All API methods should follow this structure:
+ * <pre><code>
+ * @Override
+ * public @NonNull ReturnType someMethod(ArgType1 arg1, ArgType2 arg2) throws ExceptionType {
+ * try {
+ * ReturnType result = mDelegate.someMethod(arg1, arg2);
+ * logReturn("someMethod", result, arg1, arg2);
+ * return result;
+ * } catch (Exception e) {
+ * logException("someMethod", e, arg1, arg2);
+ * throw e;
+ * }
+ * }
+ * </code></pre>
+ * The actual handling of these events is then done inside of {@link #logReturnWithObject(Object,
+ * String, Object, Object[])}, {@link #logVoidReturnWithObject(Object, String, Object[])} and {@link
+ * #logExceptionWithObject(Object, String, Exception, Object[])}.
+ */
+public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareService, Dumpable {
+ private static final String TAG = "SoundTriggerMiddlewareLogging";
+ private final @NonNull ISoundTriggerMiddlewareService mDelegate;
+
+ public SoundTriggerMiddlewareLogging(@NonNull ISoundTriggerMiddlewareService delegate) {
+ mDelegate = delegate;
+ }
+
+ @Override
+ public @NonNull SoundTriggerModuleDescriptor[] listModules() throws RemoteException {
+ try {
+ SoundTriggerModuleDescriptor[] result = mDelegate.listModules();
+ logReturn("listModules", result);
+ return result;
+ } catch (Exception e) {
+ logException("listModules", e);
+ throw e;
+ }
+ }
+
+ @Override
+ public @NonNull ISoundTriggerModule attach(int handle, ISoundTriggerCallback callback)
+ throws RemoteException {
+ try {
+ ISoundTriggerModule result = mDelegate.attach(handle, new CallbackLogging(callback));
+ logReturn("attach", result, handle, callback);
+ return new ModuleLogging(result);
+ } catch (Exception e) {
+ logException("attach", e, handle, callback);
+ throw e;
+ }
+ }
+
+ @Override
+ public void setExternalCaptureState(boolean active) throws RemoteException {
+ try {
+ mDelegate.setExternalCaptureState(active);
+ logVoidReturn("setExternalCaptureState", active);
+ } catch (Exception e) {
+ logException("setExternalCaptureState", e, active);
+ throw e;
+ }
+ }
+
+ @Override public IBinder asBinder() {
+ throw new UnsupportedOperationException(
+ "This implementation is not inteded to be used directly with Binder.");
+ }
+
+ // Override toString() in order to have the delegate's ID in it.
+ @Override
+ public String toString() {
+ return mDelegate.toString();
+ }
+
+ private void logException(String methodName, Exception ex, Object... args) {
+ logExceptionWithObject(this, methodName, ex, args);
+ }
+
+ private void logReturn(String methodName, Object retVal, Object... args) {
+ logReturnWithObject(this, methodName, retVal, args);
+ }
+
+ private void logVoidReturn(String methodName, Object... args) {
+ logVoidReturnWithObject(this, methodName, args);
+ }
+
+ private class CallbackLogging implements ISoundTriggerCallback {
+ private final ISoundTriggerCallback mDelegate;
+
+ private CallbackLogging(ISoundTriggerCallback delegate) {
+ mDelegate = delegate;
+ }
+
+ @Override
+ public void onRecognition(int modelHandle, RecognitionEvent event) throws RemoteException {
+ try {
+ mDelegate.onRecognition(modelHandle, event);
+ logVoidReturn("onRecognition", modelHandle, event);
+ } catch (Exception e) {
+ logException("onRecognition", e, modelHandle, event);
+ throw e;
+ }
+ }
+
+ @Override
+ public void onPhraseRecognition(int modelHandle, PhraseRecognitionEvent event)
+ throws RemoteException {
+ try {
+ mDelegate.onPhraseRecognition(modelHandle, event);
+ logVoidReturn("onPhraseRecognition", modelHandle, event);
+ } catch (Exception e) {
+ logException("onPhraseRecognition", e, modelHandle, event);
+ throw e;
+ }
+ }
+
+ @Override
+ public void onRecognitionAvailabilityChange(boolean available) throws RemoteException {
+ try {
+ mDelegate.onRecognitionAvailabilityChange(available);
+ logVoidReturn("onRecognitionAvailabilityChange", available);
+ } catch (Exception e) {
+ logException("onRecognitionAvailabilityChange", e, available);
+ throw e;
+ }
+ }
+
+ @Override
+ public void onModuleDied() throws RemoteException {
+ try {
+ mDelegate.onModuleDied();
+ logVoidReturn("onModuleDied");
+ } catch (Exception e) {
+ logException("onModuleDied", e);
+ throw e;
+ }
+ }
+
+ private void logException(String methodName, Exception ex, Object... args) {
+ logExceptionWithObject(this, methodName, ex, args);
+ }
+
+ private void logVoidReturn(String methodName, Object... args) {
+ logVoidReturnWithObject(this, methodName, args);
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return mDelegate.asBinder();
+ }
+
+ // Override toString() in order to have the delegate's ID in it.
+ @Override
+ public String toString() {
+ return mDelegate.toString();
+ }
+ }
+
+ private class ModuleLogging implements ISoundTriggerModule {
+ private final ISoundTriggerModule mDelegate;
+
+ private ModuleLogging(ISoundTriggerModule delegate) {
+ mDelegate = delegate;
+ }
+
+ @Override
+ public int loadModel(SoundModel model) throws RemoteException {
+ try {
+ int result = mDelegate.loadModel(model);
+ logReturn("loadModel", result, model);
+ return result;
+ } catch (Exception e) {
+ logException("loadModel", e, model);
+ throw e;
+ }
+ }
+
+ @Override
+ public int loadPhraseModel(PhraseSoundModel model) throws RemoteException {
+ try {
+ int result = mDelegate.loadPhraseModel(model);
+ logReturn("loadPhraseModel", result, model);
+ return result;
+ } catch (Exception e) {
+ logException("loadPhraseModel", e, model);
+ throw e;
+ }
+ }
+
+ @Override
+ public void unloadModel(int modelHandle) throws RemoteException {
+ try {
+ mDelegate.unloadModel(modelHandle);
+ logVoidReturn("unloadModel", modelHandle);
+ } catch (Exception e) {
+ logException("unloadModel", e, modelHandle);
+ throw e;
+ }
+ }
+
+ @Override
+ public void startRecognition(int modelHandle, RecognitionConfig config)
+ throws RemoteException {
+ try {
+ mDelegate.startRecognition(modelHandle, config);
+ logVoidReturn("startRecognition", modelHandle, config);
+ } catch (Exception e) {
+ logException("startRecognition", e, modelHandle, config);
+ throw e;
+ }
+ }
+
+ @Override
+ public void stopRecognition(int modelHandle) throws RemoteException {
+ try {
+ mDelegate.stopRecognition(modelHandle);
+ logVoidReturn("stopRecognition", modelHandle);
+ } catch (Exception e) {
+ logException("stopRecognition", e, modelHandle);
+ throw e;
+ }
+ }
+
+ @Override
+ public void forceRecognitionEvent(int modelHandle) throws RemoteException {
+ try {
+ mDelegate.forceRecognitionEvent(modelHandle);
+ logVoidReturn("forceRecognitionEvent", modelHandle);
+ } catch (Exception e) {
+ logException("forceRecognitionEvent", e, modelHandle);
+ throw e;
+ }
+ }
+
+ @Override
+ public void setModelParameter(int modelHandle, int modelParam, int value)
+ throws RemoteException {
+ try {
+ mDelegate.setModelParameter(modelHandle, modelParam, value);
+ logVoidReturn("setModelParameter", modelHandle, modelParam, value);
+ } catch (Exception e) {
+ logException("setModelParameter", e, modelHandle, modelParam, value);
+ throw e;
+ }
+ }
+
+ @Override
+ public int getModelParameter(int modelHandle, int modelParam) throws RemoteException {
+ try {
+ int result = mDelegate.getModelParameter(modelHandle, modelParam);
+ logReturn("getModelParameter", result, modelHandle, modelParam);
+ return result;
+ } catch (Exception e) {
+ logException("getModelParameter", e, modelHandle, modelParam);
+ throw e;
+ }
+ }
+
+ @Override
+ public ModelParameterRange queryModelParameterSupport(int modelHandle, int modelParam)
+ throws RemoteException {
+ try {
+ ModelParameterRange result = mDelegate.queryModelParameterSupport(modelHandle,
+ modelParam);
+ logReturn("queryModelParameterSupport", result, modelHandle, modelParam);
+ return result;
+ } catch (Exception e) {
+ logException("queryModelParameterSupport", e, modelHandle, modelParam);
+ throw e;
+ }
+ }
+
+ @Override
+ public void detach() throws RemoteException {
+ try {
+ mDelegate.detach();
+ logVoidReturn("detach");
+ } catch (Exception e) {
+ logException("detach", e);
+ throw e;
+ }
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return mDelegate.asBinder();
+ }
+
+ // Override toString() in order to have the delegate's ID in it.
+ @Override
+ public String toString() {
+ return mDelegate.toString();
+ }
+
+ private void logException(String methodName, Exception ex, Object... args) {
+ logExceptionWithObject(this, methodName, ex, args);
+ }
+
+ private void logReturn(String methodName, Object retVal, Object... args) {
+ logReturnWithObject(this, methodName, retVal, args);
+ }
+
+ private void logVoidReturn(String methodName, Object... args) {
+ logVoidReturnWithObject(this, methodName, args);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ // Actual logging logic below.
+ private static final int NUM_EVENTS_TO_DUMP = 64;
+ private final static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss:SSS");
+ private final @NonNull LinkedList<Event> mLastEvents = new LinkedList<>();
+
+ static private class Event {
+ public final long timestamp = System.currentTimeMillis();
+ public final String message;
+
+ private Event(String message) {
+ this.message = message;
+ }
+ }
+
+ private static String printArgs(@NonNull Object[] args) {
+ StringBuilder result = new StringBuilder();
+ for (int i = 0; i < args.length; ++i) {
+ if (i > 0) {
+ result.append(", ");
+ }
+ printObject(result, args[i]);
+ }
+ return result.toString();
+ }
+
+ private static void printObject(@NonNull StringBuilder builder, @Nullable Object obj) {
+ if (obj instanceof Parcelable) {
+ ObjectPrinter.print(builder, obj, true, 16);
+ } else {
+ builder.append(obj.toString());
+ }
+ }
+
+ private static String printObject(@Nullable Object obj) {
+ StringBuilder builder = new StringBuilder();
+ printObject(builder, obj);
+ return builder.toString();
+ }
+
+ private void logReturnWithObject(@NonNull Object object, String methodName,
+ @Nullable Object retVal,
+ @NonNull Object[] args) {
+ final String message = String.format("%s[this=%s, caller=%d/%d](%s) -> %s", methodName,
+ object,
+ Binder.getCallingUid(), Binder.getCallingPid(),
+ printArgs(args),
+ printObject(retVal));
+ Log.i(TAG, message);
+ appendMessage(message);
+ }
+
+ private void logVoidReturnWithObject(@NonNull Object object, @NonNull String methodName,
+ @NonNull Object[] args) {
+ final String message = String.format("%s[this=%s, caller=%d/%d](%s)", methodName,
+ object,
+ Binder.getCallingUid(), Binder.getCallingPid(),
+ printArgs(args));
+ Log.i(TAG, message);
+ appendMessage(message);
+ }
+
+ private void logExceptionWithObject(@NonNull Object object, @NonNull String methodName,
+ @NonNull Exception ex,
+ Object[] args) {
+ final String message = String.format("%s[this=%s, caller=%d/%d](%s) threw", methodName,
+ object,
+ Binder.getCallingUid(), Binder.getCallingPid(),
+ printArgs(args));
+ Log.e(TAG, message, ex);
+ appendMessage(message + " " + ex.toString());
+ }
+
+ private void appendMessage(@NonNull String message) {
+ Event event = new Event(message);
+ synchronized (mLastEvents) {
+ if (mLastEvents.size() > NUM_EVENTS_TO_DUMP) {
+ mLastEvents.remove();
+ }
+ mLastEvents.add(event);
+ }
+ }
+
+ @Override public void dump(PrintWriter pw) {
+ pw.println();
+ pw.println("=========================================");
+ pw.println("Last events");
+ pw.println("=========================================");
+ synchronized (mLastEvents) {
+ for (Event event : mLastEvents) {
+ pw.print(DATE_FORMAT.format(new Date(event.timestamp)));
+ pw.print('\t');
+ pw.println(event.message);
+ }
+ }
+ pw.println();
+
+ if (mDelegate instanceof Dumpable) {
+ ((Dumpable) mDelegate).dump(pw);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
index 1ed97be..0d8fc76 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
@@ -16,91 +16,40 @@
package com.android.server.soundtrigger_middleware;
-import android.Manifest;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.Context;
-import android.content.PermissionChecker;
import android.hardware.soundtrigger.V2_0.ISoundTriggerHw;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
import android.media.soundtrigger_middleware.PhraseSoundModel;
import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.RecognitionStatus;
import android.media.soundtrigger_middleware.SoundModel;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
-import android.media.soundtrigger_middleware.Status;
import android.os.RemoteException;
-import android.os.ServiceSpecificException;
import android.util.Log;
-import com.android.internal.util.Preconditions;
import com.android.server.SystemService;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.Objects;
-import java.util.Set;
/**
* This is a wrapper around an {@link ISoundTriggerMiddlewareService} implementation, which exposes
- * it as a Binder service and enforces permissions and correct usage by the client, as well as makes
- * sure that exceptions representing a server malfunction do not get sent to the client.
+ * it as a Binder service.
* <p>
- * This is intended to extract the non-business logic out of the underlying implementation and thus
- * make it easier to maintain each one of those separate aspects. A design trade-off is being made
- * here, in that this class would need to essentially eavesdrop on all the client-server
- * communication and retain all state known to the client, while the client doesn't necessarily care
- * about all of it, and while the server has its own representation of this information. However,
- * in this case, this is a small amount of data, and the benefits in code elegance seem worth it.
- * There is also some additional cost in employing a simplistic locking mechanism here, but
- * following the same line of reasoning, the benefits in code simplicity outweigh it.
+ * This is intended to facilitate a pattern of decorating the core implementation (business logic)
+ * of the interface with every decorator implementing a different aspect of the service, such as
+ * validation and logging. This class acts as the top-level decorator, which also adds the binder-
+ * related functionality (hence, it extends ISoundTriggerMiddlewareService.Stub as rather than
+ * implements ISoundTriggerMiddlewareService), and does the same thing for child interfaces
+ * returned.
* <p>
- * Every public method in this class, overriding an interface method, must follow the following
- * pattern:
- * <code><pre>
- * @Override public T method(S arg) {
- * // Permission check.
- * checkPermissions();
- * // Input validation.
- * ValidationUtil.validateS(arg);
- * synchronized (this) {
- * // State validation.
- * if (...state is not valid for this call...) {
- * throw new IllegalStateException("State is invalid because...");
- * }
- * // From here on, every exception isn't client's fault.
- * try {
- * T result = mDelegate.method(arg);
- * // Update state.;
- * ...
- * return result;
- * } catch (Exception e) {
- * throw handleException(e);
- * }
- * }
- * }
- * </pre></code>
- * Following this patterns ensures a consistent and rigorous handling of all aspects associated
- * with client-server separation.
- * <p>
- * <b>Exception handling approach:</b><br>
- * We make sure all client faults (permissions, argument and state validation) happen first, and
- * would throw {@link SecurityException}, {@link IllegalArgumentException}/
- * {@link NullPointerException} or {@link
- * IllegalStateException}, respectively. All those exceptions are treated specially by Binder and
- * will get sent back to the client.<br>
- * Once this is done, any subsequent fault is considered a server fault. Only {@link
- * RecoverableException}s thrown by the implementation are special-cased: they would get sent back
- * to the caller as a {@link ServiceSpecificException}, which is the behavior of Binder. Any other
- * exception gets wrapped with a {@link InternalServerError}, which is specifically chosen as a type
- * that <b>does NOT</b> get forwarded by binder. Those exceptions would be handled by a high-level
- * exception handler on the server side, typically resulting in rebooting the server.
+ * The inner class {@link Lifecycle} acts as both a factory, composing the various aspect-decorators
+ * to create a full-featured implementation, as well as as an entry-point for presenting this
+ * implementation as a system service.
* <p>
* <b>Exposing this service as a System Service:</b><br>
* Insert this line into {@link com.android.server.SystemServer}:
@@ -113,224 +62,100 @@
public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareService.Stub {
static private final String TAG = "SoundTriggerMiddlewareService";
+ @NonNull
private final ISoundTriggerMiddlewareService mDelegate;
- private final Context mContext;
- private Set<Integer> mModuleHandles;
/**
* Constructor for internal use only. Could be exposed for testing purposes in the future.
* Users should access this class via {@link Lifecycle}.
*/
- private SoundTriggerMiddlewareService(
- @NonNull ISoundTriggerMiddlewareService delegate, @NonNull Context context) {
- mDelegate = delegate;
- mContext = context;
- }
-
- /**
- * Generic exception handling for exceptions thrown by the underlying implementation.
- *
- * Would throw any {@link RecoverableException} as a {@link ServiceSpecificException} (passed
- * by Binder to the caller) and <i>any other</i> exception as {@link InternalServerError}
- * (<b>not</b> passed by Binder to the caller).
- * <p>
- * Typical usage:
- * <code><pre>
- * try {
- * ... Do server operations ...
- * } catch (Exception e) {
- * throw handleException(e);
- * }
- * </pre></code>
- */
- private static @NonNull
- RuntimeException handleException(@NonNull Exception e) {
- if (e instanceof RecoverableException) {
- throw new ServiceSpecificException(((RecoverableException) e).errorCode,
- e.getMessage());
- }
- throw new InternalServerError(e);
+ private SoundTriggerMiddlewareService(@NonNull ISoundTriggerMiddlewareService delegate) {
+ mDelegate = Objects.requireNonNull(delegate);
}
@Override
public @NonNull
- SoundTriggerModuleDescriptor[] listModules() {
- // Permission check.
- checkPermissions();
- // Input validation (always valid).
-
- synchronized (this) {
- // State validation (always valid).
-
- // From here on, every exception isn't client's fault.
- try {
- SoundTriggerModuleDescriptor[] result = mDelegate.listModules();
- mModuleHandles = new HashSet<>(result.length);
- for (SoundTriggerModuleDescriptor desc : result) {
- mModuleHandles.add(desc.handle);
- }
- return result;
- } catch (Exception e) {
- throw handleException(e);
- }
- }
+ SoundTriggerModuleDescriptor[] listModules() throws RemoteException {
+ return mDelegate.listModules();
}
@Override
public @NonNull
- ISoundTriggerModule attach(int handle, @NonNull ISoundTriggerCallback callback) {
- // Permission check.
- checkPermissions();
- // Input validation.
- Objects.requireNonNull(callback);
- Objects.requireNonNull(callback.asBinder());
-
- synchronized (this) {
- // State validation.
- if (mModuleHandles == null) {
- throw new IllegalStateException(
- "Client must call listModules() prior to attaching.");
- }
- if (!mModuleHandles.contains(handle)) {
- throw new IllegalArgumentException("Invalid handle: " + handle);
- }
-
- // From here on, every exception isn't client's fault.
- try {
- ModuleService moduleService = new ModuleService(callback);
- moduleService.attach(mDelegate.attach(handle, moduleService));
- return moduleService;
- } catch (Exception e) {
- throw handleException(e);
- }
- }
+ ISoundTriggerModule attach(int handle, @NonNull ISoundTriggerCallback callback)
+ throws RemoteException {
+ return new ModuleService(mDelegate.attach(handle, callback));
}
@Override
- public void setExternalCaptureState(boolean active) {
- // Permission check.
- checkPreemptPermissions();
- // Input validation (always valid).
+ public void setExternalCaptureState(boolean active) throws RemoteException {
+ mDelegate.setExternalCaptureState(active);
+ }
- synchronized (this) {
- // State validation (always valid).
-
- // From here on, every exception isn't client's fault.
- try {
- mDelegate.setExternalCaptureState(active);
- } catch (Exception e) {
- throw handleException(e);
- }
+ @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ if (mDelegate instanceof Dumpable) {
+ ((Dumpable) mDelegate).dump(fout);
}
}
- /**
- * Throws a {@link SecurityException} if caller permanently doesn't have the given permission,
- * or a {@link ServiceSpecificException} with a {@link Status#TEMPORARY_PERMISSION_DENIED} if
- * caller temporarily doesn't have the right permissions to use this service.
- */
- private void checkPermissions() {
- enforcePermission(Manifest.permission.RECORD_AUDIO);
- enforcePermission(Manifest.permission.CAPTURE_AUDIO_HOTWORD);
- }
+ private final static class ModuleService extends ISoundTriggerModule.Stub {
+ private final ISoundTriggerModule mDelegate;
- /**
- * Throws a {@link SecurityException} if caller permanently doesn't have the given permission,
- * or a {@link ServiceSpecificException} with a {@link Status#TEMPORARY_PERMISSION_DENIED} if
- * caller temporarily doesn't have the right permissions to preempt active sound trigger
- * sessions.
- */
- private void checkPreemptPermissions() {
- enforcePermission(Manifest.permission.PREEMPT_SOUND_TRIGGER);
- }
-
- /**
- * Throws a {@link SecurityException} if caller permanently doesn't have the given permission,
- * or a {@link ServiceSpecificException} with a {@link Status#TEMPORARY_PERMISSION_DENIED} if
- * caller temporarily doesn't have the given permission.
- *
- * @param permission The permission to check.
- */
- private void enforcePermission(String permission) {
- final int status = PermissionChecker.checkCallingOrSelfPermissionForPreflight(mContext,
- permission);
- switch (status) {
- case PermissionChecker.PERMISSION_GRANTED:
- return;
- case PermissionChecker.PERMISSION_HARD_DENIED:
- throw new SecurityException(
- String.format("Caller must have the %s permission.", permission));
- case PermissionChecker.PERMISSION_SOFT_DENIED:
- throw new ServiceSpecificException(Status.TEMPORARY_PERMISSION_DENIED,
- String.format("Caller must have the %s permission.", permission));
- default:
- throw new InternalServerError(
- new RuntimeException("Unexpected perimission check result."));
- }
- }
-
- /** State of a sound model. */
- static class ModelState {
- /** Activity state of a sound model. */
- enum Activity {
- /** Model is loaded, recognition is inactive. */
- LOADED,
- /** Model is loaded, recognition is active. */
- ACTIVE
+ private ModuleService(ISoundTriggerModule delegate) {
+ mDelegate = delegate;
}
- /** Activity state. */
- Activity activityState = Activity.LOADED;
-
- /**
- * A map of known parameter support. A missing key means we don't know yet whether the
- * parameter is supported. A null value means it is known to not be supported. A non-null
- * value indicates the valid value range.
- */
- private Map<Integer, ModelParameterRange> parameterSupport = new HashMap<>();
-
- /**
- * Check that the given parameter is known to be supported for this model.
- *
- * @param modelParam The parameter key.
- */
- void checkSupported(int modelParam) {
- if (!parameterSupport.containsKey(modelParam)) {
- throw new IllegalStateException("Parameter has not been checked for support.");
- }
- ModelParameterRange range = parameterSupport.get(modelParam);
- if (range == null) {
- throw new IllegalArgumentException("Paramater is not supported.");
- }
+ @Override
+ public int loadModel(SoundModel model) throws RemoteException {
+ return mDelegate.loadModel(model);
}
- /**
- * Check that the given parameter is known to be supported for this model and that the given
- * value is a valid value for it.
- *
- * @param modelParam The parameter key.
- * @param value The value.
- */
- void checkSupported(int modelParam, int value) {
- if (!parameterSupport.containsKey(modelParam)) {
- throw new IllegalStateException("Parameter has not been checked for support.");
- }
- ModelParameterRange range = parameterSupport.get(modelParam);
- if (range == null) {
- throw new IllegalArgumentException("Paramater is not supported.");
- }
- Preconditions.checkArgumentInRange(value, range.minInclusive, range.maxInclusive,
- "value");
+ @Override
+ public int loadPhraseModel(PhraseSoundModel model) throws RemoteException {
+ return mDelegate.loadPhraseModel(model);
}
- /**
- * Update support state for the given parameter for this model.
- *
- * @param modelParam The parameter key.
- * @param range The parameter value range, or null if not supported.
- */
- void updateParameterSupport(int modelParam, @Nullable ModelParameterRange range) {
- parameterSupport.put(modelParam, range);
+ @Override
+ public void unloadModel(int modelHandle) throws RemoteException {
+ mDelegate.unloadModel(modelHandle);
+ ;
+ }
+
+ @Override
+ public void startRecognition(int modelHandle, RecognitionConfig config)
+ throws RemoteException {
+ mDelegate.startRecognition(modelHandle, config);
+ }
+
+ @Override
+ public void stopRecognition(int modelHandle) throws RemoteException {
+ mDelegate.stopRecognition(modelHandle);
+ }
+
+ @Override
+ public void forceRecognitionEvent(int modelHandle) throws RemoteException {
+ mDelegate.forceRecognitionEvent(modelHandle);
+ }
+
+ @Override
+ public void setModelParameter(int modelHandle, int modelParam, int value)
+ throws RemoteException {
+ mDelegate.setModelParameter(modelHandle, modelParam, value);
+ }
+
+ @Override
+ public int getModelParameter(int modelHandle, int modelParam) throws RemoteException {
+ return mDelegate.getModelParameter(modelHandle, modelParam);
+ }
+
+ @Override
+ public ModelParameterRange queryModelParameterSupport(int modelHandle, int modelParam)
+ throws RemoteException {
+ return mDelegate.queryModelParameterSupport(modelHandle, modelParam);
+ }
+
+ @Override
+ public void detach() throws RemoteException {
+ mDelegate.detach();
}
}
@@ -355,395 +180,11 @@
publishBinderService(Context.SOUND_TRIGGER_MIDDLEWARE_SERVICE,
new SoundTriggerMiddlewareService(
- new SoundTriggerMiddlewareImpl(factories,
- new AudioSessionProviderImpl()),
- getContext()));
- }
- }
-
- /**
- * A wrapper around an {@link ISoundTriggerModule} implementation, to address the same aspects
- * mentioned in {@link SoundTriggerModule} above. This class follows the same conventions.
- */
- private class ModuleService extends ISoundTriggerModule.Stub implements ISoundTriggerCallback,
- DeathRecipient {
- private final ISoundTriggerCallback mCallback;
- private ISoundTriggerModule mDelegate;
- private @NonNull Map<Integer, ModelState> mLoadedModels = new HashMap<>();
-
- ModuleService(@NonNull ISoundTriggerCallback callback) {
- mCallback = callback;
- try {
- mCallback.asBinder().linkToDeath(this, 0);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
- }
-
- void attach(@NonNull ISoundTriggerModule delegate) {
- mDelegate = delegate;
- }
-
- @Override
- public int loadModel(@NonNull SoundModel model) {
- // Permission check.
- checkPermissions();
- // Input validation.
- ValidationUtil.validateGenericModel(model);
-
- synchronized (this) {
- // State validation.
- if (mDelegate == null) {
- throw new IllegalStateException("Module has been detached.");
- }
-
- // From here on, every exception isn't client's fault.
- try {
- int handle = mDelegate.loadModel(model);
- mLoadedModels.put(handle, new ModelState());
- return handle;
- } catch (Exception e) {
- throw handleException(e);
- }
- }
- }
-
- @Override
- public int loadPhraseModel(@NonNull PhraseSoundModel model) {
- // Permission check.
- checkPermissions();
- // Input validation.
- ValidationUtil.validatePhraseModel(model);
-
- synchronized (this) {
- // State validation.
- if (mDelegate == null) {
- throw new IllegalStateException("Module has been detached.");
- }
-
- // From here on, every exception isn't client's fault.
- try {
- int handle = mDelegate.loadPhraseModel(model);
- mLoadedModels.put(handle, new ModelState());
- return handle;
- } catch (Exception e) {
- throw handleException(e);
- }
- }
- }
-
- @Override
- public void unloadModel(int modelHandle) {
- // Permission check.
- checkPermissions();
- // Input validation (always valid).
-
- synchronized (this) {
- // State validation.
- if (mDelegate == null) {
- throw new IllegalStateException("Module has been detached.");
- }
- ModelState modelState = mLoadedModels.get(modelHandle);
- if (modelState == null) {
- throw new IllegalStateException("Invalid handle: " + modelHandle);
- }
- if (modelState.activityState != ModelState.Activity.LOADED) {
- throw new IllegalStateException("Model with handle: " + modelHandle
- + " has invalid state for unloading: " + modelState.activityState);
- }
-
- // From here on, every exception isn't client's fault.
- try {
- mDelegate.unloadModel(modelHandle);
- mLoadedModels.remove(modelHandle);
- } catch (Exception e) {
- throw handleException(e);
- }
- }
- }
-
- @Override
- public void startRecognition(int modelHandle, @NonNull RecognitionConfig config) {
- // Permission check.
- checkPermissions();
- // Input validation.
- ValidationUtil.validateRecognitionConfig(config);
-
- synchronized (this) {
- // State validation.
- if (mDelegate == null) {
- throw new IllegalStateException("Module has been detached.");
- }
- ModelState modelState = mLoadedModels.get(modelHandle);
- if (modelState == null) {
- throw new IllegalStateException("Invalid handle: " + modelHandle);
- }
- if (modelState.activityState != ModelState.Activity.LOADED) {
- throw new IllegalStateException("Model with handle: " + modelHandle
- + " has invalid state for starting recognition: "
- + modelState.activityState);
- }
-
- // From here on, every exception isn't client's fault.
- try {
- mDelegate.startRecognition(modelHandle, config);
- modelState.activityState = ModelState.Activity.ACTIVE;
- } catch (Exception e) {
- throw handleException(e);
- }
- }
- }
-
- @Override
- public void stopRecognition(int modelHandle) {
- // Permission check.
- checkPermissions();
- // Input validation (always valid).
-
- synchronized (this) {
- // State validation.
- if (mDelegate == null) {
- throw new IllegalStateException("Module has been detached.");
- }
- ModelState modelState = mLoadedModels.get(modelHandle);
- if (modelState == null) {
- throw new IllegalStateException("Invalid handle: " + modelHandle);
- }
- // stopRecognition is idempotent - no need to check model state.
-
- // From here on, every exception isn't client's fault.
- try {
- mDelegate.stopRecognition(modelHandle);
- modelState.activityState = ModelState.Activity.LOADED;
- } catch (Exception e) {
- throw handleException(e);
- }
- }
- }
-
- @Override
- public void forceRecognitionEvent(int modelHandle) {
- // Permission check.
- checkPermissions();
- // Input validation (always valid).
-
- synchronized (this) {
- // State validation.
- if (mDelegate == null) {
- throw new IllegalStateException("Module has been detached.");
- }
- ModelState modelState = mLoadedModels.get(modelHandle);
- if (modelState == null) {
- throw new IllegalStateException("Invalid handle: " + modelHandle);
- }
- // forceRecognitionEvent is idempotent - no need to check model state.
-
- // From here on, every exception isn't client's fault.
- try {
- mDelegate.forceRecognitionEvent(modelHandle);
- } catch (Exception e) {
- throw handleException(e);
- }
- }
- }
-
- @Override
- public void setModelParameter(int modelHandle, int modelParam, int value) {
- // Permission check.
- checkPermissions();
- // Input validation.
- ValidationUtil.validateModelParameter(modelParam);
-
- synchronized (this) {
- // State validation.
- if (mDelegate == null) {
- throw new IllegalStateException("Module has been detached.");
- }
- ModelState modelState = mLoadedModels.get(modelHandle);
- if (modelState == null) {
- throw new IllegalStateException("Invalid handle: " + modelHandle);
- }
- modelState.checkSupported(modelParam, value);
-
- // From here on, every exception isn't client's fault.
- try {
- mDelegate.setModelParameter(modelHandle, modelParam, value);
- } catch (Exception e) {
- throw handleException(e);
- }
- }
- }
-
- @Override
- public int getModelParameter(int modelHandle, int modelParam) {
- // Permission check.
- checkPermissions();
- // Input validation.
- ValidationUtil.validateModelParameter(modelParam);
-
- synchronized (this) {
- // State validation.
- if (mDelegate == null) {
- throw new IllegalStateException("Module has been detached.");
- }
- ModelState modelState = mLoadedModels.get(modelHandle);
- if (modelState == null) {
- throw new IllegalStateException("Invalid handle: " + modelHandle);
- }
- modelState.checkSupported(modelParam);
-
- // From here on, every exception isn't client's fault.
- try {
- return mDelegate.getModelParameter(modelHandle, modelParam);
- } catch (Exception e) {
- throw handleException(e);
- }
- }
- }
-
- @Override
- @Nullable
- public ModelParameterRange queryModelParameterSupport(int modelHandle, int modelParam) {
- // Permission check.
- checkPermissions();
- // Input validation.
- ValidationUtil.validateModelParameter(modelParam);
-
- synchronized (this) {
- // State validation.
- if (mDelegate == null) {
- throw new IllegalStateException("Module has been detached.");
- }
- ModelState modelState = mLoadedModels.get(modelHandle);
- if (modelState == null) {
- throw new IllegalStateException("Invalid handle: " + modelHandle);
- }
-
- // From here on, every exception isn't client's fault.
- try {
- ModelParameterRange result = mDelegate.queryModelParameterSupport(modelHandle,
- modelParam);
- modelState.updateParameterSupport(modelParam, result);
- return result;
- } catch (Exception e) {
- throw handleException(e);
- }
- }
- }
-
- @Override
- public void detach() {
- // Permission check.
- checkPermissions();
- // Input validation (always valid).
-
- synchronized (this) {
- // State validation.
- if (mDelegate == null) {
- throw new IllegalStateException("Module has already been detached.");
- }
- if (!mLoadedModels.isEmpty()) {
- throw new IllegalStateException("Cannot detach while models are loaded.");
- }
-
- // From here on, every exception isn't client's fault.
- try {
- detachInternal();
- } catch (Exception e) {
- throw handleException(e);
- }
- }
- }
-
- private void detachInternal() {
- try {
- mDelegate.detach();
- mDelegate = null;
- mCallback.asBinder().unlinkToDeath(this, 0);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
- }
-
- ////////////////////////////////////////////////////////////////////////////////////////////
- // Callbacks
-
- @Override
- public void onRecognition(int modelHandle, @NonNull RecognitionEvent event) {
- synchronized (this) {
- if (event.status != RecognitionStatus.FORCED) {
- mLoadedModels.get(modelHandle).activityState = ModelState.Activity.LOADED;
- }
- try {
- mCallback.onRecognition(modelHandle, event);
- } catch (RemoteException e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback exception.", e);
- }
- }
- }
-
- @Override
- public void onPhraseRecognition(int modelHandle, @NonNull PhraseRecognitionEvent event) {
- synchronized (this) {
- if (event.common.status != RecognitionStatus.FORCED) {
- mLoadedModels.get(modelHandle).activityState = ModelState.Activity.LOADED;
- }
- try {
- mCallback.onPhraseRecognition(modelHandle, event);
- } catch (RemoteException e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback exception.", e);
- }
- }
- }
-
- @Override
- public void onRecognitionAvailabilityChange(boolean available) {
- synchronized (this) {
- try {
- mCallback.onRecognitionAvailabilityChange(available);
- } catch (RemoteException e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback exception.", e);
- }
- }
- }
-
- @Override
- public void onModuleDied() {
- synchronized (this) {
- try {
- mCallback.onModuleDied();
- } catch (RemoteException e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback exception.", e);
- }
- }
- }
-
- @Override
- public void binderDied() {
- // This is called whenever our client process dies.
- synchronized (this) {
- try {
- // Gracefully stop all active recognitions and unload the models.
- for (Map.Entry<Integer, ModelState> entry : mLoadedModels.entrySet()) {
- if (entry.getValue().activityState == ModelState.Activity.ACTIVE) {
- mDelegate.stopRecognition(entry.getKey());
- }
- mDelegate.unloadModel(entry.getKey());
- }
- // Detach.
- detachInternal();
- } catch (Exception e) {
- throw handleException(e);
- }
- }
+ new SoundTriggerMiddlewareLogging(
+ new SoundTriggerMiddlewareValidation(
+ new SoundTriggerMiddlewareImpl(factories,
+ new AudioSessionProviderImpl()),
+ getContext()))));
}
}
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
new file mode 100644
index 0000000..c45f37d
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
@@ -0,0 +1,784 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.soundtrigger_middleware;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.PermissionChecker;
+import android.media.soundtrigger_middleware.ISoundTriggerCallback;
+import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
+import android.media.soundtrigger_middleware.ISoundTriggerModule;
+import android.media.soundtrigger_middleware.ModelParameterRange;
+import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
+import android.media.soundtrigger_middleware.PhraseSoundModel;
+import android.media.soundtrigger_middleware.RecognitionConfig;
+import android.media.soundtrigger_middleware.RecognitionEvent;
+import android.media.soundtrigger_middleware.RecognitionStatus;
+import android.media.soundtrigger_middleware.SoundModel;
+import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
+import android.media.soundtrigger_middleware.Status;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.util.Log;
+
+import com.android.internal.util.Preconditions;
+
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * This is a decorator of an {@link ISoundTriggerMiddlewareService}, which enforces permissions and
+ * correct usage by the client, as well as makes sure that exceptions representing a server
+ * malfunction do not get sent to the client.
+ * <p>
+ * This is intended to extract the non-business logic out of the underlying implementation and thus
+ * make it easier to maintain each one of those separate aspects. A design trade-off is being made
+ * here, in that this class would need to essentially eavesdrop on all the client-server
+ * communication and retain all state known to the client, while the client doesn't necessarily care
+ * about all of it, and while the server has its own representation of this information. However,
+ * in this case, this is a small amount of data, and the benefits in code elegance seem worth it.
+ * There is also some additional cost in employing a simplistic locking mechanism here, but
+ * following the same line of reasoning, the benefits in code simplicity outweigh it.
+ * <p>
+ * Every public method in this class, overriding an interface method, must follow the following
+ * pattern:
+ * <code><pre>
+ * @Override public T method(S arg) {
+ * // Permission check.
+ * checkPermissions();
+ * // Input validation.
+ * ValidationUtil.validateS(arg);
+ * synchronized (this) {
+ * // State validation.
+ * if (...state is not valid for this call...) {
+ * throw new IllegalStateException("State is invalid because...");
+ * }
+ * // From here on, every exception isn't client's fault.
+ * try {
+ * T result = mDelegate.method(arg);
+ * // Update state.;
+ * ...
+ * return result;
+ * } catch (Exception e) {
+ * throw handleException(e);
+ * }
+ * }
+ * }
+ * </pre></code>
+ * Following this patterns ensures a consistent and rigorous handling of all aspects associated
+ * with client-server separation.
+ * <p>
+ * <b>Exception handling approach:</b><br>
+ * We make sure all client faults (permissions, argument and state validation) happen first, and
+ * would throw {@link SecurityException}, {@link IllegalArgumentException}/
+ * {@link NullPointerException} or {@link
+ * IllegalStateException}, respectively. All those exceptions are treated specially by Binder and
+ * will get sent back to the client.<br>
+ * Once this is done, any subsequent fault is considered a server fault. Only {@link
+ * RecoverableException}s thrown by the implementation are special-cased: they would get sent back
+ * to the caller as a {@link ServiceSpecificException}, which is the behavior of Binder. Any other
+ * exception gets wrapped with a {@link InternalServerError}, which is specifically chosen as a type
+ * that <b>does NOT</b> get forwarded by binder. Those exceptions would be handled by a high-level
+ * exception handler on the server side, typically resulting in rebooting the server.
+ *
+ * {@hide}
+ */
+public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddlewareService, Dumpable {
+ private static final String TAG = "SoundTriggerMiddlewareValidation";
+
+ private final @NonNull ISoundTriggerMiddlewareService mDelegate;
+ private final @NonNull Context mContext;
+ private Map<Integer, Set<ModuleService>> mModules;
+
+ public SoundTriggerMiddlewareValidation(
+ @NonNull ISoundTriggerMiddlewareService delegate, @NonNull Context context) {
+ mDelegate = delegate;
+ mContext = context;
+ }
+
+ /**
+ * Generic exception handling for exceptions thrown by the underlying implementation.
+ *
+ * Would throw any {@link RecoverableException} as a {@link ServiceSpecificException} (passed
+ * by Binder to the caller) and <i>any other</i> exception as {@link InternalServerError}
+ * (<b>not</b> passed by Binder to the caller).
+ * <p>
+ * Typical usage:
+ * <code><pre>
+ * try {
+ * ... Do server operations ...
+ * } catch (Exception e) {
+ * throw handleException(e);
+ * }
+ * </pre></code>
+ */
+ static @NonNull
+ RuntimeException handleException(@NonNull Exception e) {
+ if (e instanceof RecoverableException) {
+ throw new ServiceSpecificException(((RecoverableException) e).errorCode,
+ e.getMessage());
+ }
+ throw new InternalServerError(e);
+ }
+
+ @Override
+ public @NonNull
+ SoundTriggerModuleDescriptor[] listModules() {
+ // Permission check.
+ checkPermissions();
+ // Input validation (always valid).
+
+ synchronized (this) {
+ // State validation (always valid).
+
+ // From here on, every exception isn't client's fault.
+ try {
+ SoundTriggerModuleDescriptor[] result = mDelegate.listModules();
+ mModules = new HashMap<>(result.length);
+ for (SoundTriggerModuleDescriptor desc : result) {
+ mModules.put(desc.handle, new HashSet<>());
+ }
+ return result;
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+ }
+
+ @Override
+ public @NonNull ISoundTriggerModule attach(int handle,
+ @NonNull ISoundTriggerCallback callback) {
+ // Permission check.
+ checkPermissions();
+ // Input validation.
+ Objects.requireNonNull(callback);
+ Objects.requireNonNull(callback.asBinder());
+
+ synchronized (this) {
+ // State validation.
+ if (mModules == null) {
+ throw new IllegalStateException(
+ "Client must call listModules() prior to attaching.");
+ }
+ if (!mModules.containsKey(handle)) {
+ throw new IllegalArgumentException("Invalid handle: " + handle);
+ }
+
+ // From here on, every exception isn't client's fault.
+ try {
+ ModuleService moduleService =
+ new ModuleService(handle, callback);
+ moduleService.attach(mDelegate.attach(handle, moduleService));
+ return moduleService;
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+ }
+
+ @Override
+ public void setExternalCaptureState(boolean active) {
+ // Permission check.
+ checkPreemptPermissions();
+ // Input validation (always valid).
+
+ synchronized (this) {
+ // State validation (always valid).
+
+ // From here on, every exception isn't client's fault.
+ try {
+ mDelegate.setExternalCaptureState(active);
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+ }
+
+ // Override toString() in order to have the delegate's ID in it.
+ @Override
+ public String toString() {
+ return mDelegate.toString();
+ }
+
+ /**
+ * Throws a {@link SecurityException} if caller permanently doesn't have the given permission,
+ * or a {@link ServiceSpecificException} with a {@link Status#TEMPORARY_PERMISSION_DENIED} if
+ * caller temporarily doesn't have the right permissions to use this service.
+ */
+ void checkPermissions() {
+ enforcePermission(Manifest.permission.RECORD_AUDIO);
+ enforcePermission(Manifest.permission.CAPTURE_AUDIO_HOTWORD);
+ }
+
+ /**
+ * Throws a {@link SecurityException} if caller permanently doesn't have the given permission,
+ * or a {@link ServiceSpecificException} with a {@link Status#TEMPORARY_PERMISSION_DENIED} if
+ * caller temporarily doesn't have the right permissions to preempt active sound trigger
+ * sessions.
+ */
+ void checkPreemptPermissions() {
+ enforcePermission(Manifest.permission.PREEMPT_SOUND_TRIGGER);
+ }
+
+ /**
+ * Throws a {@link SecurityException} if caller permanently doesn't have the given permission,
+ * or a {@link ServiceSpecificException} with a {@link Status#TEMPORARY_PERMISSION_DENIED} if
+ * caller temporarily doesn't have the given permission.
+ *
+ * @param permission The permission to check.
+ */
+ void enforcePermission(String permission) {
+ final int status = PermissionChecker.checkCallingOrSelfPermissionForPreflight(mContext,
+ permission);
+ switch (status) {
+ case PermissionChecker.PERMISSION_GRANTED:
+ return;
+ case PermissionChecker.PERMISSION_HARD_DENIED:
+ throw new SecurityException(
+ String.format("Caller must have the %s permission.", permission));
+ case PermissionChecker.PERMISSION_SOFT_DENIED:
+ throw new ServiceSpecificException(Status.TEMPORARY_PERMISSION_DENIED,
+ String.format("Caller must have the %s permission.", permission));
+ default:
+ throw new InternalServerError(
+ new RuntimeException("Unexpected perimission check result."));
+ }
+ }
+
+ @Override
+ public IBinder asBinder() {
+ throw new UnsupportedOperationException(
+ "This implementation is not inteded to be used directly with Binder.");
+ }
+
+ @Override public void dump(PrintWriter pw) {
+ synchronized (this) {
+ if (mModules != null) {
+ for (int handle : mModules.keySet()) {
+ pw.println("=========================================");
+ pw.printf("Active sessions for module %d", handle);
+ pw.println();
+ pw.println("=========================================");
+ for (ModuleService session : mModules.get(handle)) {
+ session.dump(pw);
+ }
+ }
+ }
+ }
+ pw.println();
+
+ if (mDelegate instanceof Dumpable) {
+ ((Dumpable) mDelegate).dump(pw);
+ }
+
+ }
+
+ /** State of a sound model. */
+ static class ModelState {
+ /** Activity state of a sound model. */
+ enum Activity {
+ /** Model is loaded, recognition is inactive. */
+ LOADED,
+ /** Model is loaded, recognition is active. */
+ ACTIVE
+ }
+
+ /** Activity state. */
+ Activity activityState = Activity.LOADED;
+
+ /**
+ * A map of known parameter support. A missing key means we don't know yet whether the
+ * parameter is supported. A null value means it is known to not be supported. A non-null
+ * value indicates the valid value range.
+ */
+ private Map<Integer, ModelParameterRange> parameterSupport = new HashMap<>();
+
+ /**
+ * Check that the given parameter is known to be supported for this model.
+ *
+ * @param modelParam The parameter key.
+ */
+ void checkSupported(int modelParam) {
+ if (!parameterSupport.containsKey(modelParam)) {
+ throw new IllegalStateException("Parameter has not been checked for support.");
+ }
+ ModelParameterRange range = parameterSupport.get(modelParam);
+ if (range == null) {
+ throw new IllegalArgumentException("Paramater is not supported.");
+ }
+ }
+
+ /**
+ * Check that the given parameter is known to be supported for this model and that the given
+ * value is a valid value for it.
+ *
+ * @param modelParam The parameter key.
+ * @param value The value.
+ */
+ void checkSupported(int modelParam, int value) {
+ if (!parameterSupport.containsKey(modelParam)) {
+ throw new IllegalStateException("Parameter has not been checked for support.");
+ }
+ ModelParameterRange range = parameterSupport.get(modelParam);
+ if (range == null) {
+ throw new IllegalArgumentException("Paramater is not supported.");
+ }
+ Preconditions.checkArgumentInRange(value, range.minInclusive, range.maxInclusive,
+ "value");
+ }
+
+ /**
+ * Update support state for the given parameter for this model.
+ *
+ * @param modelParam The parameter key.
+ * @param range The parameter value range, or null if not supported.
+ */
+ void updateParameterSupport(int modelParam, @Nullable ModelParameterRange range) {
+ parameterSupport.put(modelParam, range);
+ }
+ }
+
+ /**
+ * A wrapper around an {@link ISoundTriggerModule} implementation, to address the same aspects
+ * mentioned in {@link SoundTriggerModule} above. This class follows the same conventions.
+ */
+ private class ModuleService extends ISoundTriggerModule.Stub implements ISoundTriggerCallback,
+ IBinder.DeathRecipient {
+ private final ISoundTriggerCallback mCallback;
+ private ISoundTriggerModule mDelegate;
+ private @NonNull Map<Integer, ModelState> mLoadedModels = new HashMap<>();
+ private final int mHandle;
+
+ ModuleService(int handle, @NonNull ISoundTriggerCallback callback) {
+ mCallback = callback;
+ mHandle = handle;
+ try {
+ mCallback.asBinder().linkToDeath(null, 0);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ void attach(@NonNull ISoundTriggerModule delegate) {
+ mDelegate = delegate;
+ mModules.get(mHandle).add(this);
+ }
+
+ @Override
+ public int loadModel(@NonNull SoundModel model) {
+ // Permission check.
+ checkPermissions();
+ // Input validation.
+ ValidationUtil.validateGenericModel(model);
+
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ // State validation.
+ if (mDelegate == null) {
+ throw new IllegalStateException("Module has been detached.");
+ }
+
+ // From here on, every exception isn't client's fault.
+ try {
+ int handle = mDelegate.loadModel(model);
+ mLoadedModels.put(handle, new ModelState());
+ return handle;
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+ }
+
+ @Override
+ public int loadPhraseModel(@NonNull PhraseSoundModel model) {
+ // Permission check.
+ checkPermissions();
+ // Input validation.
+ ValidationUtil.validatePhraseModel(model);
+
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ // State validation.
+ if (mDelegate == null) {
+ throw new IllegalStateException("Module has been detached.");
+ }
+
+ // From here on, every exception isn't client's fault.
+ try {
+ int handle = mDelegate.loadPhraseModel(model);
+ mLoadedModels.put(handle, new ModelState());
+ return handle;
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+ }
+
+ @Override
+ public void unloadModel(int modelHandle) {
+ // Permission check.
+ checkPermissions();
+ // Input validation (always valid).
+
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ // State validation.
+ if (mDelegate == null) {
+ throw new IllegalStateException("Module has been detached.");
+ }
+ ModelState modelState = mLoadedModels.get(
+ modelHandle);
+ if (modelState == null) {
+ throw new IllegalStateException("Invalid handle: " + modelHandle);
+ }
+ if (modelState.activityState
+ != ModelState.Activity.LOADED) {
+ throw new IllegalStateException("Model with handle: " + modelHandle
+ + " has invalid state for unloading: " + modelState.activityState);
+ }
+
+ // From here on, every exception isn't client's fault.
+ try {
+ mDelegate.unloadModel(modelHandle);
+ mLoadedModels.remove(modelHandle);
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+ }
+
+ @Override
+ public void startRecognition(int modelHandle, @NonNull RecognitionConfig config) {
+ // Permission check.
+ checkPermissions();
+ // Input validation.
+ ValidationUtil.validateRecognitionConfig(config);
+
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ // State validation.
+ if (mDelegate == null) {
+ throw new IllegalStateException("Module has been detached.");
+ }
+ ModelState modelState = mLoadedModels.get(
+ modelHandle);
+ if (modelState == null) {
+ throw new IllegalStateException("Invalid handle: " + modelHandle);
+ }
+ if (modelState.activityState
+ != ModelState.Activity.LOADED) {
+ throw new IllegalStateException("Model with handle: " + modelHandle
+ + " has invalid state for starting recognition: "
+ + modelState.activityState);
+ }
+
+ // From here on, every exception isn't client's fault.
+ try {
+ mDelegate.startRecognition(modelHandle, config);
+ modelState.activityState =
+ ModelState.Activity.ACTIVE;
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+ }
+
+ @Override
+ public void stopRecognition(int modelHandle) {
+ // Permission check.
+ checkPermissions();
+ // Input validation (always valid).
+
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ // State validation.
+ if (mDelegate == null) {
+ throw new IllegalStateException("Module has been detached.");
+ }
+ ModelState modelState = mLoadedModels.get(
+ modelHandle);
+ if (modelState == null) {
+ throw new IllegalStateException("Invalid handle: " + modelHandle);
+ }
+ // stopRecognition is idempotent - no need to check model state.
+
+ // From here on, every exception isn't client's fault.
+ try {
+ mDelegate.stopRecognition(modelHandle);
+ modelState.activityState =
+ ModelState.Activity.LOADED;
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+ }
+
+ @Override
+ public void forceRecognitionEvent(int modelHandle) {
+ // Permission check.
+ checkPermissions();
+ // Input validation (always valid).
+
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ // State validation.
+ if (mDelegate == null) {
+ throw new IllegalStateException("Module has been detached.");
+ }
+ ModelState modelState = mLoadedModels.get(
+ modelHandle);
+ if (modelState == null) {
+ throw new IllegalStateException("Invalid handle: " + modelHandle);
+ }
+ // forceRecognitionEvent is idempotent - no need to check model state.
+
+ // From here on, every exception isn't client's fault.
+ try {
+ mDelegate.forceRecognitionEvent(modelHandle);
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+ }
+
+ @Override
+ public void setModelParameter(int modelHandle, int modelParam, int value) {
+ // Permission check.
+ checkPermissions();
+ // Input validation.
+ ValidationUtil.validateModelParameter(modelParam);
+
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ // State validation.
+ if (mDelegate == null) {
+ throw new IllegalStateException("Module has been detached.");
+ }
+ ModelState modelState = mLoadedModels.get(
+ modelHandle);
+ if (modelState == null) {
+ throw new IllegalStateException("Invalid handle: " + modelHandle);
+ }
+ modelState.checkSupported(modelParam, value);
+
+ // From here on, every exception isn't client's fault.
+ try {
+ mDelegate.setModelParameter(modelHandle, modelParam, value);
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+ }
+
+ @Override
+ public int getModelParameter(int modelHandle, int modelParam) {
+ // Permission check.
+ checkPermissions();
+ // Input validation.
+ ValidationUtil.validateModelParameter(modelParam);
+
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ // State validation.
+ if (mDelegate == null) {
+ throw new IllegalStateException("Module has been detached.");
+ }
+ ModelState modelState = mLoadedModels.get(
+ modelHandle);
+ if (modelState == null) {
+ throw new IllegalStateException("Invalid handle: " + modelHandle);
+ }
+ modelState.checkSupported(modelParam);
+
+ // From here on, every exception isn't client's fault.
+ try {
+ return mDelegate.getModelParameter(modelHandle, modelParam);
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+ }
+
+ @Override
+ @Nullable
+ public ModelParameterRange queryModelParameterSupport(int modelHandle, int modelParam) {
+ // Permission check.
+ checkPermissions();
+ // Input validation.
+ ValidationUtil.validateModelParameter(modelParam);
+
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ // State validation.
+ if (mDelegate == null) {
+ throw new IllegalStateException("Module has been detached.");
+ }
+ ModelState modelState = mLoadedModels.get(
+ modelHandle);
+ if (modelState == null) {
+ throw new IllegalStateException("Invalid handle: " + modelHandle);
+ }
+
+ // From here on, every exception isn't client's fault.
+ try {
+ ModelParameterRange result = mDelegate.queryModelParameterSupport(modelHandle,
+ modelParam);
+ modelState.updateParameterSupport(modelParam, result);
+ return result;
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+ }
+
+ @Override
+ public void detach() {
+ // Permission check.
+ checkPermissions();
+ // Input validation (always valid).
+
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ // State validation.
+ if (mDelegate == null) {
+ throw new IllegalStateException("Module has already been detached.");
+ }
+ if (!mLoadedModels.isEmpty()) {
+ throw new IllegalStateException("Cannot detach while models are loaded.");
+ }
+
+ // From here on, every exception isn't client's fault.
+ try {
+ detachInternal();
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+ }
+
+ // Override toString() in order to have the delegate's ID in it.
+ @Override
+ public String toString() {
+ return mDelegate.toString();
+ }
+
+ private void detachInternal() {
+ try {
+ mDelegate.detach();
+ mDelegate = null;
+ mCallback.asBinder().unlinkToDeath(null, 0);
+ mModules.get(mHandle).remove(this);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ void dump(PrintWriter pw) {
+ pw.printf("Loaded models for session %s (handle, active)", toString());
+ pw.println();
+ pw.println("-------------------------------");
+ for (Map.Entry<Integer, ModelState> entry : mLoadedModels.entrySet()) {
+ pw.print(entry.getKey());
+ pw.print('\t');
+ pw.print(entry.getValue().activityState.name());
+ pw.println();
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ // Callbacks
+
+ @Override
+ public void onRecognition(int modelHandle, @NonNull RecognitionEvent event) {
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ if (event.status != RecognitionStatus.FORCED) {
+ mLoadedModels.get(modelHandle).activityState =
+ ModelState.Activity.LOADED;
+ }
+ try {
+ mCallback.onRecognition(modelHandle, event);
+ } catch (RemoteException e) {
+ // Dead client will be handled by binderDied() - no need to handle here.
+ // In any case, client callbacks are considered best effort.
+ Log.e(TAG, "Client callback exception.", e);
+ }
+ }
+ }
+
+ @Override
+ public void onPhraseRecognition(int modelHandle, @NonNull PhraseRecognitionEvent event) {
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ if (event.common.status != RecognitionStatus.FORCED) {
+ mLoadedModels.get(modelHandle).activityState =
+ ModelState.Activity.LOADED;
+ }
+ try {
+ mCallback.onPhraseRecognition(modelHandle, event);
+ } catch (RemoteException e) {
+ // Dead client will be handled by binderDied() - no need to handle here.
+ // In any case, client callbacks are considered best effort.
+ Log.e(TAG, "Client callback exception.", e);
+ }
+ }
+ }
+
+ @Override
+ public void onRecognitionAvailabilityChange(boolean available) {
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ try {
+ mCallback.onRecognitionAvailabilityChange(available);
+ } catch (RemoteException e) {
+ // Dead client will be handled by binderDied() - no need to handle here.
+ // In any case, client callbacks are considered best effort.
+ Log.e(TAG, "Client callback exception.", e);
+ }
+ }
+ }
+
+ @Override
+ public void onModuleDied() {
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ try {
+ mCallback.onModuleDied();
+ } catch (RemoteException e) {
+ // Dead client will be handled by binderDied() - no need to handle here.
+ // In any case, client callbacks are considered best effort.
+ Log.e(TAG, "Client callback exception.", e);
+ }
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ // This is called whenever our client process dies.
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ try {
+ // Gracefully stop all active recognitions and unload the models.
+ for (Map.Entry<Integer, ModelState> entry :
+ mLoadedModels.entrySet()) {
+ if (entry.getValue().activityState
+ == ModelState.Activity.ACTIVE) {
+ mDelegate.stopRecognition(entry.getKey());
+ }
+ mDelegate.unloadModel(entry.getKey());
+ }
+ // Detach.
+ detachInternal();
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
index aa1558e..d6390184 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
@@ -123,7 +123,6 @@
*/
synchronized @NonNull
ISoundTriggerModule attach(@NonNull ISoundTriggerCallback callback) {
- Log.d(TAG, "attach()");
Session session = new Session(callback);
mActiveSessions.add(session);
return session;
@@ -149,8 +148,6 @@
* @param active true iff external capture is active.
*/
synchronized void setExternalCaptureState(boolean active) {
- Log.d(TAG, String.format("setExternalCaptureState(active=%b)", active));
-
if (mProperties.concurrentCapture) {
// If we support concurrent capture, we don't care about any of this.
return;
@@ -235,7 +232,6 @@
@Override
public void detach() {
- Log.d(TAG, "detach()");
synchronized (SoundTriggerModule.this) {
if (mCallback == null) {
return;
@@ -247,8 +243,6 @@
@Override
public int loadModel(@NonNull SoundModel model) {
- Log.d(TAG, String.format("loadModel(model=%s)", model));
-
// We must do this outside the lock, to avoid possible deadlocks with the remote process
// that provides the audio sessions, which may also be calling into us.
SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession =
@@ -276,8 +270,6 @@
@Override
public int loadPhraseModel(@NonNull PhraseSoundModel model) {
- Log.d(TAG, String.format("loadPhraseModel(model=%s)", model));
-
// We must do this outside the lock, to avoid possible deadlocks with the remote process
// that provides the audio sessions, which may also be calling into us.
SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession =
@@ -306,10 +298,7 @@
@Override
public void unloadModel(int modelHandle) {
- Log.d(TAG, String.format("unloadModel(handle=%d)", modelHandle));
-
int sessionId;
-
synchronized (SoundTriggerModule.this) {
checkValid();
sessionId = mLoadedModels.get(modelHandle).unload();
@@ -323,8 +312,6 @@
@Override
public void startRecognition(int modelHandle, @NonNull RecognitionConfig config) {
- Log.d(TAG,
- String.format("startRecognition(handle=%d, config=%s)", modelHandle, config));
synchronized (SoundTriggerModule.this) {
checkValid();
mLoadedModels.get(modelHandle).startRecognition(config);
@@ -333,7 +320,6 @@
@Override
public void stopRecognition(int modelHandle) {
- Log.d(TAG, String.format("stopRecognition(handle=%d)", modelHandle));
synchronized (SoundTriggerModule.this) {
mLoadedModels.get(modelHandle).stopRecognition();
}
@@ -341,7 +327,6 @@
@Override
public void forceRecognitionEvent(int modelHandle) {
- Log.d(TAG, String.format("forceRecognitionEvent(handle=%d)", modelHandle));
synchronized (SoundTriggerModule.this) {
checkValid();
mLoadedModels.get(modelHandle).forceRecognitionEvent();
@@ -350,9 +335,6 @@
@Override
public void setModelParameter(int modelHandle, int modelParam, int value) {
- Log.d(TAG,
- String.format("setModelParameter(handle=%d, param=%d, value=%d)", modelHandle,
- modelParam, value));
synchronized (SoundTriggerModule.this) {
checkValid();
mLoadedModels.get(modelHandle).setParameter(modelParam, value);
@@ -361,8 +343,6 @@
@Override
public int getModelParameter(int modelHandle, int modelParam) {
- Log.d(TAG, String.format("getModelParameter(handle=%d, param=%d)", modelHandle,
- modelParam));
synchronized (SoundTriggerModule.this) {
checkValid();
return mLoadedModels.get(modelHandle).getParameter(modelParam);
@@ -372,8 +352,6 @@
@Override
@Nullable
public ModelParameterRange queryModelParameterSupport(int modelHandle, int modelParam) {
- Log.d(TAG, String.format("queryModelParameterSupport(handle=%d, param=%d)", modelHandle,
- modelParam));
synchronized (SoundTriggerModule.this) {
checkValid();
return mLoadedModels.get(modelHandle).queryModelParameterSupport(modelParam);
@@ -584,8 +562,6 @@
public void recognitionCallback(
@NonNull ISoundTriggerHwCallback.RecognitionEvent recognitionEvent,
int cookie) {
- Log.d(TAG, String.format("recognitionCallback_2_1(event=%s, cookie=%d)",
- recognitionEvent, cookie));
synchronized (SoundTriggerModule.this) {
android.media.soundtrigger_middleware.RecognitionEvent aidlEvent =
ConversionUtil.hidl2aidlRecognitionEvent(recognitionEvent);
@@ -608,8 +584,6 @@
public void phraseRecognitionCallback(
@NonNull ISoundTriggerHwCallback.PhraseRecognitionEvent phraseRecognitionEvent,
int cookie) {
- Log.d(TAG, String.format("phraseRecognitionCallback_2_1(event=%s, cookie=%d)",
- phraseRecognitionEvent, cookie));
synchronized (SoundTriggerModule.this) {
android.media.soundtrigger_middleware.PhraseRecognitionEvent aidlEvent =
ConversionUtil.hidl2aidlPhraseRecognitionEvent(phraseRecognitionEvent);
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 3f8f6bf..e426574 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -436,8 +436,7 @@
mStoragedUidIoStatsReader = new StoragedUidIoStatsReader();
// Initialize PROC_STATS
- // TODO (b/148402814): Change this directory to stats_pull.
- mBaseDir = new File(SystemServiceManager.ensureSystemDir(), "stats_companion");
+ mBaseDir = new File(SystemServiceManager.ensureSystemDir(), "stats_pull");
// Disables throttler on CPU time readers.
mCpuUidUserSysTimeReader = new KernelCpuUidUserSysTimeReader(false);
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 5f5cd3c..2394baf 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -1437,7 +1437,8 @@
if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action)) {
refreshAgentList(getSendingUserId());
updateDevicePolicyFeatures();
- } else if (Intent.ACTION_USER_ADDED.equals(action)) {
+ } else if (Intent.ACTION_USER_ADDED.equals(action) || Intent.ACTION_USER_STARTED.equals(
+ action)) {
int userId = getUserId(intent);
if (userId > 0) {
maybeEnableFactoryTrustAgents(mLockPatternUtils, userId);
@@ -1478,6 +1479,7 @@
filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
filter.addAction(Intent.ACTION_USER_ADDED);
filter.addAction(Intent.ACTION_USER_REMOVED);
+ filter.addAction(Intent.ACTION_USER_STARTED);
context.registerReceiverAsUser(this,
UserHandle.ALL,
filter,
diff --git a/services/core/java/com/android/server/utils/quota/QuotaTracker.java b/services/core/java/com/android/server/utils/quota/QuotaTracker.java
index a8cf9f6..115b5c8 100644
--- a/services/core/java/com/android/server/utils/quota/QuotaTracker.java
+++ b/services/core/java/com/android/server/utils/quota/QuotaTracker.java
@@ -552,6 +552,7 @@
mTriggerTimeElapsed = nextTriggerTimeElapsed;
}
} else {
+ cancelAlarm(this);
mTriggerTimeElapsed = 0;
}
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 2115f7c..6eb3c0f 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1173,6 +1173,10 @@
}
};
+ private Runnable mTryToRebindRunnable = () -> {
+ tryToRebind();
+ };
+
WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper, int clientUid) {
mInfo = info;
mWallpaper = wallpaper;
@@ -1279,7 +1283,7 @@
saveSettingsLocked(mWallpaper.userId);
}
FgThread.getHandler().removeCallbacks(mResetRunnable);
- mContext.getMainThreadHandler().removeCallbacks(this::tryToRebind);
+ mContext.getMainThreadHandler().removeCallbacks(mTryToRebindRunnable);
}
}
}
@@ -1337,7 +1341,7 @@
< WALLPAPER_RECONNECT_TIMEOUT_MS) {
// Bind fail without timeout, schedule rebind
Slog.w(TAG, "Rebind fail! Try again later");
- mContext.getMainThreadHandler().postDelayed(this::tryToRebind, 1000);
+ mContext.getMainThreadHandler().postDelayed(mTryToRebindRunnable, 1000);
} else {
// Timeout
Slog.w(TAG, "Reverting to built-in wallpaper!");
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 68a7188..4fea36c 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -275,8 +275,9 @@
}
/** @return {@code true} if the activity matches a launched activity in this transition. */
- boolean contains(ActivityRecord r) {
- return r == mLastLaunchedActivity || mPendingDrawActivities.contains(r);
+ boolean contains(WindowContainer wc) {
+ final ActivityRecord r = AppTransitionController.getAppFromContainer(wc);
+ return r != null && (r == mLastLaunchedActivity || mPendingDrawActivities.contains(r));
}
/** Called when the activity is drawn or won't be drawn. */
@@ -435,10 +436,10 @@
/** @return Non-null {@link TransitionInfo} if the activity is found in an active transition. */
@Nullable
- private TransitionInfo getActiveTransitionInfo(ActivityRecord r) {
+ private TransitionInfo getActiveTransitionInfo(WindowContainer wc) {
for (int i = mTransitionInfoList.size() - 1; i >= 0; i--) {
final TransitionInfo info = mTransitionInfoList.get(i);
- if (info.contains(r)) {
+ if (info.contains(wc)) {
return info;
}
}
@@ -623,19 +624,19 @@
* @param activityToReason A map from activity to a reason integer, which must be on of
* ActivityTaskManagerInternal.APP_TRANSITION_* reasons.
*/
- void notifyTransitionStarting(ArrayMap<ActivityRecord, Integer> activityToReason) {
+ void notifyTransitionStarting(ArrayMap<WindowContainer, Integer> activityToReason) {
if (DEBUG_METRICS) Slog.i(TAG, "notifyTransitionStarting");
final long timestampNs = SystemClock.elapsedRealtimeNanos();
for (int index = activityToReason.size() - 1; index >= 0; index--) {
- final ActivityRecord r = activityToReason.keyAt(index);
- final TransitionInfo info = getActiveTransitionInfo(r);
+ final WindowContainer wc = activityToReason.keyAt(index);
+ final TransitionInfo info = getActiveTransitionInfo(wc);
if (info == null || info.mLoggedTransitionStarting) {
// Ignore any subsequent notifyTransitionStarting.
continue;
}
if (DEBUG_METRICS) {
- Slog.i(TAG, "notifyTransitionStarting activity=" + r + " info=" + info);
+ Slog.i(TAG, "notifyTransitionStarting activity=" + wc + " info=" + info);
}
info.mCurrentTransitionDelayMs = info.calculateDelay(timestampNs);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index d715ed4..76bc366 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -40,7 +40,6 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.activityTypeToString;
@@ -106,7 +105,6 @@
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
-import static android.view.WindowManager.TRANSIT_TASK_CHANGE_WINDOWING_MODE;
import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
import static android.view.WindowManager.TRANSIT_TASK_OPEN_BEHIND;
import static android.view.WindowManager.TRANSIT_UNSET;
@@ -284,7 +282,6 @@
import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.IApplicationToken;
import android.view.InputApplicationHandle;
-import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
@@ -577,12 +574,6 @@
*/
private boolean mCurrentLaunchCanTurnScreenOn = true;
- /**
- * This leash is used to "freeze" the app surface in place after the state change, but before
- * the animation is ready to start.
- */
- private SurfaceControl mTransitChangeLeash = null;
-
/** Whether our surface was set to be showing in the last call to {@link #prepareSurfaces} */
private boolean mLastSurfaceShowing = true;
@@ -1329,15 +1320,6 @@
mDisplayContent.executeAppTransition();
}
- if (prevDc.mChangingApps.remove(this)) {
- // This gets called *after* the ActivityRecord has been reparented to the new display.
- // That reparenting resulted in this window changing modes (eg. FREEFORM -> FULLSCREEN),
- // so this token is now "frozen" while waiting for the animation to start on prevDc
- // (which will be cancelled since the window is no-longer a child). However, since this
- // is no longer a child of prevDc, this won't be notified of the cancelled animation,
- // so we need to cancel the change transition here.
- clearChangeLeash(getPendingTransaction(), true /* cancel */);
- }
prevDc.mClosingApps.remove(this);
if (prevDc.mFocusedApp == this) {
@@ -3092,7 +3074,7 @@
commitVisibility(false /* visible */, true /* performLayout */);
getDisplayContent().mOpeningApps.remove(this);
- getDisplayContent().mChangingApps.remove(this);
+ getDisplayContent().mChangingContainers.remove(this);
getDisplayContent().mUnknownAppVisibilityController.appRemovedOrHidden(this);
mWmService.mTaskSnapshotController.onAppRemoved(this);
mStackSupervisor.getActivityMetricsLogger().notifyActivityRemoved(this);
@@ -3995,13 +3977,11 @@
appToken, visible, appTransition, isVisible(), mVisibleRequested,
Debug.getCallers(6));
+ onChildVisibilityRequested(visible);
+
final DisplayContent displayContent = getDisplayContent();
displayContent.mOpeningApps.remove(this);
displayContent.mClosingApps.remove(this);
- if (isInChangeTransition()) {
- clearChangeLeash(getPendingTransaction(), true /* cancel */);
- }
- displayContent.mChangingApps.remove(this);
waitingToShow = false;
mVisibleRequested = visible;
mLastDeferHidingClient = deferHidingClient;
@@ -4198,13 +4178,6 @@
final DisplayContent displayContent = getDisplayContent();
if (!displayContent.mClosingApps.contains(this)
&& !displayContent.mOpeningApps.contains(this)) {
- // The token is not closing nor opening, so even if there is an animation set, that
- // doesn't mean that it goes through the normal app transition cycle so we have
- // to inform the docked controller about visibility change.
- // TODO(multi-display): notify docked divider on all displays where visibility was
- // affected.
- displayContent.getDockedDividerController().notifyAppVisibilityChanged();
-
// Take the screenshot before possibly hiding the WSA, otherwise the screenshot
// will not be taken.
mWmService.mTaskSnapshotController.notifyAppVisibilityChanged(this, visible);
@@ -5805,11 +5778,6 @@
return !isSplitScreenPrimary || allowSplitScreenPrimaryAnimation;
}
- @Override
- boolean isChangingAppTransition() {
- return task != null ? task.isChangingAppTransition() : super.isChangingAppTransition();
- }
-
/**
* Creates a layer to apply crop to an animation.
*/
@@ -5830,84 +5798,19 @@
this, endDeferFinishCallback);
}
- private boolean shouldStartChangeTransition(int prevWinMode, int newWinMode) {
- if (mWmService.mDisableTransitionAnimation
- || !isVisible()
- || getDisplayContent().mAppTransition.isTransitionSet()
- || getSurfaceControl() == null) {
- return false;
- }
- // Only do an animation into and out-of freeform mode for now. Other mode
- // transition animations are currently handled by system-ui.
- return (prevWinMode == WINDOWING_MODE_FREEFORM) != (newWinMode == WINDOWING_MODE_FREEFORM);
- }
-
@Override
boolean isWaitingForTransitionStart() {
final DisplayContent dc = getDisplayContent();
return dc != null && dc.mAppTransition.isTransitionSet()
&& (dc.mOpeningApps.contains(this)
|| dc.mClosingApps.contains(this)
- || dc.mChangingApps.contains(this));
+ || dc.mChangingContainers.contains(this));
}
- /**
- * Initializes a change transition. Because the app is visible already, there is a small period
- * of time where the user can see the app content/window update before the transition starts.
- * To prevent this, we immediately take a snapshot and place the app/snapshot into a leash which
- * "freezes" the location/crop until the transition starts.
- * <p>
- * Here's a walk-through of the process:
- * 1. Create a temporary leash ("interim-change-leash") and reparent the app to it.
- * 2. Set the temporary leash's position/crop to the current state.
- * 3. Create a snapshot and place that at the top of the leash to cover up content changes.
- * 4. Once the transition is ready, it will reparent the app to the animation leash.
- * 5. Detach the interim-change-leash.
- */
- private void initializeChangeTransition(Rect startBounds) {
- mDisplayContent.prepareAppTransition(TRANSIT_TASK_CHANGE_WINDOWING_MODE,
- false /* alwaysKeepCurrent */, 0, false /* forceOverride */);
- mDisplayContent.mChangingApps.add(this);
- mTransitStartRect.set(startBounds);
-
- final SurfaceControl.Builder builder = makeAnimationLeash()
- .setParent(getAnimationLeashParent())
- .setName(getSurfaceControl() + " - interim-change-leash");
- mTransitChangeLeash = builder.build();
- Transaction t = getPendingTransaction();
- t.setWindowCrop(mTransitChangeLeash, startBounds.width(), startBounds.height());
- t.setPosition(mTransitChangeLeash, startBounds.left, startBounds.top);
- t.show(mTransitChangeLeash);
- t.reparent(getSurfaceControl(), mTransitChangeLeash);
- onAnimationLeashCreated(t, mTransitChangeLeash);
-
- // Skip creating snapshot if this transition is controlled by a remote animator which
- // doesn't need it.
- ArraySet<Integer> activityTypes = new ArraySet<>();
- activityTypes.add(getActivityType());
- RemoteAnimationAdapter adapter =
- mDisplayContent.mAppTransitionController.getRemoteAnimationOverride(
- this, TRANSIT_TASK_CHANGE_WINDOWING_MODE, activityTypes);
- if (adapter != null && !adapter.getChangeNeedsSnapshot()) {
- return;
- }
-
- if (mThumbnail == null && task != null && !hasCommittedReparentToAnimationLeash()) {
- SurfaceControl.ScreenshotGraphicBuffer snapshot =
- mWmService.mTaskSnapshotController.createTaskSnapshot(
- task, 1 /* scaleFraction */);
- if (snapshot != null) {
- mThumbnail = new WindowContainerThumbnail(mWmService.mSurfaceFactory, t, this,
- snapshot.getGraphicBuffer(), true /* relative */);
- }
- }
- }
-
- @Override
- public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
+ private int getAnimationLayer() {
// The leash is parented to the animation layer. We need to preserve the z-order by using
// the prefix order index, but we boost if necessary.
- int layer = 0;
+ int layer;
if (!inPinnedWindowingMode()) {
layer = getPrefixOrderIndex();
} else {
@@ -5920,21 +5823,17 @@
if (mNeedsZBoost) {
layer += Z_BOOST_BASE;
}
- if (!mNeedsAnimationBoundsLayer) {
- t.setLayer(leash, layer);
- }
+ return layer;
+ }
- final DisplayContent dc = getDisplayContent();
- dc.assignStackOrdering();
+ @Override
+ public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
+ t.setLayer(leash, getAnimationLayer());
+ getDisplayContent().assignStackOrdering();
+ }
- if (leash == mTransitChangeLeash) {
- // This is a temporary state so skip any animation notifications
- return;
- } else if (mTransitChangeLeash != null) {
- // unparent mTransitChangeLeash for clean-up
- clearChangeLeash(t, false /* cancel */);
- }
-
+ @Override
+ public void onLeashAnimationStarting(Transaction t, SurfaceControl leash) {
if (mAnimatingActivityRegistry != null) {
mAnimatingActivityRegistry.notifyStarting(this);
}
@@ -5962,7 +5861,8 @@
// surface size has already same as the animating container.
t.setWindowCrop(mAnimationBoundsLayer, mTmpRect);
}
- t.setLayer(mAnimationBoundsLayer, layer);
+ t.setLayer(leash, 0);
+ t.setLayer(mAnimationBoundsLayer, getAnimationLayer());
// Reparent leash to animation bounds layer.
t.reparent(leash, mAnimationBoundsLayer);
@@ -5994,10 +5894,6 @@
return mLastSurfaceShowing;
}
- boolean isInChangeTransition() {
- return mTransitChangeLeash != null || AppTransition.isChangeTransit(mTransit);
- }
-
void attachThumbnailAnimation() {
if (!isAnimating(PARENTS)) {
return;
@@ -6134,31 +6030,6 @@
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
- /**
- * @param cancel {@code true} if clearing the leash due to cancelling instead of transferring
- * to another leash.
- */
- private void clearChangeLeash(Transaction t, boolean cancel) {
- if (mTransitChangeLeash == null) {
- return;
- }
- if (cancel) {
- clearThumbnail();
- SurfaceControl sc = getSurfaceControl();
- SurfaceControl parentSc = getParentSurfaceControl();
- // Don't reparent if surface is getting destroyed
- if (parentSc != null && sc != null) {
- t.reparent(sc, getParentSurfaceControl());
- }
- }
- t.hide(mTransitChangeLeash);
- t.remove(mTransitChangeLeash);
- mTransitChangeLeash = null;
- if (cancel) {
- onAnimationLeashLost(t);
- }
- }
-
void clearAnimatingFlags() {
boolean wallpaperMightChange = false;
for (int i = mChildren.size() - 1; i >= 0; i--) {
@@ -6174,7 +6045,7 @@
void cancelAnimation() {
cancelAnimationOnly();
clearThumbnail();
- clearChangeLeash(getPendingTransaction(), true /* cancel */);
+ mSurfaceFreezer.unfreeze(getPendingTransaction());
}
/**
@@ -6219,6 +6090,7 @@
mRemoteAnimationDefinition = null;
}
+ @Override
RemoteAnimationDefinition getRemoteAnimationDefinition() {
return mRemoteAnimationDefinition;
}
@@ -6679,8 +6551,6 @@
return;
}
}
- final int prevWinMode = getWindowingMode();
- mTmpPrevBounds.set(getBounds());
super.onConfigurationChanged(newParentConfig);
if (shouldUseSizeCompatMode()) {
@@ -6705,12 +6575,6 @@
}
}
- final int newWinMode = getWindowingMode();
- if ((prevWinMode != newWinMode) && (mDisplayContent != null)
- && shouldStartChangeTransition(prevWinMode, newWinMode)) {
- initializeChangeTransition(mTmpPrevBounds);
- }
-
// Configuration's equality doesn't consider seq so if only seq number changes in resolved
// override configuration. Therefore ConfigurationContainer doesn't change merged override
// configuration, but it's used to push configuration changes so explicitly update that.
@@ -7595,6 +7459,7 @@
}
}
+ @Override
void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(HASH_CODE, System.identityHashCode(this));
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index ff890ff..598389b 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -17,8 +17,6 @@
package com.android.server.wm;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
@@ -39,16 +37,10 @@
import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
import static android.view.Display.INVALID_DISPLAY;
-import static android.view.WindowManager.DOCKED_BOTTOM;
-import static android.view.WindowManager.DOCKED_INVALID;
-import static android.view.WindowManager.DOCKED_LEFT;
-import static android.view.WindowManager.DOCKED_RIGHT;
-import static android.view.WindowManager.DOCKED_TOP;
import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
@@ -98,13 +90,8 @@
import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_ACTIVITY_STACK_MSG;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
-import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
import static com.android.server.wm.TaskProto.ACTIVITIES;
import static com.android.server.wm.TaskProto.ACTIVITY_TYPE;
-import static com.android.server.wm.TaskProto.ADJUSTED_BOUNDS;
-import static com.android.server.wm.TaskProto.ADJUSTED_FOR_IME;
-import static com.android.server.wm.TaskProto.ADJUST_DIVIDER_AMOUNT;
-import static com.android.server.wm.TaskProto.ADJUST_IME_AMOUNT;
import static com.android.server.wm.TaskProto.ANIMATING_BOUNDS;
import static com.android.server.wm.TaskProto.BOUNDS;
import static com.android.server.wm.TaskProto.CREATED_BY_ORGANIZER;
@@ -113,7 +100,6 @@
import static com.android.server.wm.TaskProto.DISPLAY_ID;
import static com.android.server.wm.TaskProto.FILLS_PARENT;
import static com.android.server.wm.TaskProto.LAST_NON_FULLSCREEN_BOUNDS;
-import static com.android.server.wm.TaskProto.MINIMIZE_AMOUNT;
import static com.android.server.wm.TaskProto.MIN_HEIGHT;
import static com.android.server.wm.TaskProto.MIN_WIDTH;
import static com.android.server.wm.TaskProto.ORIG_ACTIVITY;
@@ -168,7 +154,6 @@
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
-import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.ITaskOrganizer;
import android.view.SurfaceControl;
@@ -177,8 +162,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.os.logging.MetricsLoggerWrapper;
-import com.android.internal.policy.DividerSnapAlgorithm;
-import com.android.internal.policy.DockedDividerUtils;
import com.android.internal.util.function.pooled.PooledConsumer;
import com.android.internal.util.function.pooled.PooledFunction;
import com.android.internal.util.function.pooled.PooledLambda;
@@ -238,13 +221,6 @@
/** Stack is completely invisible. */
static final int STACK_VISIBILITY_INVISIBLE = 2;
- /** Minimum size of an adjusted stack bounds relative to original stack bounds. Used to
- * restrict IME adjustment so that a min portion of top stack remains visible.*/
- private static final float ADJUSTED_STACK_FRACTION_MIN = 0.3f;
-
- /** Dimming amount for non-focused stack when stacks are IME-adjusted. */
- private static final float IME_ADJUST_DIM_AMOUNT = 0.25f;
-
enum ActivityState {
INITIALIZING,
STARTED,
@@ -294,28 +270,10 @@
/** For Pinned stack controlling. */
private Rect mTmpToBounds = new Rect();
- /** Stack bounds adjusted to screen content area (taking into account IM windows, etc.) */
- private final Rect mAdjustedBounds = new Rect();
-
- /**
- * Fully adjusted IME bounds. These are different from {@link #mAdjustedBounds} because they
- * represent the state when the animation has ended.
- */
- private final Rect mFullyAdjustedImeBounds = new Rect();
-
/** Detach this stack from its display when animation completes. */
// TODO: maybe tie this to WindowContainer#removeChild some how...
private boolean mDeferRemoval;
- private final Rect mTmpAdjustedBounds = new Rect();
- private boolean mAdjustedForIme;
- private boolean mImeGoingAway;
- private WindowState mImeWin;
- private float mMinimizeAmount;
- private float mAdjustImeAmount;
- private float mAdjustDividerAmount;
- private final int mDockedStackMinimizeThickness;
-
// If this is true, we are in the bounds animating mode. The task will be down or upscaled to
// perfectly fit the region it would have been cropped to. We may also avoid certain logic we
// would otherwise apply while resizing, while resizing in the bounds animating mode.
@@ -643,8 +601,6 @@
_realActivitySuspended, userSetupComplete, minWidth, minHeight, info, _voiceSession,
_voiceInteractor, stack);
- mDockedStackMinimizeThickness = mWmService.mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.docked_stack_minimize_thickness);
EventLogTags.writeWmStackCreated(id);
mHandler = new ActivityStackHandler(mStackSupervisor.mLooper);
mCurrentUser = mAtmService.mAmInternal.getCurrentUserId();
@@ -1202,8 +1158,8 @@
@Override
boolean isFocusable() {
- return super.isFocusable() && !(inSplitScreenPrimaryWindowingMode()
- && mRootWindowContainer.mIsDockMinimized);
+ // Special check for tile which isn't really in the hierarchy
+ return mTile != null ? mTile.isFocusable() : super.isFocusable();
}
boolean isTopActivityFocusable() {
@@ -3497,43 +3453,6 @@
forAllLeafTasks(Task::prepareFreezingBounds, true /* traverseTopToBottom */);
}
- /**
- * Overrides the adjusted bounds, i.e. sets temporary layout bounds which are different from
- * the normal task bounds.
- *
- * @param bounds The adjusted bounds.
- */
- private void setAdjustedBounds(Rect bounds) {
- if (mAdjustedBounds.equals(bounds) && !isAnimatingForIme()) {
- return;
- }
-
- mAdjustedBounds.set(bounds);
- final boolean adjusted = !mAdjustedBounds.isEmpty();
- Rect insetBounds = null;
- if (adjusted && isAdjustedForMinimizedDockedStack()) {
- insetBounds = getRawBounds();
- } else if (adjusted && mAdjustedForIme) {
- if (mImeGoingAway) {
- insetBounds = getRawBounds();
- } else {
- insetBounds = mFullyAdjustedImeBounds;
- }
- }
-
- if (!matchParentBounds()) {
- final boolean alignBottom = mAdjustedForIme && getDockSide() == DOCKED_TOP;
- final PooledConsumer c = PooledLambda.obtainConsumer(Task::alignToAdjustedBounds,
- PooledLambda.__(Task.class), adjusted ? mAdjustedBounds : getRawBounds(),
- insetBounds, alignBottom);
- forAllLeafTasks(c, true /* traverseTopToBottom */);
- c.recycle();
- }
-
- mDisplayContent.setLayoutNeeded();
- updateSurfaceBounds();
- }
-
@Override
public int setBounds(Rect bounds) {
// Calling Task#setBounds() for leaf task since this is the a specialization of
@@ -3552,8 +3471,6 @@
final int result = super.setBounds(!inMultiWindowMode() ? null : bounds);
- updateAdjustedBounds();
-
updateSurfaceBounds();
return result;
}
@@ -3575,19 +3492,6 @@
bounds.set(getBounds());
}
- @Override
- public Rect getBounds() {
- // If we're currently adjusting for IME or minimized docked stack, we use the adjusted
- // bounds; otherwise, no need to adjust the output bounds if fullscreen or the docked
- // stack is visible since it is already what we want to represent to the rest of the
- // system.
- if (!mAdjustedBounds.isEmpty()) {
- return mAdjustedBounds;
- } else {
- return super.getBounds();
- }
- }
-
/**
* @return the final bounds for the bounds animation.
*/
@@ -3620,113 +3524,6 @@
}
/**
- * Updates the passed-in {@code inOutBounds} based on the current state of the
- * docked controller. This gets run *after* the override configuration is updated, so it's
- * safe to rely on the controller's state in here (though eventually this dependence should
- * be removed).
- *
- * This does NOT modify this TaskStack's configuration. However, it does, for the time-being,
- * update docked controller state.
- *
- * @param parentConfig the parent configuration for reference.
- * @param inOutBounds the bounds to update (both input and output).
- */
- void calculateDockedBoundsForConfigChange(Configuration parentConfig, Rect inOutBounds) {
- final boolean primary =
- getRequestedOverrideWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
- repositionSplitScreenStackAfterRotation(parentConfig, primary, inOutBounds);
- final DisplayCutout cutout = mDisplayContent.getDisplayInfo().displayCutout;
- snapDockedStackAfterRotation(parentConfig, cutout, inOutBounds);
- if (primary) {
- final int newDockSide = getDockSide(parentConfig, inOutBounds);
- // Update the dock create mode and clear the dock create bounds, these
- // might change after a rotation and the original values will be invalid.
- mWmService.setDockedStackCreateStateLocked(
- (newDockSide == DOCKED_LEFT || newDockSide == DOCKED_TOP)
- ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
- : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT,
- null);
- mDisplayContent.getDockedDividerController().notifyDockSideChanged(newDockSide);
- }
- }
-
- /**
- * Some primary split screen sides are not allowed by the policy. This method queries the policy
- * and moves the primary stack around if needed.
- *
- * @param parentConfig the configuration of the stack's parent.
- * @param primary true if adjusting the primary docked stack, false for secondary.
- * @param inOutBounds the bounds of the stack to adjust.
- */
- void repositionSplitScreenStackAfterRotation(Configuration parentConfig, boolean primary,
- Rect inOutBounds) {
- final int dockSide = getDockSide(mDisplayContent, parentConfig, inOutBounds);
- final int otherDockSide = DockedDividerUtils.invertDockSide(dockSide);
- final int primaryDockSide = primary ? dockSide : otherDockSide;
- if (mDisplayContent.getDockedDividerController()
- .canPrimaryStackDockTo(primaryDockSide,
- parentConfig.windowConfiguration.getBounds(),
- parentConfig.windowConfiguration.getRotation())) {
- return;
- }
- final Rect parentBounds = parentConfig.windowConfiguration.getBounds();
- switch (otherDockSide) {
- case DOCKED_LEFT:
- int movement = inOutBounds.left;
- inOutBounds.left -= movement;
- inOutBounds.right -= movement;
- break;
- case DOCKED_RIGHT:
- movement = parentBounds.right - inOutBounds.right;
- inOutBounds.left += movement;
- inOutBounds.right += movement;
- break;
- case DOCKED_TOP:
- movement = inOutBounds.top;
- inOutBounds.top -= movement;
- inOutBounds.bottom -= movement;
- break;
- case DOCKED_BOTTOM:
- movement = parentBounds.bottom - inOutBounds.bottom;
- inOutBounds.top += movement;
- inOutBounds.bottom += movement;
- break;
- }
- }
-
- /**
- * Snaps the bounds after rotation to the closest snap target for the docked stack.
- */
- void snapDockedStackAfterRotation(Configuration parentConfig, DisplayCutout displayCutout,
- Rect outBounds) {
-
- // Calculate the current position.
- final int dividerSize = mDisplayContent.getDockedDividerController().getContentWidth();
- final int dockSide = getDockSide(parentConfig, outBounds);
- final int dividerPosition = DockedDividerUtils.calculatePositionForBounds(outBounds,
- dockSide, dividerSize);
- final int displayWidth = parentConfig.windowConfiguration.getBounds().width();
- final int displayHeight = parentConfig.windowConfiguration.getBounds().height();
-
- // Snap the position to a target.
- final int rotation = parentConfig.windowConfiguration.getRotation();
- final int orientation = parentConfig.orientation;
- mDisplayContent.getDisplayPolicy().getStableInsetsLw(rotation, displayWidth, displayHeight,
- displayCutout, outBounds);
- final DividerSnapAlgorithm algorithm = new DividerSnapAlgorithm(
- mWmService.mContext.getResources(), displayWidth, displayHeight,
- dividerSize, orientation == Configuration.ORIENTATION_PORTRAIT, outBounds,
- getDockSide(), isMinimizedDockAndHomeStackResizable());
- final DividerSnapAlgorithm.SnapTarget target =
- algorithm.calculateNonDismissingSnapTarget(dividerPosition);
-
- // Recalculate the bounds based on the position of the target.
- DockedDividerUtils.calculateBoundsForPosition(target.position, dockSide,
- outBounds, displayWidth, displayHeight,
- dividerSize);
- }
-
- /**
* Put a Task in this stack. Used for adding only.
* When task is added to top of the stack, the entire branch of the hierarchy (including stack
* and display) will be brought to top.
@@ -3916,445 +3713,8 @@
}
}
- /**
- * Determines the stack and task bounds of the other stack when in docked mode. The current task
- * bounds is passed in but depending on the stack, the task and stack must match. Only in
- * minimized mode with resizable launcher, the other stack ignores calculating the stack bounds
- * and uses the task bounds passed in as the stack and task bounds, otherwise the stack bounds
- * is calculated and is also used for its task bounds.
- * If any of the out bounds are empty, it represents default bounds
- *
- * @param currentTempTaskBounds the current task bounds of the other stack
- * @param outStackBounds the calculated stack bounds of the other stack
- * @param outTempTaskBounds the calculated task bounds of the other stack
- */
- void getStackDockedModeBounds(Rect dockedBounds,
- Rect currentTempTaskBounds, Rect outStackBounds, Rect outTempTaskBounds) {
- final Configuration parentConfig = getParent().getConfiguration();
- outTempTaskBounds.setEmpty();
-
- if (dockedBounds == null || dockedBounds.isEmpty()) {
- // Calculate the primary docked bounds.
- final boolean dockedOnTopOrLeft = mWmService.mDockedStackCreateMode
- == SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
- getStackDockedModeBounds(parentConfig,
- true /* primary */, outStackBounds, dockedBounds,
- mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft);
- return;
- }
- final int dockedSide = getDockSide(parentConfig, dockedBounds);
-
- // When the home stack is resizable, should always have the same stack and task bounds
- if (isActivityTypeHome()) {
- final Task homeTask = getTopMostTask();
- if (homeTask == null || homeTask.isResizeable()) {
- // Calculate the home stack bounds when in docked mode and the home stack is
- // resizeable.
- getDisplayContent().mDividerControllerLocked
- .getHomeStackBoundsInDockedMode(parentConfig,
- dockedSide, outStackBounds);
- } else {
- // Home stack isn't resizeable, so don't specify stack bounds.
- outStackBounds.setEmpty();
- }
-
- outTempTaskBounds.set(outStackBounds);
- return;
- }
-
- // When minimized state, the stack bounds for all non-home and docked stack bounds should
- // match the passed task bounds
- if (isMinimizedDockAndHomeStackResizable() && currentTempTaskBounds != null) {
- outStackBounds.set(currentTempTaskBounds);
- return;
- }
-
- if (dockedSide == DOCKED_INVALID) {
- // Not sure how you got here...Only thing we can do is return current bounds.
- Slog.e(TAG_WM, "Failed to get valid docked side for docked stack");
- outStackBounds.set(getRawBounds());
- return;
- }
-
- final boolean dockedOnTopOrLeft = dockedSide == DOCKED_TOP || dockedSide == DOCKED_LEFT;
- getStackDockedModeBounds(parentConfig,
- false /* primary */, outStackBounds, dockedBounds,
- mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft);
- }
-
- /**
- * Outputs the bounds a stack should be given the presence of a docked stack on the display.
- * @param parentConfig The parent configuration.
- * @param primary {@code true} if getting the primary stack bounds.
- * @param outBounds Output bounds that should be used for the stack.
- * @param dockedBounds Bounds of the docked stack.
- * @param dockDividerWidth We need to know the width of the divider make to the output bounds
- * close to the side of the dock.
- * @param dockOnTopOrLeft If the docked stack is on the top or left side of the screen.
- */
- private void getStackDockedModeBounds(Configuration parentConfig, boolean primary,
- Rect outBounds, Rect dockedBounds, int dockDividerWidth,
- boolean dockOnTopOrLeft) {
- final Rect displayRect = parentConfig.windowConfiguration.getBounds();
- final boolean splitHorizontally = displayRect.width() > displayRect.height();
-
- outBounds.set(displayRect);
- if (primary) {
- if (mWmService.mDockedStackCreateBounds != null) {
- outBounds.set(mWmService.mDockedStackCreateBounds);
- return;
- }
-
- // The initial bounds of the docked stack when it is created about half the screen space
- // and its bounds can be adjusted after that. The bounds of all other stacks are
- // adjusted to occupy whatever screen space the docked stack isn't occupying.
- final DisplayCutout displayCutout = mDisplayContent.getDisplayInfo().displayCutout;
- mDisplayContent.getDisplayPolicy().getStableInsetsLw(
- parentConfig.windowConfiguration.getRotation(),
- displayRect.width(), displayRect.height(), displayCutout, mTmpRect2);
- final int position = new DividerSnapAlgorithm(mWmService.mContext.getResources(),
- displayRect.width(),
- displayRect.height(),
- dockDividerWidth,
- parentConfig.orientation == ORIENTATION_PORTRAIT,
- mTmpRect2).getMiddleTarget().position;
-
- if (dockOnTopOrLeft) {
- if (splitHorizontally) {
- outBounds.right = position;
- } else {
- outBounds.bottom = position;
- }
- } else {
- if (splitHorizontally) {
- outBounds.left = position + dockDividerWidth;
- } else {
- outBounds.top = position + dockDividerWidth;
- }
- }
- return;
- }
-
- // Other stacks occupy whatever space is left by the docked stack.
- if (!dockOnTopOrLeft) {
- if (splitHorizontally) {
- outBounds.right = dockedBounds.left - dockDividerWidth;
- } else {
- outBounds.bottom = dockedBounds.top - dockDividerWidth;
- }
- } else {
- if (splitHorizontally) {
- outBounds.left = dockedBounds.right + dockDividerWidth;
- } else {
- outBounds.top = dockedBounds.bottom + dockDividerWidth;
- }
- }
- DockedDividerUtils.sanitizeStackBounds(outBounds, !dockOnTopOrLeft);
- }
-
- void resetDockedStackToMiddle() {
- if (!inSplitScreenPrimaryWindowingMode()) {
- throw new IllegalStateException("Not a docked stack=" + this);
- }
-
- mWmService.mDockedStackCreateBounds = null;
-
- final Rect bounds = new Rect();
- final Rect tempBounds = new Rect();
- getStackDockedModeBounds(null /* dockedBounds */, null /* currentTempTaskBounds */,
- bounds, tempBounds);
- mStackSupervisor.resizeDockedStackLocked(bounds, null /* tempTaskBounds */,
- null /* tempTaskInsetBounds */, null /* tempOtherTaskBounds */,
- null /* tempOtherTaskInsetBounds */, false /* preserveWindows */,
- false /* deferResume */);
- }
-
- /**
- * Adjusts the stack bounds if the IME is visible.
- *
- * @param imeWin The IME window.
- * @param keepLastAmount Use {@code true} to keep the last adjusted amount from
- * {@link DockedStackDividerController} for adjusting the stack bounds,
- * Use {@code false} to reset adjusted amount as 0.
- * @see #updateAdjustForIme(float, float, boolean)
- */
- void setAdjustedForIme(WindowState imeWin, boolean keepLastAmount) {
- mImeWin = imeWin;
- mImeGoingAway = false;
- if (!mAdjustedForIme || keepLastAmount) {
- mAdjustedForIme = true;
- DockedStackDividerController controller = getDisplayContent().mDividerControllerLocked;
- final float adjustImeAmount = keepLastAmount ? controller.mLastAnimationProgress : 0f;
- final float adjustDividerAmount = keepLastAmount ? controller.mLastDividerProgress : 0f;
- updateAdjustForIme(adjustImeAmount, adjustDividerAmount, true /* force */);
- }
- }
-
- boolean isAdjustedForIme() {
- return mAdjustedForIme;
- }
-
- boolean isAnimatingForIme() {
- return mImeWin != null && mImeWin.isAnimatingLw();
- }
-
- /**
- * Update the stack's bounds (crop or position) according to the IME window's
- * current position. When IME window is animated, the bottom stack is animated
- * together to track the IME window's current position, and the top stack is
- * cropped as necessary.
- *
- * @return true if a traversal should be performed after the adjustment.
- */
- boolean updateAdjustForIme(float adjustAmount, float adjustDividerAmount, boolean force) {
- if (adjustAmount != mAdjustImeAmount
- || adjustDividerAmount != mAdjustDividerAmount || force) {
- mAdjustImeAmount = adjustAmount;
- mAdjustDividerAmount = adjustDividerAmount;
- updateAdjustedBounds();
- return isVisible();
- } else {
- return false;
- }
- }
-
- /**
- * Resets the adjustment after it got adjusted for the IME.
- * @param adjustBoundsNow if true, reset and update the bounds immediately and forget about
- * animations; otherwise, set flag and animates the window away together
- * with IME window.
- */
- void resetAdjustedForIme(boolean adjustBoundsNow) {
- if (adjustBoundsNow) {
- mImeWin = null;
- mImeGoingAway = false;
- mAdjustImeAmount = 0f;
- mAdjustDividerAmount = 0f;
- if (!mAdjustedForIme) {
- return;
- }
- mAdjustedForIme = false;
- updateAdjustedBounds();
- mWmService.setResizeDimLayer(false, getWindowingMode(), 1.0f);
- } else {
- mImeGoingAway |= mAdjustedForIme;
- }
- }
-
- /**
- * Sets the amount how much we currently minimize our stack.
- *
- * @param minimizeAmount The amount, between 0 and 1.
- * @return Whether the amount has changed and a layout is needed.
- */
- boolean setAdjustedForMinimizedDock(float minimizeAmount) {
- if (minimizeAmount != mMinimizeAmount) {
- mMinimizeAmount = minimizeAmount;
- updateAdjustedBounds();
- return isVisible();
- } else {
- return false;
- }
- }
-
boolean shouldIgnoreInput() {
- return isAdjustedForMinimizedDockedStack()
- || (inSplitScreenPrimaryWindowingMode() && isMinimizedDockAndHomeStackResizable());
- }
-
- /**
- * Puts all visible tasks that are adjusted for IME into resizing mode and adds the windows
- * to the list of to be drawn windows the service is waiting for.
- */
- void beginImeAdjustAnimation() {
- forAllLeafTasks((t) -> {
- if (t.hasContentToDisplay()) {
- t.setDragResizing(true, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
- t.setWaitingForDrawnIfResizingChanged();
- }
- }, true /* traverseTopToBottom */);
- }
-
- /** Resets the resizing state of all windows. */
- void endImeAdjustAnimation() {
- forAllLeafTasks((t) -> {
- t.setDragResizing(false, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
- }, true /* traverseTopToBottom */);
- }
-
- private int getMinTopStackBottom(final Rect displayContentRect, int originalStackBottom) {
- return displayContentRect.top + (int)
- ((originalStackBottom - displayContentRect.top) * ADJUSTED_STACK_FRACTION_MIN);
- }
-
- private boolean adjustForIME(final WindowState imeWin) {
- // To prevent task stack resize animation may flicking when playing app transition
- // animation & IME window enter animation in parallel, we need to make sure app
- // transition is done and then adjust task size for IME, skip the new adjusted frame when
- // app transition is still running.
- if (getDisplayContent().mAppTransition.isRunning()) {
- return false;
- }
-
- final int dockedSide = getDockSide();
- final boolean dockedTopOrBottom = dockedSide == DOCKED_TOP || dockedSide == DOCKED_BOTTOM;
- if (imeWin == null || !dockedTopOrBottom) {
- return false;
- }
-
- final Rect displayStableRect = mTmpRect;
- final Rect contentBounds = mTmpRect2;
-
- // Calculate the content bounds excluding the area occupied by IME
- getDisplayContent().getStableRect(displayStableRect);
- contentBounds.set(displayStableRect);
- int imeTop = Math.max(imeWin.getFrameLw().top, contentBounds.top);
-
- imeTop += imeWin.getGivenContentInsetsLw().top;
- if (contentBounds.bottom > imeTop) {
- contentBounds.bottom = imeTop;
- }
-
- final int yOffset = displayStableRect.bottom - contentBounds.bottom;
-
- final int dividerWidth =
- getDisplayContent().mDividerControllerLocked.getContentWidth();
- final int dividerWidthInactive =
- getDisplayContent().mDividerControllerLocked.getContentWidthInactive();
-
- if (dockedSide == DOCKED_TOP) {
- // If this stack is docked on top, we make it smaller so the bottom stack is not
- // occluded by IME. We shift its bottom up by the height of the IME, but
- // leaves at least 30% of the top stack visible.
- final int minTopStackBottom =
- getMinTopStackBottom(displayStableRect, getRawBounds().bottom);
- final int bottom = Math.max(
- getRawBounds().bottom - yOffset + dividerWidth - dividerWidthInactive,
- minTopStackBottom);
- mTmpAdjustedBounds.set(getRawBounds());
- mTmpAdjustedBounds.bottom = (int) (mAdjustImeAmount * bottom + (1 - mAdjustImeAmount)
- * getRawBounds().bottom);
- mFullyAdjustedImeBounds.set(getRawBounds());
- } else {
- // When the stack is on bottom and has no focus, it's only adjusted for divider width.
- final int dividerWidthDelta = dividerWidthInactive - dividerWidth;
-
- // When the stack is on bottom and has focus, it needs to be moved up so as to
- // not occluded by IME, and at the same time adjusted for divider width.
- // We try to move it up by the height of the IME window, but only to the extent
- // that leaves at least 30% of the top stack visible.
- // 'top' is where the top of bottom stack will move to in this case.
- final int topBeforeImeAdjust =
- getRawBounds().top - dividerWidth + dividerWidthInactive;
- final int minTopStackBottom =
- getMinTopStackBottom(displayStableRect,
- getRawBounds().top - dividerWidth);
- final int top = Math.max(
- getRawBounds().top - yOffset, minTopStackBottom + dividerWidthInactive);
-
- mTmpAdjustedBounds.set(getRawBounds());
- // Account for the adjustment for IME and divider width separately.
- // (top - topBeforeImeAdjust) is the amount of movement due to IME only,
- // and dividerWidthDelta is due to divider width change only.
- mTmpAdjustedBounds.top =
- getRawBounds().top + (int) (mAdjustImeAmount * (top - topBeforeImeAdjust)
- + mAdjustDividerAmount * dividerWidthDelta);
- mFullyAdjustedImeBounds.set(getRawBounds());
- mFullyAdjustedImeBounds.top = top;
- mFullyAdjustedImeBounds.bottom = top + getRawBounds().height();
- }
- return true;
- }
-
- private boolean adjustForMinimizedDockedStack(float minimizeAmount) {
- final int dockSide = getDockSide();
- if (dockSide == DOCKED_INVALID && !mTmpAdjustedBounds.isEmpty()) {
- return false;
- }
-
- if (dockSide == DOCKED_TOP) {
- mWmService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect);
- int topInset = mTmpRect.top;
- mTmpAdjustedBounds.set(getRawBounds());
- mTmpAdjustedBounds.bottom = (int) (minimizeAmount * topInset + (1 - minimizeAmount)
- * getRawBounds().bottom);
- } else if (dockSide == DOCKED_LEFT) {
- mTmpAdjustedBounds.set(getRawBounds());
- final int width = getRawBounds().width();
- mTmpAdjustedBounds.right =
- (int) (minimizeAmount * mDockedStackMinimizeThickness
- + (1 - minimizeAmount) * getRawBounds().right);
- mTmpAdjustedBounds.left = mTmpAdjustedBounds.right - width;
- } else if (dockSide == DOCKED_RIGHT) {
- mTmpAdjustedBounds.set(getRawBounds());
- mTmpAdjustedBounds.left =
- (int) (minimizeAmount * (getRawBounds().right - mDockedStackMinimizeThickness)
- + (1 - minimizeAmount) * getRawBounds().left);
- }
- return true;
- }
-
- boolean isMinimizedDockAndHomeStackResizable() {
- return mDisplayContent.mDividerControllerLocked.isMinimizedDock()
- && mDisplayContent.mDividerControllerLocked.isHomeStackResizable();
- }
-
- /**
- * @return the distance in pixels how much the stack gets minimized from it's original size
- */
- int getMinimizeDistance() {
- final int dockSide = getDockSide();
- if (dockSide == DOCKED_INVALID) {
- return 0;
- }
-
- if (dockSide == DOCKED_TOP) {
- mWmService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect);
- int topInset = mTmpRect.top;
- return getRawBounds().bottom - topInset;
- } else if (dockSide == DOCKED_LEFT || dockSide == DOCKED_RIGHT) {
- return getRawBounds().width() - mDockedStackMinimizeThickness;
- } else {
- return 0;
- }
- }
-
- /**
- * Updates the adjustment depending on it's current state.
- */
- private void updateAdjustedBounds() {
- boolean adjust = false;
- if (mMinimizeAmount != 0f) {
- adjust = adjustForMinimizedDockedStack(mMinimizeAmount);
- } else if (mAdjustedForIme) {
- adjust = adjustForIME(mImeWin);
- }
- if (!adjust) {
- mTmpAdjustedBounds.setEmpty();
- }
- setAdjustedBounds(mTmpAdjustedBounds);
-
- final boolean isImeTarget = (mWmService.getImeFocusStackLocked() == this);
- if (mAdjustedForIme && adjust && !isImeTarget) {
- final float alpha = Math.max(mAdjustImeAmount, mAdjustDividerAmount)
- * IME_ADJUST_DIM_AMOUNT;
- mWmService.setResizeDimLayer(true, getWindowingMode(), alpha);
- }
- }
-
- void applyAdjustForImeIfNeeded(Task task) {
- if (mMinimizeAmount != 0f || !mAdjustedForIme || mAdjustedBounds.isEmpty()) {
- return;
- }
-
- final Rect insetBounds = mImeGoingAway ? getRawBounds() : mFullyAdjustedImeBounds;
- task.alignToAdjustedBounds(mAdjustedBounds, insetBounds, getDockSide() == DOCKED_TOP);
- mDisplayContent.setLayoutNeeded();
- }
-
-
- boolean isAdjustedForMinimizedDockedStack() {
- return mMinimizeAmount != 0f;
+ return inSplitScreenPrimaryWindowingMode() && !isFocusable();
}
@Override
@@ -4362,17 +3722,6 @@
pw.println(prefix + "mStackId=" + getRootTaskId());
pw.println(prefix + "mDeferRemoval=" + mDeferRemoval);
pw.println(prefix + "mBounds=" + getRawBounds().toShortString());
- if (mMinimizeAmount != 0f) {
- pw.println(prefix + "mMinimizeAmount=" + mMinimizeAmount);
- }
- if (mAdjustedForIme) {
- pw.println(prefix + "mAdjustedForIme=true");
- pw.println(prefix + "mAdjustImeAmount=" + mAdjustImeAmount);
- pw.println(prefix + "mAdjustDividerAmount=" + mAdjustDividerAmount);
- }
- if (!mAdjustedBounds.isEmpty()) {
- pw.println(prefix + "mAdjustedBounds=" + mAdjustedBounds.toShortString());
- }
for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; taskNdx--) {
mChildren.get(taskNdx).dump(pw, prefix + " ", dumpAll);
}
@@ -4391,39 +3740,6 @@
}
/**
- * For docked workspace (or workspace that's side-by-side to the docked), provides
- * information which side of the screen was the dock anchored.
- */
- int getDockSide() {
- return getDockSide(mDisplayContent.getConfiguration(), getRawBounds());
- }
-
- int getDockSideForDisplay(DisplayContent dc) {
- return getDockSide(dc, dc.getConfiguration(), getRawBounds());
- }
-
- int getDockSide(Configuration parentConfig, Rect bounds) {
- if (mDisplayContent == null) {
- return DOCKED_INVALID;
- }
- return getDockSide(mDisplayContent, parentConfig, bounds);
- }
-
- private int getDockSide(DisplayContent dc, Configuration parentConfig, Rect bounds) {
- return dc.getDockedDividerController().getDockSide(bounds,
- parentConfig.windowConfiguration.getBounds(),
- parentConfig.orientation, parentConfig.windowConfiguration.getRotation());
- }
-
- boolean hasTaskForUser(int userId) {
- final PooledPredicate p = PooledLambda.obtainPredicate(
- Task::isTaskForUser, PooledLambda.__(Task.class), userId);
- final Task task = getTask(p);
- p.recycle();
- return task != null;
- }
-
- /**
* Sets the current picture-in-picture aspect ratio.
*/
void setPictureInPictureAspectRatio(float aspectRatio) {
@@ -4639,16 +3955,11 @@
bounds.dumpDebug(proto, BOUNDS);
}
getOverrideDisplayedBounds().dumpDebug(proto, DISPLAYED_BOUNDS);
- mAdjustedBounds.dumpDebug(proto, ADJUSTED_BOUNDS);
if (mLastNonFullscreenBounds != null) {
mLastNonFullscreenBounds.dumpDebug(proto, LAST_NON_FULLSCREEN_BOUNDS);
}
proto.write(DEFER_REMOVAL, mDeferRemoval);
- proto.write(MINIMIZE_AMOUNT, mMinimizeAmount);
- proto.write(ADJUSTED_FOR_IME, mAdjustedForIme);
- proto.write(ADJUST_IME_AMOUNT, mAdjustImeAmount);
- proto.write(ADJUST_DIVIDER_AMOUNT, mAdjustDividerAmount);
proto.write(ANIMATING_BOUNDS, mBoundsAnimating);
if (mSurfaceControl != null) {
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index b2d2f62..6d7f8fb 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -35,12 +35,10 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.pm.PackageManager.NOTIFY_PACKAGE_USE_ACTIVITY;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.graphics.Rect.copyOrNull;
import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
import static android.os.Process.INVALID_UID;
import static android.os.Process.SYSTEM_UID;
@@ -210,17 +208,6 @@
/** True if the docked stack is currently being resized. */
private boolean mDockedStackResizing;
- /**
- * True if there are pending docked bounds that need to be applied after
- * {@link #mDockedStackResizing} is reset to false.
- */
- private boolean mHasPendingDockedBounds;
- private Rect mPendingDockedBounds;
- private Rect mPendingTempDockedTaskBounds;
- private Rect mPendingTempDockedTaskInsetBounds;
- private Rect mPendingTempOtherTaskBounds;
- private Rect mPendingTempOtherTaskInsetBounds;
-
// Activity actions an app cannot start if it uses a permission which is not granted.
private static final ArrayMap<String, String> ACTION_TO_RUNTIME_PERMISSION =
new ArrayMap<>();
@@ -387,15 +374,6 @@
*/
private final ArraySet<Integer> mResizingTasksDuringAnimation = new ArraySet<>();
-
- /**
- * If set to {@code false} all calls to resize the docked stack {@link #resizeDockedStackLocked}
- * will be ignored. Useful for the case where the caller is handling resizing of other stack and
- * moving tasks around and doesn't want dock stack to be resized due to an automatic trigger
- * like the docked stack going empty.
- */
- private boolean mAllowDockedStackResize = true;
-
private KeyguardController mKeyguardController;
private PowerManager mPowerManager;
@@ -1541,12 +1519,6 @@
}
otherStack.setWindowingMode(WINDOWING_MODE_UNDEFINED);
}
-
- // Also disable docked stack resizing since we have manually adjusted the
- // size of other stacks above and we don't want to trigger a docked stack
- // resize when we remove task from it below and it is detached from the
- // display because it no longer contains any tasks.
- mAllowDockedStackResize = false;
}
// If we are moving from the pinned stack, then the animation takes care of updating
@@ -1563,7 +1535,6 @@
mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
mRootWindowContainer.resumeFocusedStacksTopActivities();
} finally {
- mAllowDockedStackResize = true;
mService.continueWindowLayout();
}
}
@@ -1580,120 +1551,6 @@
mDockedStackResizing = resizing;
mWindowManager.setDockedStackResizing(resizing);
-
- if (!resizing && mHasPendingDockedBounds) {
- resizeDockedStackLocked(mPendingDockedBounds, mPendingTempDockedTaskBounds,
- mPendingTempDockedTaskInsetBounds, mPendingTempOtherTaskBounds,
- mPendingTempOtherTaskInsetBounds, PRESERVE_WINDOWS);
-
- mHasPendingDockedBounds = false;
- mPendingDockedBounds = null;
- mPendingTempDockedTaskBounds = null;
- mPendingTempDockedTaskInsetBounds = null;
- mPendingTempOtherTaskBounds = null;
- mPendingTempOtherTaskInsetBounds = null;
- }
- }
-
- void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
- Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds,
- boolean preserveWindows) {
- resizeDockedStackLocked(dockedBounds, tempDockedTaskBounds, tempDockedTaskInsetBounds,
- tempOtherTaskBounds, tempOtherTaskInsetBounds, preserveWindows,
- false /* deferResume */);
- }
-
- void resizeDockedStackLocked(Rect displayedBounds, Rect tempDockedTaskBounds,
- Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds,
- boolean preserveWindows, boolean deferResume) {
-
- if (!mAllowDockedStackResize) {
- // Docked stack resize currently disabled.
- return;
- }
-
- final ActivityStack stack =
- mRootWindowContainer.getDefaultDisplay().getRootSplitScreenPrimaryTask();
- if (stack == null) {
- Slog.w(TAG, "resizeDockedStackLocked: docked stack not found");
- return;
- }
-
- if (mDockedStackResizing) {
- mHasPendingDockedBounds = true;
- mPendingDockedBounds = copyOrNull(displayedBounds);
- mPendingTempDockedTaskBounds = copyOrNull(tempDockedTaskBounds);
- mPendingTempDockedTaskInsetBounds = copyOrNull(tempDockedTaskInsetBounds);
- mPendingTempOtherTaskBounds = copyOrNull(tempOtherTaskBounds);
- mPendingTempOtherTaskInsetBounds = copyOrNull(tempOtherTaskInsetBounds);
- }
-
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resizeDockedStack");
- mService.deferWindowLayout();
- try {
- // Don't allow re-entry while resizing. E.g. due to docked stack detaching.
- mAllowDockedStackResize = false;
- ActivityRecord r = stack.topRunningActivity();
- stack.resize(displayedBounds, tempDockedTaskBounds,
- !PRESERVE_WINDOWS, DEFER_RESUME);
-
- // TODO: Checking for isAttached might not be needed as if the user passes in null
- // dockedBounds then they want the docked stack to be dismissed.
- if (stack.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
- || (displayedBounds == null && !stack.isAttached())) {
- // The dock stack either was dismissed or went fullscreen, which is kinda the same.
- // In this case we make all other static stacks fullscreen and move all
- // docked stack tasks to the fullscreen stack.
- moveTasksToFullscreenStackLocked(stack, ON_TOP);
-
- // stack shouldn't contain anymore activities, so nothing to resume.
- r = null;
- } else {
- // Docked stacks occupy a dedicated region on screen so the size of all other
- // static stacks need to be adjusted so they don't overlap with the docked stack.
- // We get the bounds to use from window manager which has been adjusted for any
- // screen controls and is also the same for all stacks.
- final DisplayContent display = mRootWindowContainer.getDefaultDisplay();
- final Rect otherTaskRect = new Rect();
- for (int i = display.getStackCount() - 1; i >= 0; --i) {
- final ActivityStack current = display.getStackAt(i);
- if (!current.inSplitScreenSecondaryWindowingMode()) {
- continue;
- }
- if (!current.affectedBySplitScreenResize()) {
- continue;
- }
- if (mDockedStackResizing && !current.isTopActivityVisible()) {
- // Non-visible stacks get resized once we're done with the resize
- // interaction.
- continue;
- }
- current.getStackDockedModeBounds(displayedBounds,
- tempOtherTaskBounds /* currentTempTaskBounds */,
- tempRect /* outStackBounds */,
- otherTaskRect /* outTempTaskBounds */);
-
- if (tempRect.isEmpty()) {
- // If this scenario is hit, it means something is not working right.
- // Empty/null bounds implies fullscreen. In the event that this stack
- // *should* be fullscreen, its mode should be set explicitly in a form
- // of setWindowingMode so that other parts of the system are updated
- // properly.
- throw new IllegalArgumentException("Trying to set null bounds on a"
- + " non-fullscreen stack");
- }
-
- current.resize(tempRect, tempOtherTaskBounds, preserveWindows, deferResume);
- }
- }
- if (!deferResume) {
- stack.ensureVisibleActivitiesConfiguration(r, preserveWindows);
- }
- } finally {
- mAllowDockedStackResize = true;
- mService.continueWindowLayout();
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- }
}
private void removeStackInSurfaceTransaction(ActivityStack stack) {
@@ -2723,9 +2580,6 @@
mService.deferWindowLayout();
try {
if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- mWindowManager.setDockedStackCreateStateLocked(
- activityOptions.getSplitScreenCreateMode(), null /* initialBounds */);
-
// Defer updating the stack in which recents is until the app transition is done, to
// not run into issues where we still need to draw the task in recents but the
// docked stack is already created.
@@ -2801,24 +2655,6 @@
// the window renders full-screen with the background filling the void. Also only
// call this at the end to make sure that tasks exists on the window manager side.
setResizingDuringAnimation(task);
-
- final DisplayContent display = task.getStack().getDisplay();
- final ActivityStack topSecondaryStack =
- display.getTopStackInWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
- if (topSecondaryStack != null && topSecondaryStack.isActivityTypeHome()) {
- // If the home activity is the top split-screen secondary stack, then the
- // primary split-screen stack is in the minimized mode which means it can't
- // receive input keys, so we should move the focused app to the home app so that
- // window manager can correctly calculate the focus window that can receive
- // input keys.
- display.moveHomeActivityToTop(
- "startActivityFromRecents: homeVisibleInSplitScreen");
-
- // Immediately update the minimized docked stack mode, the upcoming animation
- // for the docked activity (WMS.overridePendingAppTransitionMultiThumbFuture)
- // will do the animation to the target bounds
- mWindowManager.checkSplitScreenMinimizedChanged(false /* animate */);
- }
}
mService.continueWindowLayout();
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index f52c7f2..3210304 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1819,12 +1819,12 @@
*/
private int deliverToCurrentTopIfNeeded(ActivityStack topStack) {
final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop);
- final boolean dontStart = top != null
+ final boolean dontStart = top != null && mStartActivity.resultTo == null
&& top.mActivityComponent.equals(mStartActivity.mActivityComponent)
&& top.mUserId == mStartActivity.mUserId
&& top.attachedToProcess()
&& ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
- || isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK))
+ || isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK))
// This allows home activity to automatically launch on secondary display when
// display added, if home was the top activity on default display, instead of
// sending new intent to the home activity on default display.
@@ -2055,6 +2055,8 @@
&& !isLaunchModeOneOf(LAUNCH_SINGLE_TASK, LAUNCH_SINGLE_INSTANCE)
&& (mLaunchFlags & FLAG_ACTIVITY_NEW_DOCUMENT) != 0;
+ sendNewTaskResultRequestIfNeeded();
+
if ((mLaunchFlags & FLAG_ACTIVITY_NEW_DOCUMENT) != 0 && r.resultTo == null) {
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
}
@@ -2236,8 +2238,6 @@
mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
}
}
-
- sendNewTaskResultRequestIfNeeded();
}
private void computeSourceStack() {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 510072d..ca856ca 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -169,12 +169,6 @@
public abstract List<IBinder> getTopVisibleActivities();
/**
- * Callback for window manager to let activity manager know that docked stack changes its
- * minimized state.
- */
- public abstract void notifyDockedStackMinimizedChanged(boolean minimized);
-
- /**
* Notify listeners that contents are drawn for the first time on a single task display.
*
* @param displayId An ID of the display on which contents are drawn.
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 693a5e4..770dabf 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -31,12 +31,12 @@
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW;
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -2309,9 +2309,7 @@
@Override
public boolean setTaskWindowingMode(int taskId, int windowingMode, boolean toTop) {
if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- return setTaskWindowingModeSplitScreenPrimary(taskId,
- SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT,
- toTop, ANIMATE, null /* initialBounds */, true /* showRecents */);
+ return setTaskWindowingModeSplitScreenPrimary(taskId, toTop);
}
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "setTaskWindowingMode()");
synchronized (mGlobalLock) {
@@ -2681,10 +2679,6 @@
throw new IllegalArgumentException("moveTaskToStack: Attempt to move task "
+ taskId + " to stack " + stackId);
}
- if (stack.inSplitScreenPrimaryWindowingMode()) {
- mWindowManager.setDockedStackCreateStateLocked(
- SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */);
- }
task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME,
"moveTaskToStack");
} finally {
@@ -2697,22 +2691,11 @@
* Moves the specified task to the primary-split-screen stack.
*
* @param taskId Id of task to move.
- * @param createMode The mode the primary split screen stack should be created in if it doesn't
- * exist already. See
- * {@link android.app.ActivityTaskManager#SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT}
- * and
- * {@link android.app.ActivityTaskManager#SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT}
* @param toTop If the task and stack should be moved to the top.
- * @param animate Whether we should play an animation for the moving the task.
- * @param initialBounds If the primary stack gets created, it will use these bounds for the
- * stack. Pass {@code null} to use default bounds.
- * @param showRecents If the recents activity should be shown on the other side of the task
- * going into split-screen mode.
* @return Whether the task was successfully put into splitscreen.
*/
@Override
- public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode,
- boolean toTop, boolean animate, Rect initialBounds, boolean showRecents) {
+ public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, boolean toTop) {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
"setTaskWindowingModeSplitScreenPrimary()");
synchronized (mGlobalLock) {
@@ -4273,6 +4256,7 @@
}
}
+ // TODO(b/149338177): remove when CTS no-longer requires it
@Override
public void resizeDockedStack(Rect dockedBounds, Rect tempDockedTaskBounds,
Rect tempDockedTaskInsetBounds,
@@ -4281,9 +4265,42 @@
long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- mStackSupervisor.resizeDockedStackLocked(dockedBounds, tempDockedTaskBounds,
- tempDockedTaskInsetBounds, tempOtherTaskBounds, tempOtherTaskInsetBounds,
- PRESERVE_WINDOWS);
+ final DisplayContent dc = mRootWindowContainer.getDefaultDisplay();
+ TaskTile primary = null;
+ TaskTile secondary = null;
+ for (int i = dc.getStackCount() - 1; i >= 0; --i) {
+ final TaskTile t = dc.getStackAt(i).asTile();
+ if (t == null) {
+ continue;
+ }
+ if (t.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ primary = t;
+ } else if (t.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
+ secondary = t;
+ }
+ }
+ if (primary == null || secondary == null) {
+ return;
+ }
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ final Rect primaryRect =
+ tempDockedTaskInsetBounds != null ? tempDockedTaskInsetBounds
+ : (tempDockedTaskBounds != null ? tempDockedTaskBounds
+ : dockedBounds);
+ wct.setBounds(primary.mRemoteToken, primaryRect);
+ Rect otherRect = tempOtherTaskInsetBounds != null ? tempOtherTaskInsetBounds
+ : tempOtherTaskBounds;
+ if (otherRect == null) {
+ // Temporary estimation... again this is just for tests.
+ otherRect = new Rect(secondary.getBounds());
+ if (dc.getBounds().width() > dc.getBounds().height()) {
+ otherRect.left = primaryRect.right + 6;
+ } else {
+ otherRect.top = primaryRect.bottom + 6;
+ }
+ }
+ wct.setBounds(secondary.mRemoteToken, otherRect);
+ mTaskOrganizerController.applyContainerTransaction(wct, null /* organizer */);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -5868,8 +5885,8 @@
* Return the intent set with {@link Intent#CATEGORY_SECONDARY_HOME} to resolve secondary home
* activities.
*
- * @param preferredPackage Specify a preferred package name, otherwise use secondary home
- * component defined in config_secondaryHomeComponent.
+ * @param preferredPackage Specify a preferred package name, otherwise use the package name
+ * defined in config_secondaryHomePackage.
* @return the intent set with {@link Intent#CATEGORY_SECONDARY_HOME}
*/
Intent getSecondaryHomeIntent(String preferredPackage) {
@@ -5877,10 +5894,10 @@
final boolean useSystemProvidedLauncher = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary);
if (preferredPackage == null || useSystemProvidedLauncher) {
- // Using the component stored in config if no package name or forced.
- final String secondaryHomeComponent = mContext.getResources().getString(
- com.android.internal.R.string.config_secondaryHomeComponent);
- intent.setComponent(ComponentName.unflattenFromString(secondaryHomeComponent));
+ // Using the package name stored in config if no preferred package name or forced.
+ final String secondaryHomePackage = mContext.getResources().getString(
+ com.android.internal.R.string.config_secondaryHomePackage);
+ intent.setPackage(secondaryHomePackage);
} else {
intent.setPackage(preferredPackage);
}
@@ -6136,13 +6153,6 @@
}
@Override
- public void notifyDockedStackMinimizedChanged(boolean minimized) {
- synchronized (mGlobalLock) {
- mRootWindowContainer.setDockedStackMinimized(minimized);
- }
- }
-
- @Override
public int startActivitiesAsPackage(String packageName, @Nullable String featureId,
int userId, Intent[] intents, Bundle bOptions) {
Objects.requireNonNull(intents, "intents");
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 8cf0881..f86aeb2 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -172,6 +172,7 @@
private static final int MAX_CLIP_REVEAL_TRANSITION_DURATION = 420;
private static final int THUMBNAIL_APP_TRANSITION_DURATION = 336;
private static final long APP_TRANSITION_TIMEOUT_MS = 5000;
+ static final int MAX_APP_TRANSITION_DURATION = 3 * 1000; // 3 secs.
private final Context mContext;
private final WindowManagerService mService;
@@ -446,8 +447,6 @@
? topOpeningAnim.getStatusBarTransitionsStartTime()
: SystemClock.uptimeMillis(),
AnimationAdapter.STATUS_BAR_TRANSITION_DURATION);
- mDisplayContent.getDockedDividerController()
- .notifyAppTransitionStarting(openingApps, transit);
if (mRemoteAnimationController != null) {
mRemoteAnimationController.goodToGo();
@@ -2308,14 +2307,14 @@
}
notifyAppTransitionTimeoutLocked();
if (isTransitionSet() || !dc.mOpeningApps.isEmpty() || !dc.mClosingApps.isEmpty()
- || !dc.mChangingApps.isEmpty()) {
+ || !dc.mChangingContainers.isEmpty()) {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"*** APP TRANSITION TIMEOUT. displayId=%d isTransitionSet()=%b "
+ "mOpeningApps.size()=%d mClosingApps.size()=%d "
+ "mChangingApps.size()=%d",
dc.getDisplayId(), dc.mAppTransition.isTransitionSet(),
dc.mOpeningApps.size(), dc.mClosingApps.size(),
- dc.mChangingApps.size());
+ dc.mChangingContainers.size());
setTimeout();
mService.mWindowPlacerLocked.performSurfacePlacement();
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 3f4e791..0912b2e 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -55,6 +55,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import android.annotation.NonNull;
import android.os.Trace;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -86,7 +87,7 @@
private final WallpaperController mWallpaperControllerLocked;
private RemoteAnimationDefinition mRemoteAnimationDefinition = null;
- private final ArrayMap<ActivityRecord, Integer> mTempTransitionReasons = new ArrayMap<>();
+ private final ArrayMap<WindowContainer, Integer> mTempTransitionReasons = new ArrayMap<>();
AppTransitionController(WindowManagerService service, DisplayContent displayContent) {
mService = service;
@@ -104,7 +105,8 @@
void handleAppTransitionReady() {
mTempTransitionReasons.clear();
if (!transitionGoodToGo(mDisplayContent.mOpeningApps, mTempTransitionReasons)
- || !transitionGoodToGo(mDisplayContent.mChangingApps, mTempTransitionReasons)) {
+ || !transitionGoodToGo(mDisplayContent.mChangingContainers,
+ mTempTransitionReasons)) {
return;
}
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
@@ -130,17 +132,21 @@
// transition selection depends on wallpaper target visibility.
mDisplayContent.mOpeningApps.valueAtUnchecked(i).clearAnimatingFlags();
}
- appCount = mDisplayContent.mChangingApps.size();
+ appCount = mDisplayContent.mChangingContainers.size();
for (int i = 0; i < appCount; ++i) {
// Clearing for same reason as above.
- mDisplayContent.mChangingApps.valueAtUnchecked(i).clearAnimatingFlags();
+ final ActivityRecord activity = getAppFromContainer(
+ mDisplayContent.mChangingContainers.valueAtUnchecked(i));
+ if (activity != null) {
+ activity.clearAnimatingFlags();
+ }
}
// Adjust wallpaper before we pull the lower/upper target, since pending changes
// (like the clearAnimatingFlags() above) might affect wallpaper target result.
// Or, the opening app window should be a wallpaper target.
mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(
- mDisplayContent.mOpeningApps, mDisplayContent.mChangingApps);
+ mDisplayContent.mOpeningApps);
// Determine if closing and opening app token sets are wallpaper targets, in which case
// special animations are needed.
@@ -159,7 +165,7 @@
// no need to do an animation. This is the case, for example, when this transition is being
// done behind a dream window.
final ArraySet<Integer> activityTypes = collectActivityTypes(mDisplayContent.mOpeningApps,
- mDisplayContent.mClosingApps, mDisplayContent.mChangingApps);
+ mDisplayContent.mClosingApps, mDisplayContent.mChangingContainers);
final boolean allowAnimations = mDisplayContent.getDisplayPolicy().allowAppAnimationsLw();
final ActivityRecord animLpActivity = allowAnimations
? findAnimLayoutParamsToken(transit, activityTypes)
@@ -171,14 +177,13 @@
? getTopApp(mDisplayContent.mClosingApps, false /* ignoreHidden */)
: null;
final ActivityRecord topChangingApp = allowAnimations
- ? getTopApp(mDisplayContent.mChangingApps, false /* ignoreHidden */)
+ ? getTopApp(mDisplayContent.mChangingContainers, false /* ignoreHidden */)
: null;
final WindowManager.LayoutParams animLp = getAnimLp(animLpActivity);
overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes);
final boolean voiceInteraction = containsVoiceInteraction(mDisplayContent.mOpeningApps)
- || containsVoiceInteraction(mDisplayContent.mOpeningApps)
- || containsVoiceInteraction(mDisplayContent.mChangingApps);
+ || containsVoiceInteraction(mDisplayContent.mOpeningApps);
final int layoutRedo;
mService.mSurfaceAnimationRunner.deferStartingAnimations();
@@ -206,7 +211,7 @@
mDisplayContent.mOpeningApps.clear();
mDisplayContent.mClosingApps.clear();
- mDisplayContent.mChangingApps.clear();
+ mDisplayContent.mChangingContainers.clear();
mDisplayContent.mUnknownAppVisibilityController.clear();
// This has changed the visibility of windows, so perform
@@ -235,9 +240,9 @@
return mainWindow != null ? mainWindow.mAttrs : null;
}
- RemoteAnimationAdapter getRemoteAnimationOverride(ActivityRecord animLpActivity,
+ RemoteAnimationAdapter getRemoteAnimationOverride(@NonNull WindowContainer container,
@TransitionType int transit, ArraySet<Integer> activityTypes) {
- final RemoteAnimationDefinition definition = animLpActivity.getRemoteAnimationDefinition();
+ final RemoteAnimationDefinition definition = container.getRemoteAnimationDefinition();
if (definition != null) {
final RemoteAnimationAdapter adapter = definition.getAdapter(transit, activityTypes);
if (adapter != null) {
@@ -271,6 +276,11 @@
}
}
+ static ActivityRecord getAppFromContainer(WindowContainer wc) {
+ return wc.asTask() != null ? wc.asTask().getTopNonFinishingActivity()
+ : wc.asActivityRecord();
+ }
+
/**
* @return The window token that determines the animation theme.
*/
@@ -279,14 +289,14 @@
ActivityRecord result;
final ArraySet<ActivityRecord> closingApps = mDisplayContent.mClosingApps;
final ArraySet<ActivityRecord> openingApps = mDisplayContent.mOpeningApps;
- final ArraySet<ActivityRecord> changingApps = mDisplayContent.mChangingApps;
+ final ArraySet<WindowContainer> changingApps = mDisplayContent.mChangingContainers;
// Remote animations always win, but fullscreen tokens override non-fullscreen tokens.
result = lookForHighestTokenWithFilter(closingApps, openingApps, changingApps,
w -> w.getRemoteAnimationDefinition() != null
&& w.getRemoteAnimationDefinition().hasTransition(transit, activityTypes));
if (result != null) {
- return result;
+ return getAppFromContainer(result);
}
result = lookForHighestTokenWithFilter(closingApps, openingApps, changingApps,
w -> w.fillsParent() && w.findMainWindow() != null);
@@ -302,7 +312,7 @@
* of apps in {@code array1}, {@code array2}, and {@code array3}.
*/
private static ArraySet<Integer> collectActivityTypes(ArraySet<ActivityRecord> array1,
- ArraySet<ActivityRecord> array2, ArraySet<ActivityRecord> array3) {
+ ArraySet<ActivityRecord> array2, ArraySet<WindowContainer> array3) {
final ArraySet<Integer> result = new ArraySet<>();
for (int i = array1.size() - 1; i >= 0; i--) {
result.add(array1.valueAt(i).getActivityType());
@@ -317,7 +327,7 @@
}
private static ActivityRecord lookForHighestTokenWithFilter(ArraySet<ActivityRecord> array1,
- ArraySet<ActivityRecord> array2, ArraySet<ActivityRecord> array3,
+ ArraySet<ActivityRecord> array2, ArraySet<WindowContainer> array3,
Predicate<ActivityRecord> filter) {
final int array2base = array1.size();
final int array3base = array2.size() + array2base;
@@ -325,15 +335,16 @@
int bestPrefixOrderIndex = Integer.MIN_VALUE;
ActivityRecord bestToken = null;
for (int i = 0; i < count; i++) {
- final ActivityRecord wtoken = i < array2base
+ final WindowContainer wtoken = i < array2base
? array1.valueAt(i)
: (i < array3base
? array2.valueAt(i - array2base)
: array3.valueAt(i - array3base));
final int prefixOrderIndex = wtoken.getPrefixOrderIndex();
- if (filter.test(wtoken) && prefixOrderIndex > bestPrefixOrderIndex) {
+ final ActivityRecord r = getAppFromContainer(wtoken);
+ if (r != null && filter.test(r) && prefixOrderIndex > bestPrefixOrderIndex) {
bestPrefixOrderIndex = prefixOrderIndex;
- bestToken = wtoken;
+ bestToken = r;
}
}
return bestToken;
@@ -589,21 +600,13 @@
}
private void handleChangingApps(@TransitionType int transit) {
- final ArraySet<ActivityRecord> apps = mDisplayContent.mChangingApps;
+ final ArraySet<WindowContainer> apps = mDisplayContent.mChangingContainers;
final int appsCount = apps.size();
for (int i = 0; i < appsCount; i++) {
- ActivityRecord activity = apps.valueAt(i);
- ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now changing app %s", activity);
- activity.cancelAnimationOnly();
- activity.applyAnimation(null, transit, true, false,
+ WindowContainer wc = apps.valueAt(i);
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now changing app %s", wc);
+ wc.applyAnimation(null, transit, true, false,
null /* animationFinishedCallback */);
- activity.updateReportedVisibilityLocked();
- mService.openSurfaceTransaction();
- try {
- activity.showAllWindowsLocked();
- } finally {
- mService.closeSurfaceTransaction("handleChangingApps");
- }
}
}
@@ -628,8 +631,8 @@
}
}
- private boolean transitionGoodToGo(ArraySet<ActivityRecord> apps,
- ArrayMap<ActivityRecord, Integer> outReasons) {
+ private boolean transitionGoodToGo(ArraySet<? extends WindowContainer> apps,
+ ArrayMap<WindowContainer, Integer> outReasons) {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"Checking %d opening apps (frozen=%b timeout=%b)...", apps.size(),
mService.mDisplayFrozen, mDisplayContent.mAppTransition.isTimeout());
@@ -652,13 +655,17 @@
return false;
}
for (int i = 0; i < apps.size(); i++) {
- ActivityRecord activity = apps.valueAt(i);
+ WindowContainer wc = apps.valueAt(i);
+ final ActivityRecord activity = getAppFromContainer(wc);
+ if (activity == null) {
+ continue;
+ }
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
- "Check opening app=%s: allDrawn=%b startingDisplayed=%b "
- + "startingMoved=%b isRelaunching()=%b startingWindow=%s",
- activity, activity.allDrawn, activity.startingDisplayed,
- activity.startingMoved, activity.isRelaunching(),
- activity.startingWindow);
+ "Check opening app=%s: allDrawn=%b startingDisplayed=%b "
+ + "startingMoved=%b isRelaunching()=%b startingWindow=%s",
+ activity, activity.allDrawn, activity.startingDisplayed,
+ activity.startingMoved, activity.isRelaunching(),
+ activity.startingWindow);
final boolean allDrawn = activity.allDrawn && !activity.isRelaunching();
@@ -838,7 +845,7 @@
@VisibleForTesting
boolean isTransitWithinTask(@TransitionType int transit, Task task) {
if (task == null
- || !mDisplayContent.mChangingApps.isEmpty()) {
+ || !mDisplayContent.mChangingContainers.isEmpty()) {
// if there is no task, then we can't constrain to the task.
// if anything is changing, it can animate outside its task.
return false;
@@ -882,12 +889,13 @@
* {@link ActivityRecord#isVisible}.
* @return The top {@link ActivityRecord}.
*/
- private ActivityRecord getTopApp(ArraySet<ActivityRecord> apps, boolean ignoreInvisible) {
+ private ActivityRecord getTopApp(ArraySet<? extends WindowContainer> apps,
+ boolean ignoreInvisible) {
int topPrefixOrderIndex = Integer.MIN_VALUE;
ActivityRecord topApp = null;
for (int i = apps.size() - 1; i >= 0; i--) {
- final ActivityRecord app = apps.valueAt(i);
- if (ignoreInvisible && !app.isVisible()) {
+ final ActivityRecord app = getAppFromContainer(apps.valueAt(i));
+ if (app == null || ignoreInvisible && !app.isVisible()) {
continue;
}
final int prefixOrderIndex = app.getPrefixOrderIndex();
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index e468810..5cd2930 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -91,11 +91,9 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
import static com.android.server.wm.DisplayContentProto.APP_TRANSITION;
-import static com.android.server.wm.DisplayContentProto.CHANGING_APPS;
import static com.android.server.wm.DisplayContentProto.CLOSING_APPS;
import static com.android.server.wm.DisplayContentProto.DISPLAY_FRAMES;
import static com.android.server.wm.DisplayContentProto.DISPLAY_INFO;
-import static com.android.server.wm.DisplayContentProto.DOCKED_STACK_DIVIDER_CONTROLLER;
import static com.android.server.wm.DisplayContentProto.DPI;
import static com.android.server.wm.DisplayContentProto.FOCUSED_APP;
import static com.android.server.wm.DisplayContentProto.FOCUSED_ROOT_TASK_ID;
@@ -132,7 +130,7 @@
import static com.android.server.wm.WindowManagerService.H.REPORT_FOCUS_CHANGE;
import static com.android.server.wm.WindowManagerService.H.REPORT_HARD_KEYBOARD_STATUS_CHANGE;
import static com.android.server.wm.WindowManagerService.H.REPORT_LOSING_FOCUS;
-import static com.android.server.wm.WindowManagerService.H.UPDATE_DOCKED_STACK_DIVIDER;
+import static com.android.server.wm.WindowManagerService.H.UPDATE_MULTI_WINDOW_STACKS;
import static com.android.server.wm.WindowManagerService.H.WINDOW_HIDE_TIMEOUT;
import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD;
import static com.android.server.wm.WindowManagerService.SEAMLESS_ROTATION_TIMEOUT_DURATION;
@@ -314,7 +312,7 @@
final ArraySet<ActivityRecord> mOpeningApps = new ArraySet<>();
final ArraySet<ActivityRecord> mClosingApps = new ArraySet<>();
- final ArraySet<ActivityRecord> mChangingApps = new ArraySet<>();
+ final ArraySet<WindowContainer> mChangingContainers = new ArraySet<>();
final UnknownAppVisibilityController mUnknownAppVisibilityController;
private MetricsLogger mMetricsLogger;
@@ -1006,7 +1004,7 @@
mDisplayPolicy.systemReady();
}
mWindowCornerRadius = mDisplayPolicy.getWindowCornerRadius();
- mDividerControllerLocked = new DockedStackDividerController(mWmService, this);
+ mDividerControllerLocked = new DockedStackDividerController(this);
mPinnedStackControllerLocked = new PinnedStackController(mWmService, this);
final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(mSession)
@@ -2233,12 +2231,6 @@
* for bounds calculations.
*/
void preOnConfigurationChanged() {
- final DockedStackDividerController dividerController = getDockedDividerController();
-
- if (dividerController != null) {
- getDockedDividerController().onConfigurationChanged();
- }
-
final PinnedStackController pinnedStackController = getPinnedStackController();
if (pinnedStackController != null) {
@@ -2622,10 +2614,11 @@
// We also remove the outside touch area for resizing for all freeform
// tasks (including the focused).
// We save the focused task region once we find it, and add it back at the end.
- // If the task is home stack and it is resizable in the minimized state, we want to
- // exclude the docked stack from touch so we need the entire screen area and not just a
+ // If the task is home stack and it is resizable and visible (top of its root task), we want
+ // to exclude the docked stack from touch so we need the entire screen area and not just a
// small portion which the home stack currently is resized to.
- if (task.isActivityTypeHome() && task.getStack().isMinimizedDockAndHomeStackResizable()) {
+ if (task.isActivityTypeHome() && task.isVisible() && task.getStack().getTile() != null
+ && task.isResizeable()) {
mDisplayContent.getBounds(mTmpRect);
} else {
task.getDimBounds(mTmpRect);
@@ -2634,6 +2627,8 @@
if (task == focusedTask) {
// Add the focused task rect back into the exclude region once we are done
// processing stacks.
+ // NOTE: this *looks* like a no-op, but this usage of mTmpRect2 is expected by
+ // updateTouchExcludeRegion.
mTmpRect2.set(mTmpRect);
}
@@ -2695,7 +2690,7 @@
// Clear all transitions & screen frozen states when removing display.
mOpeningApps.clear();
mClosingApps.clear();
- mChangingApps.clear();
+ mChangingContainers.clear();
mUnknownAppVisibilityController.clear();
mAppTransition.removeAppTransitionTimeoutCallbacks();
handleAnimatingStoppedAndTransition();
@@ -2747,58 +2742,6 @@
return mDeferredRemoval;
}
- boolean animateForIme(float interpolatedValue, float animationTarget,
- float dividerAnimationTarget) {
- boolean updated = false;
-
- for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) {
- final ActivityStack stack = mTaskContainers.getChildAt(i);
- if (stack == null || !stack.isAdjustedForIme()) {
- continue;
- }
-
- if (interpolatedValue >= 1f && animationTarget == 0f && dividerAnimationTarget == 0f) {
- stack.resetAdjustedForIme(true /* adjustBoundsNow */);
- updated = true;
- } else {
- mDividerControllerLocked.mLastAnimationProgress =
- mDividerControllerLocked.getInterpolatedAnimationValue(interpolatedValue);
- mDividerControllerLocked.mLastDividerProgress =
- mDividerControllerLocked.getInterpolatedDividerValue(interpolatedValue);
- updated |= stack.updateAdjustForIme(
- mDividerControllerLocked.mLastAnimationProgress,
- mDividerControllerLocked.mLastDividerProgress,
- false /* force */);
- }
- if (interpolatedValue >= 1f) {
- stack.endImeAdjustAnimation();
- }
- }
-
- return updated;
- }
-
- boolean clearImeAdjustAnimation() {
- boolean changed = false;
- for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) {
- final ActivityStack stack = mTaskContainers.getChildAt(i);
- if (stack != null && stack.isAdjustedForIme()) {
- stack.resetAdjustedForIme(true /* adjustBoundsNow */);
- changed = true;
- }
- }
- return changed;
- }
-
- void beginImeAdjustAnimation() {
- for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) {
- final ActivityStack stack = mTaskContainers.getChildAt(i);
- if (stack.isVisible() && stack.isAdjustedForIme()) {
- stack.beginImeAdjustAnimation();
- }
- }
- }
-
void adjustForImeIfNeeded() {
final WindowState imeWin = mInputMethodWindow;
final boolean imeVisible = imeWin != null && imeWin.isVisibleLw()
@@ -2893,7 +2836,6 @@
final ActivityStack stack = mTaskContainers.getChildAt(i);
stack.dumpDebug(proto, TASKS, logLevel);
}
- mDividerControllerLocked.dumpDebug(proto, DOCKED_STACK_DIVIDER_CONTROLLER);
for (int i = mOverlayContainers.getChildCount() - 1; i >= 0; --i) {
final WindowToken windowToken = mOverlayContainers.getChildAt(i);
windowToken.dumpDebug(proto, OVERLAY_WINDOWS, logLevel);
@@ -2916,9 +2858,6 @@
for (int i = mClosingApps.size() - 1; i >= 0; i--) {
mClosingApps.valueAt(i).writeIdentifierToProto(proto, CLOSING_APPS);
}
- for (int i = mChangingApps.size() - 1; i >= 0; i--) {
- mChangingApps.valueAt(i).writeIdentifierToProto(proto, CHANGING_APPS);
- }
proto.write(SINGLE_TASK_INSTANCE, mSingleTaskInstance);
final ActivityStack focusedStack = getFocusedStack();
@@ -3066,8 +3005,6 @@
}
pw.println();
- mDividerControllerLocked.dump(prefix, pw);
- pw.println();
mPinnedStackControllerLocked.dump(prefix, pw);
pw.println();
@@ -3670,7 +3607,7 @@
}
}
- if (!mOpeningApps.isEmpty() || !mClosingApps.isEmpty() || !mChangingApps.isEmpty()) {
+ if (!mOpeningApps.isEmpty() || !mClosingApps.isEmpty() || !mChangingContainers.isEmpty()) {
pw.println();
if (mOpeningApps.size() > 0) {
pw.print(" mOpeningApps="); pw.println(mOpeningApps);
@@ -3678,8 +3615,8 @@
if (mClosingApps.size() > 0) {
pw.print(" mClosingApps="); pw.println(mClosingApps);
}
- if (mChangingApps.size() > 0) {
- pw.print(" mChangingApps="); pw.println(mChangingApps);
+ if (mChangingContainers.size() > 0) {
+ pw.print(" mChangingApps="); pw.println(mChangingContainers);
}
}
@@ -4088,7 +4025,7 @@
mInputMonitor.updateInputWindowsLw(false /*force*/);
}
- mWmService.mH.sendEmptyMessage(UPDATE_DOCKED_STACK_DIVIDER);
+ mWmService.mH.sendEmptyMessage(UPDATE_MULTI_WINDOW_STACKS);
}
/**
@@ -4696,17 +4633,18 @@
@Override
int getOrientation(int candidate) {
if (isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) {
- // Apps and their containers are not allowed to specify an orientation while the
- // docked stack is visible...except for the home stack if the docked stack is
- // minimized and it actually set something and the bounds is different from the
- // display.
+ // Apps and their containers are not allowed to specify an orientation while using
+ // root tasks...except for the home stack if it is not resizable and currently
+ // visible (top of) its root task.
if (mRootHomeTask != null && mRootHomeTask.isVisible()
- && mDividerControllerLocked.isMinimizedDock()
- && !(mDividerControllerLocked.isHomeStackResizable()
- && mRootHomeTask.matchParentBounds())) {
- final int orientation = mRootHomeTask.getOrientation();
- if (orientation != SCREEN_ORIENTATION_UNSET) {
- return orientation;
+ && mRootHomeTask.getTile() != null) {
+ final Task topMost = mRootHomeTask.getTopMostTask();
+ final boolean resizable = topMost == null && topMost.isResizeable();
+ if (!(resizable && mRootHomeTask.matchParentBounds())) {
+ final int orientation = mRootHomeTask.getOrientation();
+ if (orientation != SCREEN_ORIENTATION_UNSET) {
+ return orientation;
+ }
}
}
return SCREEN_ORIENTATION_UNSPECIFIED;
@@ -6171,8 +6109,6 @@
t.removeAllChildren();
}
}
- mDividerControllerLocked.setMinimizedDockedStack(false /* minimized */,
- false /* animate */);
} finally {
final ActivityStack topFullscreenStack =
getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index f02a9dd..497c473 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1178,8 +1178,6 @@
return R.anim.dock_left_enter;
}
}
- } else if (win.getAttrs().type == TYPE_DOCK_DIVIDER) {
- return selectDockedDividerAnimation(win, transit);
}
if (transit == TRANSIT_PREVIEW_DONE) {
@@ -1204,36 +1202,6 @@
return ANIMATION_STYLEABLE;
}
- private int selectDockedDividerAnimation(WindowState win, int transit) {
- int insets = mDisplayContent.getDockedDividerController().getContentInsets();
-
- // If the divider is behind the navigation bar, don't animate.
- final Rect frame = win.getFrameLw();
- final boolean behindNavBar = mNavigationBar != null
- && ((mNavigationBarPosition == NAV_BAR_BOTTOM
- && frame.top + insets >= mNavigationBar.getFrameLw().top)
- || (mNavigationBarPosition == NAV_BAR_RIGHT
- && frame.left + insets >= mNavigationBar.getFrameLw().left)
- || (mNavigationBarPosition == NAV_BAR_LEFT
- && frame.right - insets <= mNavigationBar.getFrameLw().right));
- final boolean landscape = frame.height() > frame.width();
- final boolean offscreenLandscape = landscape && (frame.right - insets <= 0
- || frame.left + insets >= win.getDisplayFrameLw().right);
- final boolean offscreenPortrait = !landscape && (frame.top - insets <= 0
- || frame.bottom + insets >= win.getDisplayFrameLw().bottom);
- final boolean offscreen = offscreenLandscape || offscreenPortrait;
- if (behindNavBar || offscreen) {
- return ANIMATION_STYLEABLE;
- }
- if (transit == TRANSIT_ENTER || transit == TRANSIT_SHOW) {
- return R.anim.fade_in;
- } else if (transit == TRANSIT_EXIT) {
- return R.anim.fade_out;
- } else {
- return ANIMATION_STYLEABLE;
- }
- }
-
/**
* Called when a new system UI visibility is being reported, allowing
* the policy to adjust what is actually reported.
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 958c8ae..20738ed 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -16,330 +16,26 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-import static android.view.Surface.ROTATION_270;
-import static android.view.Surface.ROTATION_90;
-import static android.view.WindowManager.DOCKED_BOTTOM;
-import static android.view.WindowManager.DOCKED_INVALID;
-import static android.view.WindowManager.DOCKED_LEFT;
-import static android.view.WindowManager.DOCKED_RIGHT;
-import static android.view.WindowManager.DOCKED_TOP;
-import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
-
-import static com.android.server.wm.AppTransition.DEFAULT_APP_TRANSITION_DURATION;
-import static com.android.server.wm.AppTransition.TOUCH_RESPONSE_INTERPOLATOR;
-import static com.android.server.wm.DockedStackDividerControllerProto.MINIMIZED_DOCK;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
-import android.content.Context;
-import android.content.res.Configuration;
import android.graphics.Rect;
-import android.os.RemoteCallbackList;
-import android.os.RemoteException;
-import android.util.ArraySet;
-import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
-import android.view.DisplayCutout;
-import android.view.DisplayInfo;
-import android.view.IDockedStackListener;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-import android.view.animation.PathInterpolator;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.inputmethod.SoftInputShowHideReason;
-import com.android.internal.policy.DividerSnapAlgorithm;
-import com.android.internal.policy.DockedDividerUtils;
-import com.android.server.LocalServices;
-import com.android.server.inputmethod.InputMethodManagerInternal;
-import com.android.server.wm.WindowManagerService.H;
-
-import java.io.PrintWriter;
/**
* Keeps information about the docked stack divider.
*/
public class DockedStackDividerController {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "DockedStackDividerController" : TAG_WM;
-
- /**
- * The fraction during the maximize/clip reveal animation the divider meets the edge of the clip
- * revealing surface at the earliest.
- */
- private static final float CLIP_REVEAL_MEET_EARLIEST = 0.6f;
-
- /**
- * The fraction during the maximize/clip reveal animation the divider meets the edge of the clip
- * revealing surface at the latest.
- */
- private static final float CLIP_REVEAL_MEET_LAST = 1f;
-
- /**
- * If the app translates at least CLIP_REVEAL_MEET_FRACTION_MIN * minimize distance, we start
- * meet somewhere between {@link #CLIP_REVEAL_MEET_LAST} and {@link #CLIP_REVEAL_MEET_EARLIEST}.
- */
- private static final float CLIP_REVEAL_MEET_FRACTION_MIN = 0.4f;
-
- /**
- * If the app translates equals or more than CLIP_REVEAL_MEET_FRACTION_MIN * minimize distance,
- * we meet at {@link #CLIP_REVEAL_MEET_EARLIEST}.
- */
- private static final float CLIP_REVEAL_MEET_FRACTION_MAX = 0.8f;
-
- private static final Interpolator IME_ADJUST_ENTRY_INTERPOLATOR =
- new PathInterpolator(0.2f, 0f, 0.1f, 1f);
-
- private static final long IME_ADJUST_ANIM_DURATION = 280;
-
- private static final long IME_ADJUST_DRAWN_TIMEOUT = 200;
-
- private static final int DIVIDER_WIDTH_INACTIVE_DP = 4;
-
- private final WindowManagerService mService;
private final DisplayContent mDisplayContent;
- private int mDividerWindowWidth;
- private int mDividerWindowWidthInactive;
- private int mDividerInsets;
- private int mTaskHeightInMinimizedMode;
private boolean mResizing;
- private WindowState mWindow;
- private final Rect mTmpRect = new Rect();
- private final Rect mTmpRect2 = new Rect();
- private final Rect mTmpRect3 = new Rect();
- private final Rect mLastRect = new Rect();
- private boolean mLastVisibility = false;
- private final RemoteCallbackList<IDockedStackListener> mDockedStackListeners
- = new RemoteCallbackList<>();
- private boolean mMinimizedDock;
- private int mOriginalDockedSide = DOCKED_INVALID;
- private boolean mAnimatingForMinimizedDockedStack;
- private boolean mAnimationStarted;
- private long mAnimationStartTime;
- private float mAnimationStart;
- private float mAnimationTarget;
- private long mAnimationDuration;
- private boolean mAnimationStartDelayed;
- private final Interpolator mMinimizedDockInterpolator;
- private float mMaximizeMeetFraction;
private final Rect mTouchRegion = new Rect();
- private boolean mAnimatingForIme;
- private boolean mAdjustedForIme;
- private int mImeHeight;
- private WindowState mDelayedImeWin;
- private boolean mAdjustedForDivider;
- private float mDividerAnimationStart;
- private float mDividerAnimationTarget;
- float mLastAnimationProgress;
- float mLastDividerProgress;
- private final DividerSnapAlgorithm[] mSnapAlgorithmForRotation = new DividerSnapAlgorithm[4];
- private boolean mImeHideRequested;
- private ActivityStack mDimmedStack;
- DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) {
- mService = service;
+ DockedStackDividerController(DisplayContent displayContent) {
mDisplayContent = displayContent;
- final Context context = service.mContext;
- mMinimizedDockInterpolator = AnimationUtils.loadInterpolator(
- context, android.R.interpolator.fast_out_slow_in);
- loadDimens();
- }
-
- int getSmallestWidthDpForBounds(Rect bounds) {
- final DisplayInfo di = mDisplayContent.getDisplayInfo();
-
- final int baseDisplayWidth = mDisplayContent.mBaseDisplayWidth;
- final int baseDisplayHeight = mDisplayContent.mBaseDisplayHeight;
- int minWidth = Integer.MAX_VALUE;
-
- // Go through all screen orientations and find the orientation in which the task has the
- // smallest width.
- for (int rotation = 0; rotation < 4; rotation++) {
- mTmpRect.set(bounds);
- mDisplayContent.rotateBounds(di.rotation, rotation, mTmpRect);
- final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
- mTmpRect2.set(0, 0,
- rotated ? baseDisplayHeight : baseDisplayWidth,
- rotated ? baseDisplayWidth : baseDisplayHeight);
- final int orientation = mTmpRect2.width() <= mTmpRect2.height()
- ? ORIENTATION_PORTRAIT
- : ORIENTATION_LANDSCAPE;
- final int dockSide = getDockSide(mTmpRect, mTmpRect2, orientation, rotation);
- final int position = DockedDividerUtils.calculatePositionForBounds(mTmpRect, dockSide,
- getContentWidth());
-
- final DisplayCutout displayCutout = mDisplayContent.calculateDisplayCutoutForRotation(
- rotation).getDisplayCutout();
-
- // Since we only care about feasible states, snap to the closest snap target, like it
- // would happen when actually rotating the screen.
- final int snappedPosition = mSnapAlgorithmForRotation[rotation]
- .calculateNonDismissingSnapTarget(position).position;
- DockedDividerUtils.calculateBoundsForPosition(snappedPosition, dockSide, mTmpRect,
- mTmpRect2.width(), mTmpRect2.height(), getContentWidth());
- mDisplayContent.getDisplayPolicy().getStableInsetsLw(rotation, mTmpRect2.width(),
- mTmpRect2.height(), displayCutout, mTmpRect3);
- mService.intersectDisplayInsetBounds(mTmpRect2, mTmpRect3, mTmpRect);
- minWidth = Math.min(mTmpRect.width(), minWidth);
- }
- return (int) (minWidth / mDisplayContent.getDisplayMetrics().density);
- }
-
- /**
- * Get the current docked side. Determined by its location of {@param bounds} within
- * {@param displayRect} but if both are the same, it will try to dock to each side and determine
- * if allowed in its respected {@param orientation}.
- *
- * @param bounds bounds of the docked task to get which side is docked
- * @param displayRect bounds of the display that contains the docked task
- * @param orientation the origination of device
- * @return current docked side
- */
- int getDockSide(Rect bounds, Rect displayRect, int orientation, int rotation) {
- if (orientation == Configuration.ORIENTATION_PORTRAIT) {
- // Portrait mode, docked either at the top or the bottom.
- final int diff = (displayRect.bottom - bounds.bottom) - (bounds.top - displayRect.top);
- if (diff > 0) {
- return DOCKED_TOP;
- } else if (diff < 0) {
- return DOCKED_BOTTOM;
- }
- return canPrimaryStackDockTo(DOCKED_TOP, displayRect, rotation)
- ? DOCKED_TOP : DOCKED_BOTTOM;
- } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
- // Landscape mode, docked either on the left or on the right.
- final int diff = (displayRect.right - bounds.right) - (bounds.left - displayRect.left);
- if (diff > 0) {
- return DOCKED_LEFT;
- } else if (diff < 0) {
- return DOCKED_RIGHT;
- }
- return canPrimaryStackDockTo(DOCKED_LEFT, displayRect, rotation)
- ? DOCKED_LEFT : DOCKED_RIGHT;
- }
- return DOCKED_INVALID;
- }
-
- void getHomeStackBoundsInDockedMode(Configuration parentConfig, int dockSide, Rect outBounds) {
- final DisplayCutout displayCutout = mDisplayContent.getDisplayInfo().displayCutout;
- final int displayWidth = parentConfig.windowConfiguration.getBounds().width();
- final int displayHeight = parentConfig.windowConfiguration.getBounds().height();
- mDisplayContent.getDisplayPolicy().getStableInsetsLw(
- parentConfig.windowConfiguration.getRotation(), displayWidth, displayHeight,
- displayCutout, mTmpRect);
- int dividerSize = mDividerWindowWidth - 2 * mDividerInsets;
- // The offset in the left (landscape)/top (portrait) is calculated with the minimized
- // offset value with the divider size and any system insets in that direction.
- if (parentConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
- outBounds.set(0, mTaskHeightInMinimizedMode + dividerSize + mTmpRect.top,
- displayWidth, displayHeight);
- } else {
- // In landscape also inset the left/right side with the status bar height to match the
- // minimized size height in portrait mode.
- final int primaryTaskWidth = mTaskHeightInMinimizedMode + dividerSize + mTmpRect.top;
- int left = mTmpRect.left;
- int right = displayWidth - mTmpRect.right;
- if (dockSide == DOCKED_LEFT) {
- left += primaryTaskWidth;
- } else if (dockSide == DOCKED_RIGHT) {
- right -= primaryTaskWidth;
- }
- outBounds.set(left, 0, right, displayHeight);
- }
- }
-
- boolean isHomeStackResizable() {
- final ActivityStack homeStack = mDisplayContent.getRootHomeTask();
- if (homeStack == null) {
- return false;
- }
- final Task homeTask = homeStack.getTopMostTask();
- return homeTask != null && homeTask.isResizeable();
- }
-
- private void initSnapAlgorithmForRotations() {
- final Configuration baseConfig = mDisplayContent.getConfiguration();
-
- // Initialize the snap algorithms for all 4 screen orientations.
- final Configuration config = new Configuration();
- for (int rotation = 0; rotation < 4; rotation++) {
- final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
- final int dw = rotated
- ? mDisplayContent.mBaseDisplayHeight
- : mDisplayContent.mBaseDisplayWidth;
- final int dh = rotated
- ? mDisplayContent.mBaseDisplayWidth
- : mDisplayContent.mBaseDisplayHeight;
- final DisplayCutout displayCutout =
- mDisplayContent.calculateDisplayCutoutForRotation(rotation).getDisplayCutout();
- final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
- displayPolicy.getStableInsetsLw(rotation, dw, dh, displayCutout, mTmpRect);
- config.unset();
- config.orientation = (dw <= dh) ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
-
- final int appWidth = displayPolicy.getNonDecorDisplayWidth(dw, dh, rotation,
- baseConfig.uiMode, displayCutout);
- final int appHeight = displayPolicy.getNonDecorDisplayHeight(dw, dh, rotation,
- baseConfig.uiMode, displayCutout);
- displayPolicy.getNonDecorInsetsLw(rotation, dw, dh, displayCutout, mTmpRect);
- final int leftInset = mTmpRect.left;
- final int topInset = mTmpRect.top;
-
- config.windowConfiguration.setAppBounds(leftInset /*left*/, topInset /*top*/,
- leftInset + appWidth /*right*/, topInset + appHeight /*bottom*/);
-
- final float density = mDisplayContent.getDisplayMetrics().density;
- config.screenWidthDp = (int) (displayPolicy.getConfigDisplayWidth(dw, dh, rotation,
- baseConfig.uiMode, displayCutout) / density);
- config.screenHeightDp = (int) (displayPolicy.getConfigDisplayHeight(dw, dh, rotation,
- baseConfig.uiMode, displayCutout) / density);
- final Context rotationContext = mService.mContext.createConfigurationContext(config);
- mSnapAlgorithmForRotation[rotation] = new DividerSnapAlgorithm(
- rotationContext.getResources(), dw, dh, getContentWidth(),
- config.orientation == ORIENTATION_PORTRAIT, mTmpRect);
- }
- }
-
- private void loadDimens() {
- final Context context = mService.mContext;
- mDividerWindowWidth = context.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.docked_stack_divider_thickness);
- mDividerInsets = context.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.docked_stack_divider_insets);
- mDividerWindowWidthInactive = WindowManagerService.dipToPixel(
- DIVIDER_WIDTH_INACTIVE_DP, mDisplayContent.getDisplayMetrics());
- mTaskHeightInMinimizedMode = context.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.task_height_of_minimized_mode);
- initSnapAlgorithmForRotations();
- }
-
- void onConfigurationChanged() {
- loadDimens();
}
boolean isResizing() {
return mResizing;
}
- int getContentWidth() {
- return mDividerWindowWidth - 2 * mDividerInsets;
- }
-
- int getContentInsets() {
- return mDividerInsets;
- }
-
- int getContentWidthInactive() {
- return mDividerWindowWidthInactive;
- }
-
void setResizing(boolean resizing) {
if (mResizing != resizing) {
mResizing = resizing;
@@ -353,676 +49,10 @@
void getTouchRegion(Rect outRegion) {
outRegion.set(mTouchRegion);
- if (mWindow != null) {
- outRegion.offset(mWindow.getFrameLw().left, mWindow.getFrameLw().top);
- }
}
private void resetDragResizingChangeReported() {
mDisplayContent.forAllWindows(WindowState::resetDragResizingChangeReported,
true /* traverseTopToBottom */ );
}
-
- void setWindow(WindowState window) {
- mWindow = window;
- reevaluateVisibility(false);
- }
-
- void reevaluateVisibility(boolean force) {
- if (mWindow == null) {
- return;
- }
- ActivityStack stack = mDisplayContent.getRootSplitScreenPrimaryTask();
-
- // If the stack is invisible, we policy force hide it in WindowAnimator.shouldForceHide
- final boolean visible = stack != null;
- if (mLastVisibility == visible && !force) {
- return;
- }
- mLastVisibility = visible;
- notifyDockedDividerVisibilityChanged(visible);
- if (!visible) {
- setResizeDimLayer(false, WINDOWING_MODE_UNDEFINED, 0f);
- }
- }
-
- private boolean wasVisible() {
- return mLastVisibility;
- }
-
- void setAdjustedForIme(
- boolean adjustedForIme, boolean adjustedForDivider,
- boolean animate, WindowState imeWin, int imeHeight) {
- if (mAdjustedForIme != adjustedForIme || (adjustedForIme && mImeHeight != imeHeight)
- || mAdjustedForDivider != adjustedForDivider) {
- if (animate && !mAnimatingForMinimizedDockedStack) {
- // Notify SystemUI to set the target docked stack size according current docked
- // state without animation when calling startImeAdjustAnimation.
- notifyDockedStackMinimizedChanged(mMinimizedDock, false /* animate */,
- isHomeStackResizable());
- startImeAdjustAnimation(adjustedForIme, adjustedForDivider, imeWin);
- } else {
- // Animation might be delayed, so only notify if we don't run an animation.
- notifyAdjustedForImeChanged(adjustedForIme || adjustedForDivider, 0 /* duration */);
- }
- mAdjustedForIme = adjustedForIme;
- mImeHeight = imeHeight;
- mAdjustedForDivider = adjustedForDivider;
- }
- }
-
- int getImeHeightAdjustedFor() {
- return mImeHeight;
- }
-
- void positionDockedStackedDivider(Rect frame) {
- ActivityStack stack = mDisplayContent.getRootSplitScreenPrimaryTask();
- if (stack == null) {
- // Unfortunately we might end up with still having a divider, even though the underlying
- // stack was already removed. This is because we are on AM thread and the removal of the
- // divider was deferred to WM thread and hasn't happened yet. In that case let's just
- // keep putting it in the same place it was before the stack was removed to have
- // continuity and prevent it from jumping to the center. It will get hidden soon.
- frame.set(mLastRect);
- return;
- } else {
- stack.getDimBounds(mTmpRect);
- }
- int side = stack.getDockSide();
- switch (side) {
- case DOCKED_LEFT:
- frame.set(mTmpRect.right - mDividerInsets, frame.top,
- mTmpRect.right + frame.width() - mDividerInsets, frame.bottom);
- break;
- case DOCKED_TOP:
- frame.set(frame.left, mTmpRect.bottom - mDividerInsets,
- mTmpRect.right, mTmpRect.bottom + frame.height() - mDividerInsets);
- break;
- case DOCKED_RIGHT:
- frame.set(mTmpRect.left - frame.width() + mDividerInsets, frame.top,
- mTmpRect.left + mDividerInsets, frame.bottom);
- break;
- case DOCKED_BOTTOM:
- frame.set(frame.left, mTmpRect.top - frame.height() + mDividerInsets,
- frame.right, mTmpRect.top + mDividerInsets);
- break;
- }
- mLastRect.set(frame);
- }
-
- private void notifyDockedDividerVisibilityChanged(boolean visible) {
- final int size = mDockedStackListeners.beginBroadcast();
- for (int i = 0; i < size; ++i) {
- final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
- try {
- listener.onDividerVisibilityChanged(visible);
- } catch (RemoteException e) {
- Slog.e(TAG_WM, "Error delivering divider visibility changed event.", e);
- }
- }
- mDockedStackListeners.finishBroadcast();
- }
-
- /**
- * Checks if the primary stack is allowed to dock to a specific side based on its original dock
- * side.
- *
- * @param dockSide the side to see if it is valid
- * @return true if the side provided is valid
- */
- boolean canPrimaryStackDockTo(int dockSide, Rect parentRect, int rotation) {
- final DisplayPolicy policy = mDisplayContent.getDisplayPolicy();
- return isDockSideAllowed(dockSide, mOriginalDockedSide,
- policy.navigationBarPosition(parentRect.width(), parentRect.height(), rotation),
- policy.navigationBarCanMove());
- }
-
- @VisibleForTesting
- static boolean isDockSideAllowed(int dockSide, int originalDockSide, int navBarPosition,
- boolean navigationBarCanMove) {
- if (dockSide == DOCKED_TOP) {
- return true;
- }
-
- if (navigationBarCanMove) {
- // Only allow the dockside opposite to the nav bar position in landscape
- return dockSide == DOCKED_LEFT && navBarPosition == NAV_BAR_RIGHT
- || dockSide == DOCKED_RIGHT && navBarPosition == NAV_BAR_LEFT;
- }
-
- // Side is the same as original side
- if (dockSide == originalDockSide) {
- return true;
- }
-
- // Only if original docked side was top in portrait will allow left for landscape
- return dockSide == DOCKED_LEFT && originalDockSide == DOCKED_TOP;
- }
-
- void notifyDockedStackExistsChanged(boolean exists) {
- // TODO(multi-display): Perform all actions only for current display.
- final int size = mDockedStackListeners.beginBroadcast();
- for (int i = 0; i < size; ++i) {
- final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
- try {
- listener.onDockedStackExistsChanged(exists);
- } catch (RemoteException e) {
- Slog.e(TAG_WM, "Error delivering docked stack exists changed event.", e);
- }
- }
- mDockedStackListeners.finishBroadcast();
- if (exists) {
- InputMethodManagerInternal inputMethodManagerInternal =
- LocalServices.getService(InputMethodManagerInternal.class);
- if (inputMethodManagerInternal != null) {
-
- // Hide the current IME to avoid problems with animations from IME adjustment when
- // attaching the docked stack.
- inputMethodManagerInternal.hideCurrentInputMethod(
- SoftInputShowHideReason.HIDE_DOCKED_STACK_ATTACHED);
- mImeHideRequested = true;
- }
-
- // If a primary stack was just created, it will not have access to display content at
- // this point so pass it from here to get a valid dock side.
- final ActivityStack stack =
- mDisplayContent.getRootSplitScreenPrimaryTask();
- mOriginalDockedSide = stack.getDockSideForDisplay(mDisplayContent);
- return;
- }
- mOriginalDockedSide = DOCKED_INVALID;
- setMinimizedDockedStack(false /* minimizedDock */, false /* animate */);
-
- if (mDimmedStack != null) {
- mDimmedStack.stopDimming();
- mDimmedStack = null;
- }
- }
-
- /**
- * Resets the state that IME hide has been requested. See {@link #isImeHideRequested}.
- */
- void resetImeHideRequested() {
- mImeHideRequested = false;
- }
-
- /**
- * The docked stack divider controller makes sure the IME gets hidden when attaching the docked
- * stack, to avoid animation problems. This flag indicates whether the request to hide the IME
- * has been sent in an asynchronous manner, and the IME should be treated as hidden already.
- *
- * @return whether IME hide request has been sent
- */
- boolean isImeHideRequested() {
- return mImeHideRequested;
- }
-
- private void notifyDockedStackMinimizedChanged(boolean minimizedDock, boolean animate,
- boolean isHomeStackResizable) {
- long animDuration = 0;
- if (animate) {
- final ActivityStack stack =
- mDisplayContent.getRootSplitScreenPrimaryTask();
- final long transitionDuration = isAnimationMaximizing()
- ? mDisplayContent.mAppTransition.getLastClipRevealTransitionDuration()
- : DEFAULT_APP_TRANSITION_DURATION;
- mAnimationDuration = (long)
- (transitionDuration * mService.getTransitionAnimationScaleLocked());
- mMaximizeMeetFraction = getClipRevealMeetFraction(stack);
- animDuration = (long) (mAnimationDuration * mMaximizeMeetFraction);
- }
- final int size = mDockedStackListeners.beginBroadcast();
- for (int i = 0; i < size; ++i) {
- final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
- try {
- listener.onDockedStackMinimizedChanged(minimizedDock, animDuration,
- isHomeStackResizable);
- } catch (RemoteException e) {
- Slog.e(TAG_WM, "Error delivering minimized dock changed event.", e);
- }
- }
- mDockedStackListeners.finishBroadcast();
- // Only notify ATM after we update the remote listeners, otherwise it may trigger another
- // minimize change, which would lead to an inversion of states send to the listeners
- mService.mAtmInternal.notifyDockedStackMinimizedChanged(minimizedDock);
- }
-
- void notifyDockSideChanged(int newDockSide) {
- final int size = mDockedStackListeners.beginBroadcast();
- for (int i = 0; i < size; ++i) {
- final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
- try {
- listener.onDockSideChanged(newDockSide);
- } catch (RemoteException e) {
- Slog.e(TAG_WM, "Error delivering dock side changed event.", e);
- }
- }
- mDockedStackListeners.finishBroadcast();
- }
-
- private void notifyAdjustedForImeChanged(boolean adjustedForIme, long animDuration) {
- final int size = mDockedStackListeners.beginBroadcast();
- for (int i = 0; i < size; ++i) {
- final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
- try {
- listener.onAdjustedForImeChanged(adjustedForIme, animDuration);
- } catch (RemoteException e) {
- Slog.e(TAG_WM, "Error delivering adjusted for ime changed event.", e);
- }
- }
- mDockedStackListeners.finishBroadcast();
- }
-
- void registerDockedStackListener(IDockedStackListener listener) {
- mDockedStackListeners.register(listener);
- notifyDockedDividerVisibilityChanged(wasVisible());
- notifyDockedStackExistsChanged(
- mDisplayContent.getRootSplitScreenPrimaryTask() != null);
- notifyDockedStackMinimizedChanged(mMinimizedDock, false /* animate */,
- isHomeStackResizable());
- notifyAdjustedForImeChanged(mAdjustedForIme, 0 /* animDuration */);
-
- }
-
- /**
- * Shows a dim layer with {@param alpha} if {@param visible} is true and
- * {@param targetWindowingMode} isn't
- * {@link android.app.WindowConfiguration#WINDOWING_MODE_UNDEFINED} and there is a stack on the
- * display in that windowing mode.
- */
- void setResizeDimLayer(boolean visible, int targetWindowingMode, float alpha) {
- // TODO: Maybe only allow split-screen windowing modes?
- final ActivityStack stack = targetWindowingMode != WINDOWING_MODE_UNDEFINED
- ? mDisplayContent.getTopStackInWindowingMode(targetWindowingMode)
- : null;
- final ActivityStack dockedStack = mDisplayContent.getRootSplitScreenPrimaryTask();
- boolean visibleAndValid = visible && stack != null && dockedStack != null;
-
- // Ensure an old dim that was shown for the docked stack divider is removed so we don't end
- // up with dim layers that can no longer be removed.
- if (mDimmedStack != null && mDimmedStack != stack) {
- mDimmedStack.stopDimming();
- mDimmedStack = null;
- }
-
- if (visibleAndValid) {
- mDimmedStack = stack;
- stack.dim(alpha);
- }
- if (!visibleAndValid && stack != null) {
- mDimmedStack = null;
- stack.stopDimming();
- }
- }
-
- /**
- * Notifies the docked stack divider controller of a visibility change that happens without
- * an animation.
- */
- void notifyAppVisibilityChanged() {
- checkMinimizeChanged(false /* animate */);
- }
-
- void notifyAppTransitionStarting(ArraySet<ActivityRecord> openingApps, int appTransition) {
- final boolean wasMinimized = mMinimizedDock;
- checkMinimizeChanged(true /* animate */);
-
- // We were minimized, and now we are still minimized, but somebody is trying to launch an
- // app in docked stack, better show recent apps so we actually get unminimized! However do
- // not do this if keyguard is dismissed such as when the device is unlocking. This catches
- // any case that was missed in ActivityStarter.postStartActivityUncheckedProcessing because
- // we couldn't retrace the launch of the app in the docked stack to the launch from
- // homescreen.
- if (wasMinimized && mMinimizedDock && containsAppInDockedStack(openingApps)
- && appTransition != TRANSIT_NONE &&
- !AppTransition.isKeyguardGoingAwayTransit(appTransition)) {
- if (mService.mAtmInternal.isRecentsComponentHomeActivity(mService.mCurrentUserId)) {
- // When the home activity is the recents component and we are already minimized,
- // then there is nothing to do here since home is already visible
- } else {
- mService.showRecentApps();
- }
- }
- }
-
- /**
- * @return true if {@param apps} contains an activity in the docked stack, false otherwise.
- */
- private boolean containsAppInDockedStack(ArraySet<ActivityRecord> apps) {
- for (int i = apps.size() - 1; i >= 0; i--) {
- final ActivityRecord activity = apps.valueAt(i);
- if (activity.getTask() != null && activity.inSplitScreenPrimaryWindowingMode()) {
- return true;
- }
- }
- return false;
- }
-
- boolean isMinimizedDock() {
- return mMinimizedDock;
- }
-
- void checkMinimizeChanged(boolean animate) {
- if (mDisplayContent.getRootSplitScreenPrimaryTask() == null) {
- return;
- }
- final ActivityStack homeStack = mDisplayContent.getRootHomeTask();
- if (homeStack == null) {
- return;
- }
- final Task homeTask = homeStack.getTopMostTask();
- if (homeTask == null || !isWithinDisplay(homeTask)) {
- return;
- }
-
- // Do not minimize when dock is already minimized while keyguard is showing and not
- // occluded such as unlocking the screen
- if (mMinimizedDock && mService.mKeyguardOrAodShowingOnDefaultDisplay) {
- return;
- }
- final ActivityStack topSecondaryStack = mDisplayContent.getTopStackInWindowingMode(
- WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
- final RecentsAnimationController recentsAnim = mService.getRecentsAnimationController();
- final boolean minimizedForRecentsAnimation = recentsAnim != null &&
- recentsAnim.isSplitScreenMinimized();
- boolean homeVisible = homeTask.getTopVisibleActivity() != null;
- if (homeVisible && topSecondaryStack != null) {
- // Home should only be considered visible if it is greater or equal to the top secondary
- // stack in terms of z-order.
- homeVisible = homeStack.compareTo(topSecondaryStack) >= 0;
- }
- setMinimizedDockedStack(homeVisible || minimizedForRecentsAnimation, animate);
- }
-
- private boolean isWithinDisplay(Task task) {
- task.getBounds(mTmpRect);
- mDisplayContent.getBounds(mTmpRect2);
- return mTmpRect.intersect(mTmpRect2);
- }
-
- /**
- * Sets whether the docked stack is currently in a minimized state, i.e. all the tasks in the
- * docked stack are heavily clipped so you can only see a minimal peek state.
- *
- * @param minimizedDock Whether the docked stack is currently minimized.
- * @param animate Whether to animate the change.
- */
- void setMinimizedDockedStack(boolean minimizedDock, boolean animate) {
- final boolean wasMinimized = mMinimizedDock;
- mMinimizedDock = minimizedDock;
- if (minimizedDock == wasMinimized) {
- return;
- }
-
- final boolean imeChanged = clearImeAdjustAnimation();
- boolean minimizedChange = false;
- if (isHomeStackResizable()) {
- notifyDockedStackMinimizedChanged(minimizedDock, animate,
- true /* isHomeStackResizable */);
- minimizedChange = true;
- } else {
- if (minimizedDock) {
- if (animate) {
- startAdjustAnimation(0f, 1f);
- } else {
- minimizedChange |= setMinimizedDockedStack(true);
- }
- } else {
- if (animate) {
- startAdjustAnimation(1f, 0f);
- } else {
- minimizedChange |= setMinimizedDockedStack(false);
- }
- }
- }
- if (imeChanged || minimizedChange) {
- if (imeChanged && !minimizedChange) {
- Slog.d(TAG, "setMinimizedDockedStack: IME adjust changed due to minimizing,"
- + " minimizedDock=" + minimizedDock
- + " minimizedChange=" + minimizedChange);
- }
- mService.mWindowPlacerLocked.performSurfacePlacement();
- }
- }
-
- private boolean clearImeAdjustAnimation() {
- final boolean changed = mDisplayContent.clearImeAdjustAnimation();
- mAnimatingForIme = false;
- return changed;
- }
-
- private void startAdjustAnimation(float from, float to) {
- mAnimatingForMinimizedDockedStack = true;
- mAnimationStarted = false;
- mAnimationStart = from;
- mAnimationTarget = to;
- }
-
- private void startImeAdjustAnimation(
- boolean adjustedForIme, boolean adjustedForDivider, WindowState imeWin) {
-
- // If we're not in an animation, the starting point depends on whether we're adjusted
- // or not. If we're already in an animation, we start from where the current animation
- // left off, so that the motion doesn't look discontinuous.
- if (!mAnimatingForIme) {
- mAnimationStart = mAdjustedForIme ? 1 : 0;
- mDividerAnimationStart = mAdjustedForDivider ? 1 : 0;
- mLastAnimationProgress = mAnimationStart;
- mLastDividerProgress = mDividerAnimationStart;
- } else {
- mAnimationStart = mLastAnimationProgress;
- mDividerAnimationStart = mLastDividerProgress;
- }
- mAnimatingForIme = true;
- mAnimationStarted = false;
- mAnimationTarget = adjustedForIme ? 1 : 0;
- mDividerAnimationTarget = adjustedForDivider ? 1 : 0;
-
- mDisplayContent.beginImeAdjustAnimation();
-
- // We put all tasks into drag resizing mode - wait until all of them have completed the
- // drag resizing switch.
- final Runnable existingWaitingForDrwanCallback =
- mService.mWaitingForDrawnCallbacks.get(mService.mRoot);
- if (existingWaitingForDrwanCallback != null) {
- mService.mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, mService.mRoot);
- mService.mH.sendMessageDelayed(mService.mH.obtainMessage(H.WAITING_FOR_DRAWN_TIMEOUT,
- mService.mRoot),
- IME_ADJUST_DRAWN_TIMEOUT);
- mAnimationStartDelayed = true;
- if (imeWin != null) {
-
- // There might be an old window delaying the animation start - clear it.
- if (mDelayedImeWin != null) {
- mDelayedImeWin.endDelayingAnimationStart();
- }
- mDelayedImeWin = imeWin;
- imeWin.startDelayingAnimationStart();
- }
-
- // If we are already waiting for something to be drawn, clear out the old one so it
- // still gets executed.
- // TODO: Have a real system where we can wait on different windows to be drawn with
- // different callbacks.
- existingWaitingForDrwanCallback.run();
- mService.mWaitingForDrawnCallbacks.put(mService.mRoot, () -> {
- synchronized (mService.mGlobalLock) {
- mAnimationStartDelayed = false;
- if (mDelayedImeWin != null) {
- mDelayedImeWin.endDelayingAnimationStart();
- }
- // If the adjust status changed since this was posted, only notify
- // the new states and don't animate.
- long duration = 0;
- if (mAdjustedForIme == adjustedForIme
- && mAdjustedForDivider == adjustedForDivider) {
- duration = IME_ADJUST_ANIM_DURATION;
- } else {
- Slog.w(TAG, "IME adjust changed while waiting for drawn:"
- + " adjustedForIme=" + adjustedForIme
- + " adjustedForDivider=" + adjustedForDivider
- + " mAdjustedForIme=" + mAdjustedForIme
- + " mAdjustedForDivider=" + mAdjustedForDivider);
- }
- notifyAdjustedForImeChanged(
- mAdjustedForIme || mAdjustedForDivider, duration);
- }
- });
- } else {
- notifyAdjustedForImeChanged(
- adjustedForIme || adjustedForDivider, IME_ADJUST_ANIM_DURATION);
- }
- }
-
- private boolean setMinimizedDockedStack(boolean minimized) {
- final ActivityStack stack = mDisplayContent.getRootSplitScreenPrimaryTask();
- notifyDockedStackMinimizedChanged(minimized, false /* animate */, isHomeStackResizable());
- return stack != null && stack.setAdjustedForMinimizedDock(minimized ? 1f : 0f);
- }
-
- private boolean isAnimationMaximizing() {
- return mAnimationTarget == 0f;
- }
-
- public boolean animate(long now) {
- if (mWindow == null) {
- return false;
- }
- if (mAnimatingForMinimizedDockedStack) {
- return animateForMinimizedDockedStack(now);
- } else if (mAnimatingForIme && !mDisplayContent.mAppTransition.isRunning()) {
- // To prevent task stack resize animation may flicking when playing app transition
- // animation & IME window enter animation in parallel, make sure app transition is done
- // and then start to animate for IME.
- return animateForIme(now);
- }
- return false;
- }
-
- private boolean animateForIme(long now) {
- if (!mAnimationStarted || mAnimationStartDelayed) {
- mAnimationStarted = true;
- mAnimationStartTime = now;
- mAnimationDuration = (long)
- (IME_ADJUST_ANIM_DURATION * mService.getWindowAnimationScaleLocked());
- }
- float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration);
- t = (mAnimationTarget == 1f ? IME_ADJUST_ENTRY_INTERPOLATOR : TOUCH_RESPONSE_INTERPOLATOR)
- .getInterpolation(t);
- final boolean updated =
- mDisplayContent.animateForIme(t, mAnimationTarget, mDividerAnimationTarget);
- if (updated) {
- mService.mWindowPlacerLocked.performSurfacePlacement();
- }
- if (t >= 1.0f) {
- mLastAnimationProgress = mAnimationTarget;
- mLastDividerProgress = mDividerAnimationTarget;
- mAnimatingForIme = false;
- return false;
- } else {
- return true;
- }
- }
-
- private boolean animateForMinimizedDockedStack(long now) {
- final ActivityStack stack = mDisplayContent.getRootSplitScreenPrimaryTask();
- if (!mAnimationStarted) {
- mAnimationStarted = true;
- mAnimationStartTime = now;
- notifyDockedStackMinimizedChanged(mMinimizedDock, true /* animate */,
- isHomeStackResizable() /* isHomeStackResizable */);
- }
- float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration);
- t = (isAnimationMaximizing() ? TOUCH_RESPONSE_INTERPOLATOR : mMinimizedDockInterpolator)
- .getInterpolation(t);
- if (stack != null) {
- if (stack.setAdjustedForMinimizedDock(getMinimizeAmount(stack, t))) {
- mService.mWindowPlacerLocked.performSurfacePlacement();
- }
- }
- if (t >= 1.0f) {
- mAnimatingForMinimizedDockedStack = false;
- return false;
- } else {
- return true;
- }
- }
-
- float getInterpolatedAnimationValue(float t) {
- return t * mAnimationTarget + (1 - t) * mAnimationStart;
- }
-
- float getInterpolatedDividerValue(float t) {
- return t * mDividerAnimationTarget + (1 - t) * mDividerAnimationStart;
- }
-
- /**
- * Gets the amount how much to minimize a stack depending on the interpolated fraction t.
- */
- private float getMinimizeAmount(ActivityStack stack, float t) {
- final float naturalAmount = getInterpolatedAnimationValue(t);
- if (isAnimationMaximizing()) {
- return adjustMaximizeAmount(stack, t, naturalAmount);
- } else {
- return naturalAmount;
- }
- }
-
- /**
- * When maximizing the stack during a clip reveal transition, this adjusts the minimize amount
- * during the transition such that the edge of the clip reveal rect is met earlier in the
- * transition so we don't create a visible "hole", but only if both the clip reveal and the
- * docked stack divider start from about the same portion on the screen.
- */
- private float adjustMaximizeAmount(ActivityStack stack, float t, float naturalAmount) {
- if (mMaximizeMeetFraction == 1f) {
- return naturalAmount;
- }
- final int minimizeDistance = stack.getMinimizeDistance();
- final float startPrime = mDisplayContent.mAppTransition.getLastClipRevealMaxTranslation()
- / (float) minimizeDistance;
- final float amountPrime = t * mAnimationTarget + (1 - t) * startPrime;
- final float t2 = Math.min(t / mMaximizeMeetFraction, 1);
- return amountPrime * t2 + naturalAmount * (1 - t2);
- }
-
- /**
- * Retrieves the animation fraction at which the docked stack has to meet the clip reveal
- * edge. See {@link #adjustMaximizeAmount}.
- */
- private float getClipRevealMeetFraction(ActivityStack stack) {
- if (!isAnimationMaximizing() || stack == null ||
- !mDisplayContent.mAppTransition.hadClipRevealAnimation()) {
- return 1f;
- }
- final int minimizeDistance = stack.getMinimizeDistance();
- final float fraction = Math.abs(mDisplayContent.mAppTransition
- .getLastClipRevealMaxTranslation()) / (float) minimizeDistance;
- final float t = Math.max(0, Math.min(1, (fraction - CLIP_REVEAL_MEET_FRACTION_MIN)
- / (CLIP_REVEAL_MEET_FRACTION_MAX - CLIP_REVEAL_MEET_FRACTION_MIN)));
- return CLIP_REVEAL_MEET_EARLIEST
- + (1 - t) * (CLIP_REVEAL_MEET_LAST - CLIP_REVEAL_MEET_EARLIEST);
- }
-
- public String toShortString() {
- return TAG;
- }
-
- WindowState getWindow() {
- return mWindow;
- }
-
- void dump(String prefix, PrintWriter pw) {
- pw.println(prefix + "DockedStackDividerController");
- pw.println(prefix + " mLastVisibility=" + mLastVisibility);
- pw.println(prefix + " mMinimizedDock=" + mMinimizedDock);
- pw.println(prefix + " mAdjustedForIme=" + mAdjustedForIme);
- pw.println(prefix + " mAdjustedForDivider=" + mAdjustedForDivider);
- }
-
- void dumpDebug(ProtoOutputStream proto, long fieldId) {
- final long token = proto.start(fieldId);
- proto.write(MINIMIZED_DOCK, mMinimizedDock);
- proto.end(token);
- }
}
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index b663336..f6bf397 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -20,6 +20,8 @@
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.view.InsetsController.ANIMATION_TYPE_HIDE;
+import static android.view.InsetsController.ANIMATION_TYPE_SHOW;
import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_HIDDEN;
import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
@@ -354,7 +356,8 @@
mListener, typesReady, this, mListener.getDurationMs(),
InsetsController.INTERPOLATOR, true,
show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN
- : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN);
+ : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN,
+ show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE);
SurfaceAnimationThread.getHandler().post(
() -> mListener.onReady(mAnimationControl, typesReady));
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index e69551a..54cea93 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -125,10 +125,6 @@
// enabled for it to start intercepting touch events.
private boolean mInputConsumerEnabled;
- // Whether or not the recents animation should cause the primary split-screen stack to be
- // minimized
- private boolean mSplitScreenMinimized;
-
private final Rect mTmpRect = new Rect();
private boolean mLinkedToDeathOfRunner;
@@ -277,23 +273,6 @@
}
@Override
- public void setSplitScreenMinimized(boolean minimized) {
- final long token = Binder.clearCallingIdentity();
- try {
- synchronized (mService.getWindowManagerLock()) {
- if (mCanceled) {
- return;
- }
-
- mSplitScreenMinimized = minimized;
- mService.checkSplitScreenMinimizedChanged(true /* animate */);
- }
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- @Override
public void hideCurrentInputMethod() {
final long token = Binder.clearCallingIdentity();
try {
@@ -500,7 +479,7 @@
}
if (mTargetActivityRecord != null) {
- final ArrayMap<ActivityRecord, Integer> reasons = new ArrayMap<>(1);
+ final ArrayMap<WindowContainer, Integer> reasons = new ArrayMap<>(1);
reasons.put(mTargetActivityRecord, APP_TRANSITION_RECENTS_ANIM);
mService.mAtmService.mStackSupervisor.getActivityMetricsLogger()
.notifyTransitionStarting(reasons);
@@ -738,10 +717,6 @@
}
}
- boolean isSplitScreenMinimized() {
- return mSplitScreenMinimized;
- }
-
boolean isWallpaperVisible(WindowState w) {
return w != null && w.mAttrs.type == TYPE_BASE_APPLICATION &&
((w.mActivityRecord != null && mTargetActivityRecord == w.mActivityRecord)
@@ -944,7 +919,6 @@
pw.print(innerPrefix); pw.println("mPendingAnimations=" + mPendingAnimations.size());
pw.print(innerPrefix); pw.println("mCanceled=" + mCanceled);
pw.print(innerPrefix); pw.println("mInputConsumerEnabled=" + mInputConsumerEnabled);
- pw.print(innerPrefix); pw.println("mSplitScreenMinimized=" + mSplitScreenMinimized);
pw.print(innerPrefix); pw.println("mTargetActivityRecord=" + mTargetActivityRecord);
pw.print(innerPrefix); pw.println("isTargetOverWallpaper=" + isTargetOverWallpaper());
pw.print(innerPrefix); pw.println("mRequestDeferCancelUntilNextTransition="
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index d2dbab8..0eb9daf 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -387,7 +387,7 @@
final ActivityRecord topActivity = mWindowContainer.getTopMostActivity();
if (dc.mOpeningApps.contains(topActivity)) {
return RemoteAnimationTarget.MODE_OPENING;
- } else if (dc.mChangingApps.contains(topActivity)) {
+ } else if (dc.mChangingContainers.contains(topActivity)) {
return RemoteAnimationTarget.MODE_CHANGING;
} else {
return RemoteAnimationTarget.MODE_CLOSING;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 64d7db2..ada5685 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -258,9 +258,6 @@
*/
final ArrayList<ActivityTaskManagerInternal.SleepToken> mSleepTokens = new ArrayList<>();
- /** Is dock currently minimized. */
- boolean mIsDockMinimized;
-
/** Set when a power hint has started, but not ended. */
private boolean mPowerHintSent;
@@ -1011,9 +1008,7 @@
// Send any pending task-info changes that were queued-up during a layout deferment
mWmService.mAtmService.mTaskOrganizerController.dispatchPendingTaskInfoChanges();
- if (DEBUG_WINDOW_TRACE) Slog.e(TAG,
- "performSurfacePlacementInner exit: animating="
- + mWmService.mAnimator.isAnimating());
+ if (DEBUG_WINDOW_TRACE) Slog.e(TAG, "performSurfacePlacementInner exit");
}
private void checkAppTransitionReady(WindowSurfacePlacer surfacePlacer) {
@@ -1989,8 +1984,7 @@
// We dismiss the docked stack whenever we switch users.
final ActivityStack dockedStack = getDefaultDisplay().getRootSplitScreenPrimaryTask();
if (dockedStack != null) {
- mStackSupervisor.moveTasksToFullscreenStackLocked(
- dockedStack, dockedStack.isFocusedStackOnDisplay());
+ getDefaultDisplay().onSplitScreenModeDismissed();
}
// Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will
// also cause all tasks to be moved to the fullscreen stack at a position that is
@@ -2167,21 +2161,6 @@
}
}
- void setDockedStackMinimized(boolean minimized) {
- // Get currently focused stack before setting mIsDockMinimized. We do this because if
- // split-screen is active, primary stack will not be focusable (see #isFocusable) while
- // still occluding other stacks. This will cause getTopDisplayFocusedStack() to return null.
- final ActivityStack current = getTopDisplayFocusedStack();
- mIsDockMinimized = minimized;
- if (mIsDockMinimized) {
- if (current.inSplitScreenPrimaryWindowingMode()) {
- // The primary split-screen stack can't be focused while it is minimize, so move
- // focus to something else.
- current.adjustFocusToNextFocusableStack("setDockedStackMinimized");
- }
- }
- }
-
ActivityRecord findTask(ActivityRecord r, int preferredDisplayId) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + r);
mTmpFindTaskResult.clear();
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index f6cdac5..68975b9 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -205,7 +205,7 @@
SurfaceControl.ScreenshotGraphicBuffer gb =
mService.mDisplayManagerInternal.screenshot(displayId);
if (gb != null) {
- mStartLuma = RotationAnimationUtils.getAvgBorderLuma(gb.getGraphicBuffer(),
+ mStartLuma = RotationAnimationUtils.getMedianBorderLuma(gb.getGraphicBuffer(),
gb.getColorSpace());
try {
surface.attachAndQueueBufferWithColorSpace(gb.getGraphicBuffer(),
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 7164cd8..1e54e69 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -128,7 +128,8 @@
*/
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type,
- @Nullable OnAnimationFinishedCallback animationFinishedCallback) {
+ @Nullable OnAnimationFinishedCallback animationFinishedCallback,
+ @Nullable SurfaceFreezer freezer) {
cancelAnimation(t, true /* restarting */, true /* forwardCancel */);
mAnimation = anim;
mAnimationType = type;
@@ -139,9 +140,14 @@
cancelAnimation();
return;
}
- mLeash = createAnimationLeash(surface, t,
- mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), hidden);
- mAnimatable.onAnimationLeashCreated(t, mLeash);
+ mLeash = freezer != null ? freezer.takeLeashForAnimation() : null;
+ if (mLeash == null) {
+ mLeash = createAnimationLeash(mAnimatable, surface, t,
+ mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), 0 /* x */,
+ 0 /* y */, hidden);
+ mAnimatable.onAnimationLeashCreated(t, mLeash);
+ }
+ mAnimatable.onLeashAnimationStarting(t, mLeash);
if (mAnimationStartDelayed) {
if (DEBUG_ANIM) Slog.i(TAG, "Animation start delayed");
return;
@@ -150,6 +156,12 @@
}
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
+ @AnimationType int type,
+ @Nullable OnAnimationFinishedCallback animationFinishedCallback) {
+ startAnimation(t, anim, hidden, type, animationFinishedCallback, null /* freezer */);
+ }
+
+ void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type) {
startAnimation(t, anim, hidden, type, null /* animationFinishedCallback */);
}
@@ -311,15 +323,31 @@
}
private void reset(Transaction t, boolean destroyLeash) {
- final SurfaceControl surface = mAnimatable.getSurfaceControl();
- final SurfaceControl parent = mAnimatable.getParentSurfaceControl();
+ mService.mAnimationTransferMap.remove(mAnimation);
+ mAnimation = null;
+ mAnimationFinishedCallback = null;
+ mAnimationType = ANIMATION_TYPE_NONE;
+ if (mLeash == null) {
+ return;
+ }
+ SurfaceControl leash = mLeash;
+ mLeash = null;
+ final boolean scheduleAnim = removeLeash(t, mAnimatable, leash, destroyLeash);
+ if (scheduleAnim) {
+ mService.scheduleAnimationLocked();
+ }
+ }
+ static boolean removeLeash(Transaction t, Animatable animatable, @NonNull SurfaceControl leash,
+ boolean destroy) {
boolean scheduleAnim = false;
+ final SurfaceControl surface = animatable.getSurfaceControl();
+ final SurfaceControl parent = animatable.getParentSurfaceControl();
// If the surface was destroyed or the leash is invalid, we don't care to reparent it back.
// Note that we also set this variable to true even if the parent isn't valid anymore, in
// order to ensure onAnimationLeashLost still gets called in this case.
- final boolean reparent = mLeash != null && surface != null;
+ final boolean reparent = surface != null;
if (reparent) {
if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to original parent: " + parent);
// We shouldn't really need these isValid checks but we do
@@ -329,40 +357,30 @@
scheduleAnim = true;
}
}
- mService.mAnimationTransferMap.remove(mAnimation);
- if (mLeash != null && destroyLeash) {
- t.remove(mLeash);
+ if (destroy) {
+ t.remove(leash);
scheduleAnim = true;
}
- mLeash = null;
- mAnimation = null;
- mAnimationFinishedCallback = null;
- mAnimationType = ANIMATION_TYPE_NONE;
if (reparent) {
// Make sure to inform the animatable after the surface was reparented (or reparent
// wasn't possible, but we still need to invoke the callback)
- mAnimatable.onAnimationLeashLost(t);
+ animatable.onAnimationLeashLost(t);
scheduleAnim = true;
}
-
- if (scheduleAnim) {
- mService.scheduleAnimationLocked();
- }
+ return scheduleAnim;
}
- private SurfaceControl createAnimationLeash(SurfaceControl surface, Transaction t, int width,
- int height, boolean hidden) {
+ static SurfaceControl createAnimationLeash(Animatable animatable, SurfaceControl surface,
+ Transaction t, int width, int height, int x, int y, boolean hidden) {
if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash");
- final SurfaceControl.Builder builder = mAnimatable.makeAnimationLeash()
- .setParent(mAnimatable.getAnimationLeashParent())
+ final SurfaceControl.Builder builder = animatable.makeAnimationLeash()
+ .setParent(animatable.getAnimationLeashParent())
.setHidden(hidden)
.setName(surface + " - animation-leash");
final SurfaceControl leash = builder.build();
t.setWindowCrop(leash, width, height);
-
- // TODO: rely on builder.setHidden(hidden) instead of show and setAlpha when b/138459974 is
- // fixed.
+ t.setPosition(leash, x, y);
t.show(leash);
t.setAlpha(leash, hidden ? 0 : 1);
@@ -489,7 +507,8 @@
void commitPendingTransaction();
/**
- * Called when the was created.
+ * Called when the animation leash is created. Note that this is also called by
+ * {@link SurfaceFreezer}, so this doesn't mean we're about to start animating.
*
* @param t The transaction to use to apply any necessary changes.
* @param leash The leash that was created.
@@ -497,6 +516,14 @@
void onAnimationLeashCreated(Transaction t, SurfaceControl leash);
/**
+ * Called when the animator is about to start animating the leash.
+ *
+ * @param t The transaction to use to apply any necessary changes.
+ * @param leash The leash that was created.
+ */
+ default void onLeashAnimationStarting(Transaction t, SurfaceControl leash) { }
+
+ /**
* Called when the leash is being destroyed, or when the leash is being transferred to
* another SurfaceAnimator.
*
diff --git a/services/core/java/com/android/server/wm/SurfaceFreezer.java b/services/core/java/com/android/server/wm/SurfaceFreezer.java
new file mode 100644
index 0000000..20435ea
--- /dev/null
+++ b/services/core/java/com/android/server/wm/SurfaceFreezer.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.GraphicBuffer;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.view.Surface;
+import android.view.SurfaceControl;
+
+import com.android.server.protolog.common.ProtoLog;
+
+import java.util.function.Supplier;
+
+/**
+ * This class handles "freezing" of an Animatable. The Animatable in question should implement
+ * Freezable.
+ *
+ * The point of this is to enable WindowContainers to each be capable of freezing themselves.
+ * Freezing means taking a snapshot and placing it above everything in the sub-hierarchy.
+ * The "placing above" requires that a parent surface be inserted above the target surface so that
+ * the target surface and the snapshot are siblings.
+ *
+ * The overall flow for a transition using this would be:
+ * 1. Set transition and record animatable in mChangingApps
+ * 2. Call {@link #freeze} to set-up the leashes and cover with a snapshot.
+ * 3. When transition participants are ready, start SurfaceAnimator with this as a parameter
+ * 4. SurfaceAnimator will then {@link #takeLeashForAnimation} instead of creating another leash.
+ * 5. The animation system should eventually clean this up via {@link #unfreeze}.
+ */
+class SurfaceFreezer {
+
+ private final Freezable mAnimatable;
+ private final WindowManagerService mWmService;
+ private SurfaceControl mLeash;
+ Snapshot mSnapshot = null;
+ final Rect mFreezeBounds = new Rect();
+
+ /**
+ * @param animatable The object to animate.
+ */
+ SurfaceFreezer(Freezable animatable, WindowManagerService service) {
+ mAnimatable = animatable;
+ mWmService = service;
+ }
+
+ /**
+ * Freeze the target surface. This is done by creating a leash (inserting a parent surface
+ * above the target surface) and then taking a snapshot and placing it over the target surface.
+ *
+ * @param startBounds The original bounds (on screen) of the surface we are snapshotting.
+ */
+ void freeze(SurfaceControl.Transaction t, Rect startBounds) {
+ mFreezeBounds.set(startBounds);
+
+ mLeash = SurfaceAnimator.createAnimationLeash(mAnimatable, mAnimatable.getSurfaceControl(),
+ t, startBounds.width(), startBounds.height(), startBounds.left, startBounds.top,
+ false /* hidden */);
+ mAnimatable.onAnimationLeashCreated(t, mLeash);
+
+ SurfaceControl freezeTarget = mAnimatable.getFreezeSnapshotTarget();
+ if (freezeTarget != null) {
+ GraphicBuffer snapshot = createSnapshotBuffer(freezeTarget, startBounds);
+ if (snapshot != null) {
+ mSnapshot = new Snapshot(mWmService.mSurfaceFactory, t, snapshot, mLeash);
+ }
+ }
+ }
+
+ /**
+ * Used by {@link SurfaceAnimator}. This "transfers" the leash to be used for animation.
+ * By transferring the leash, this will no longer try to clean-up the leash when finished.
+ */
+ SurfaceControl takeLeashForAnimation() {
+ SurfaceControl out = mLeash;
+ mLeash = null;
+ return out;
+ }
+
+ /**
+ * Clean-up the snapshot and remove leash. If the leash was taken, this just cleans-up the
+ * snapshot.
+ */
+ void unfreeze(SurfaceControl.Transaction t) {
+ if (mSnapshot != null) {
+ mSnapshot.destroy(t);
+ }
+ if (mLeash == null) {
+ return;
+ }
+ SurfaceControl leash = mLeash;
+ mLeash = null;
+ final boolean scheduleAnim = SurfaceAnimator.removeLeash(t, mAnimatable, leash,
+ false /* destroy */);
+ if (scheduleAnim) {
+ mWmService.scheduleAnimationLocked();
+ }
+ }
+
+ boolean hasLeash() {
+ return mLeash != null;
+ }
+
+ private static GraphicBuffer createSnapshotBuffer(@NonNull SurfaceControl target,
+ @Nullable Rect bounds) {
+ Rect cropBounds = null;
+ if (bounds != null) {
+ cropBounds = new Rect(bounds);
+ cropBounds.offsetTo(0, 0);
+ }
+ final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer =
+ SurfaceControl.captureLayers(
+ target, cropBounds, 1.f /* frameScale */, PixelFormat.RGBA_8888);
+ final GraphicBuffer buffer = screenshotBuffer != null ? screenshotBuffer.getGraphicBuffer()
+ : null;
+ if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) {
+ return null;
+ }
+ return buffer;
+ }
+
+ class Snapshot {
+ private SurfaceControl mSurfaceControl;
+ private AnimationAdapter mAnimation;
+ private SurfaceAnimator.OnAnimationFinishedCallback mFinishedCallback;
+
+ /**
+ * @param t Transaction to create the thumbnail in.
+ * @param thumbnailHeader A thumbnail or placeholder for thumbnail to initialize with.
+ */
+ Snapshot(Supplier<Surface> surfaceFactory, SurfaceControl.Transaction t,
+ GraphicBuffer thumbnailHeader, SurfaceControl parent) {
+ Surface drawSurface = surfaceFactory.get();
+ // We can't use a delegating constructor since we need to
+ // reference this::onAnimationFinished
+ final int width = thumbnailHeader.getWidth();
+ final int height = thumbnailHeader.getHeight();
+
+ mSurfaceControl = mAnimatable.makeAnimationLeash()
+ .setName("snapshot anim: " + mAnimatable.toString())
+ .setBufferSize(width, height)
+ .setFormat(PixelFormat.TRANSLUCENT)
+ .setParent(parent)
+ .build();
+
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, " THUMBNAIL %s: CREATE", mSurfaceControl);
+
+ // Transfer the thumbnail to the surface
+ drawSurface.copyFrom(mSurfaceControl);
+ drawSurface.attachAndQueueBuffer(thumbnailHeader);
+ drawSurface.release();
+ t.show(mSurfaceControl);
+
+ // We parent the thumbnail to the container, and just place it on top of anything else
+ // in the container.
+ t.setLayer(mSurfaceControl, Integer.MAX_VALUE);
+ }
+
+ void destroy(SurfaceControl.Transaction t) {
+ if (mSurfaceControl == null) {
+ return;
+ }
+ t.remove(mSurfaceControl);
+ mSurfaceControl = null;
+ }
+
+ /**
+ * Starts an animation.
+ *
+ * @param anim The object that bridges the controller, {@link SurfaceAnimator}, with the
+ * component responsible for running the animation. It runs the animation with
+ * {@link AnimationAdapter#startAnimation} once the hierarchy with
+ * the Leash has been set up.
+ * @param animationFinishedCallback The callback being triggered when the animation
+ * finishes.
+ */
+ void startAnimation(SurfaceControl.Transaction t, AnimationAdapter anim, int type,
+ @Nullable SurfaceAnimator.OnAnimationFinishedCallback animationFinishedCallback) {
+ cancelAnimation(t, true /* restarting */);
+ mAnimation = anim;
+ mFinishedCallback = animationFinishedCallback;
+ if (mSurfaceControl == null) {
+ cancelAnimation(t, false /* restarting */);
+ return;
+ }
+ mAnimation.startAnimation(mSurfaceControl, t, type, animationFinishedCallback);
+ }
+
+ /**
+ * Cancels the animation, and resets the leash.
+ *
+ * @param t The transaction to use for all cancelling surface operations.
+ * @param restarting Whether we are restarting the animation.
+ */
+ private void cancelAnimation(SurfaceControl.Transaction t, boolean restarting) {
+ final SurfaceControl leash = mSurfaceControl;
+ final AnimationAdapter animation = mAnimation;
+ final SurfaceAnimator.OnAnimationFinishedCallback animationFinishedCallback =
+ mFinishedCallback;
+ mAnimation = null;
+ mFinishedCallback = null;
+ if (animation != null) {
+ animation.onAnimationCancelled(leash);
+ if (!restarting) {
+ if (animationFinishedCallback != null) {
+ animationFinishedCallback.onAnimationFinished(
+ ANIMATION_TYPE_APP_TRANSITION, animation);
+ }
+ }
+ }
+ if (!restarting) {
+ // TODO: do we need to destroy?
+ destroy(t);
+ }
+ }
+ }
+
+ /** freezable */
+ public interface Freezable extends SurfaceAnimator.Animatable {
+ /**
+ * @return The surface to take a snapshot of. If this returns {@code null}, no snapshot
+ * will be generated (but the rest of the freezing logic will still happen).
+ */
+ @Nullable SurfaceControl getFreezeSnapshotTarget();
+ }
+}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 76805e9..27acb23 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -58,6 +58,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.SurfaceControl.METADATA_TASK_ID;
+import static android.view.WindowManager.TRANSIT_TASK_CHANGE_WINDOWING_MODE;
import static com.android.internal.policy.DecorView.DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
import static com.android.internal.policy.DecorView.DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
@@ -78,6 +79,9 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskManagerService.TAG_STACK;
import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
+import static com.android.server.wm.IdentifierProto.HASH_CODE;
+import static com.android.server.wm.IdentifierProto.TITLE;
+import static com.android.server.wm.IdentifierProto.USER_ID;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -119,10 +123,13 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.service.voice.IVoiceInteractionSession;
+import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
import android.view.ITaskOrganizer;
+import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -1884,6 +1891,8 @@
.setBounds(mLastNonFullscreenBounds);
}
+ final int prevWinMode = getWindowingMode();
+ mTmpPrevBounds.set(getBounds());
final boolean wasInMultiWindowMode = inMultiWindowMode();
super.onConfigurationChanged(newParentConfig);
if (wasInMultiWindowMode != inMultiWindowMode()) {
@@ -1891,6 +1900,12 @@
updateShadowsRadius(isFocused(), getPendingTransaction());
}
+ final int newWinMode = getWindowingMode();
+ if ((prevWinMode != newWinMode) && (mDisplayContent != null)
+ && shouldStartChangeTransition(prevWinMode, newWinMode)) {
+ initializeChangeTransition(mTmpPrevBounds);
+ }
+
// If the configuration supports persistent bounds (eg. Freeform), keep track of the
// current (non-fullscreen) bounds for persistence.
if (getWindowConfiguration().persistTaskBounds()) {
@@ -1905,6 +1920,63 @@
}
/**
+ * Initializes a change transition. See {@link SurfaceFreezer} for more information.
+ */
+ private void initializeChangeTransition(Rect startBounds) {
+ mDisplayContent.prepareAppTransition(TRANSIT_TASK_CHANGE_WINDOWING_MODE,
+ false /* alwaysKeepCurrent */, 0, false /* forceOverride */);
+ mDisplayContent.mChangingContainers.add(this);
+
+ mSurfaceFreezer.freeze(getPendingTransaction(), startBounds);
+ }
+
+ private boolean shouldStartChangeTransition(int prevWinMode, int newWinMode) {
+ if (mWmService.mDisableTransitionAnimation
+ || !isVisible()
+ || getDisplayContent().mAppTransition.isTransitionSet()
+ || getSurfaceControl() == null) {
+ return false;
+ }
+ // Only do an animation into and out-of freeform mode for now. Other mode
+ // transition animations are currently handled by system-ui.
+ return (prevWinMode == WINDOWING_MODE_FREEFORM) != (newWinMode == WINDOWING_MODE_FREEFORM);
+ }
+
+ @VisibleForTesting
+ boolean isInChangeTransition() {
+ return mSurfaceFreezer.hasLeash() || AppTransition.isChangeTransit(mTransit);
+ }
+
+ @Override
+ public SurfaceControl getFreezeSnapshotTarget() {
+ final int transit = mDisplayContent.mAppTransition.getAppTransition();
+ if (!AppTransition.isChangeTransit(transit)) {
+ return null;
+ }
+ // Skip creating snapshot if this transition is controlled by a remote animator which
+ // doesn't need it.
+ final ArraySet<Integer> activityTypes = new ArraySet<>();
+ activityTypes.add(getActivityType());
+ final RemoteAnimationAdapter adapter =
+ mDisplayContent.mAppTransitionController.getRemoteAnimationOverride(
+ this, transit, activityTypes);
+ if (adapter != null && !adapter.getChangeNeedsSnapshot()) {
+ return null;
+ }
+ return getSurfaceControl();
+ }
+
+ @Override
+ void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(HASH_CODE, System.identityHashCode(this));
+ proto.write(USER_ID, mUserId);
+ proto.write(TITLE, intent != null && intent.getComponent() != null
+ ? intent.getComponent().flattenToShortString() : "Task");
+ proto.end(token);
+ }
+
+ /**
* Saves launching state if necessary so that we can launch the activity to its latest state.
* It only saves state if this task has been shown to user and it's in fullscreen or freeform
* mode on freeform displays.
@@ -2036,18 +2108,6 @@
intersectWithInsetsIfFits(outStableBounds, mTmpBounds, mTmpInsets);
}
- /**
- * Asks docked-divider controller for the smallestwidthdp given bounds.
- * @param bounds bounds to calculate smallestwidthdp for.
- */
- private int getSmallestScreenWidthDpForDockedBounds(Rect bounds) {
- DisplayContent dc = getDisplayContent();
- if (dc != null) {
- return dc.getDockedDividerController().getSmallestWidthDpForBounds(bounds);
- }
- return Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
- }
-
void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
@NonNull Configuration parentConfig) {
computeConfigResourceOverrides(inOutConfig, parentConfig, null /* compatInsets */);
@@ -2614,12 +2674,22 @@
if (!isRootTask) {
adjustBoundsForDisplayChangeIfNeeded(dc);
}
+ final DisplayContent prevDc = mDisplayContent;
super.onDisplayChanged(dc);
if (!isRootTask) {
final int displayId = (dc != null) ? dc.getDisplayId() : INVALID_DISPLAY;
mWmService.mAtmService.getTaskChangeNotificationController().notifyTaskDisplayChanged(
mTaskId, displayId);
}
+ if (prevDc != null && prevDc.mChangingContainers.remove(this)) {
+ // This gets called *after* this has been reparented to the new display.
+ // That reparenting resulted in this window changing modes (eg. FREEFORM -> FULLSCREEN),
+ // so this token is now "frozen" while waiting for the animation to start on prevDc
+ // (which will be cancelled since the window is no-longer a child). However, since this
+ // is no longer a child of prevDc, this won't be notified of the cancelled animation,
+ // so we need to cancel the change transition here.
+ mSurfaceFreezer.unfreeze(getPendingTransaction());
+ }
}
/**
@@ -3010,15 +3080,6 @@
return forAllTasks((t) -> { return t != this && t.isTaskAnimating(); });
}
- /**
- * @return {@code true} if changing app transition is running.
- */
- @Override
- boolean isChangingAppTransition() {
- final ActivityRecord activity = getTopVisibleActivity();
- return activity != null && getDisplayContent().mChangingApps.contains(activity);
- }
-
@Override
RemoteAnimationTarget createRemoteAnimationTarget(
RemoteAnimationController.RemoteAnimationRecord record) {
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 137d122..669eb78 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -669,8 +669,7 @@
* Adjusts the wallpaper windows if the input display has a pending wallpaper layout or one of
* the opening apps should be a wallpaper target.
*/
- void adjustWallpaperWindowsForAppTransitionIfNeeded(ArraySet<ActivityRecord> openingApps,
- ArraySet<ActivityRecord> changingApps) {
+ void adjustWallpaperWindowsForAppTransitionIfNeeded(ArraySet<ActivityRecord> openingApps) {
boolean adjust = false;
if ((mDisplayContent.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
adjust = true;
@@ -682,15 +681,6 @@
break;
}
}
- if (!adjust) {
- for (int i = changingApps.size() - 1; i >= 0; --i) {
- final ActivityRecord activity = changingApps.valueAt(i);
- if (activity.windowsCanBeWallpaperTarget()) {
- adjust = true;
- break;
- }
- }
- }
}
if (adjust) {
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index fd91bc5..b6be386 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -50,7 +50,6 @@
final WindowManagerPolicy mPolicy;
/** Is any window animating? */
- private boolean mAnimating;
private boolean mLastRootAnimating;
final Choreographer.FrameCallback mAnimationFrameCallback;
@@ -135,7 +134,6 @@
synchronized (mService.mGlobalLock) {
mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE;
- mAnimating = false;
if (DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
}
@@ -160,17 +158,12 @@
final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
dc.checkAppWindowsReadyToShow();
- orAnimating(dc.getDockedDividerController().animate(mCurrentTime));
if (accessibilityController != null) {
accessibilityController.drawMagnifiedRegionBorderIfNeededLocked(displayId,
mTransaction);
}
}
- if (!mAnimating) {
- cancelAnimation();
- }
-
if (mService.mWatermark != null) {
mService.mWatermark.drawIfNeeded();
}
@@ -220,7 +213,7 @@
executeAfterPrepareSurfacesRunnables();
if (DEBUG_WINDOW_TRACE) {
- Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating
+ Slog.i(TAG, "!!! animate: exit"
+ " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
+ " hasPendingLayoutChanges=" + hasPendingLayoutChanges);
}
@@ -302,10 +295,6 @@
private class DisplayContentsAnimator {
}
- boolean isAnimating() {
- return mAnimating;
- }
-
boolean isAnimationScheduled() {
return mAnimationFrameCallbackScheduled;
}
@@ -314,14 +303,6 @@
return mChoreographer;
}
- void setAnimating(boolean animating) {
- mAnimating = animating;
- }
-
- void orAnimating(boolean animating) {
- mAnimating |= animating;
- }
-
/**
* Adds a runnable to be executed after {@link WindowContainer#prepareSurfaces} is called and
* the corresponding transaction is closed and applied.
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index aaaabf2..b12c698 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -25,9 +25,14 @@
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.os.UserHandle.USER_NULL;
import static android.view.SurfaceControl.Transaction;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.AppTransition.MAX_APP_TRANSITION_DURATION;
+import static com.android.server.wm.IdentifierProto.HASH_CODE;
+import static com.android.server.wm.IdentifierProto.TITLE;
+import static com.android.server.wm.IdentifierProto.USER_ID;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
@@ -64,6 +69,7 @@
import android.view.DisplayInfo;
import android.view.IWindowContainer;
import android.view.MagnificationSpec;
+import android.view.RemoteAnimationDefinition;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Builder;
@@ -94,7 +100,7 @@
* changes are made to this class.
*/
class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>
- implements Comparable<WindowContainer>, Animatable,
+ implements Comparable<WindowContainer>, Animatable, SurfaceFreezer.Freezable,
BLASTSyncEngine.TransactionReadyListener {
private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowContainer" : TAG_WM;
@@ -169,6 +175,7 @@
* Applied as part of the animation pass in "prepareSurfaces".
*/
protected final SurfaceAnimator mSurfaceAnimator;
+ final SurfaceFreezer mSurfaceFreezer;
protected final WindowManagerService mWmService;
private final Point mTmpPos = new Point();
@@ -252,7 +259,6 @@
* where it represents the starting-state snapshot.
*/
WindowContainerThumbnail mThumbnail;
- final Rect mTransitStartRect = new Rect();
final Point mTmpPoint = new Point();
protected final Rect mTmpRect = new Rect();
final Rect mTmpPrevBounds = new Rect();
@@ -277,6 +283,7 @@
mWmService = wms;
mPendingTransaction = wms.mTransactionFactory.get();
mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, wms);
+ mSurfaceFreezer = new SurfaceFreezer(this, wms);
}
@Override
@@ -834,7 +841,7 @@
* @return {@code true} if the container is in changing app transition.
*/
boolean isChangingAppTransition() {
- return false;
+ return mDisplayContent != null && mDisplayContent.mChangingContainers.contains(this);
}
void sendAppVisibilityToClients() {
@@ -886,6 +893,31 @@
}
/**
+ * Called when the visibility of a child is asked to change. This is before visibility actually
+ * changes (eg. a transition animation might play out first).
+ */
+ void onChildVisibilityRequested(boolean visible) {
+ // If we are changing visibility, then a snapshot isn't necessary and we are no-longer
+ // part of a change transition.
+ mSurfaceFreezer.unfreeze(getPendingTransaction());
+ if (mDisplayContent != null) {
+ mDisplayContent.mChangingContainers.remove(this);
+ }
+ WindowContainer parent = getParent();
+ if (parent != null) {
+ parent.onChildVisibilityRequested(visible);
+ }
+ }
+
+ void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(HASH_CODE, System.identityHashCode(this));
+ proto.write(USER_ID, USER_NULL);
+ proto.write(TITLE, "WindowContainer");
+ proto.end(token);
+ }
+
+ /**
* Returns {@code true} if this container is focusable. Generally, if a parent is not focusable,
* this will not be focusable either.
*/
@@ -1917,7 +1949,8 @@
// TODO: This should use isVisible() but because isVisible has a really weird meaning at
// the moment this doesn't work for all animatable window containers.
- mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback);
+ mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback,
+ mSurfaceFreezer);
}
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@@ -1934,6 +1967,11 @@
}
@Override
+ public SurfaceControl getFreezeSnapshotTarget() {
+ return null;
+ }
+
+ @Override
public Builder makeAnimationLeash() {
return makeSurface().setContainerLayer();
}
@@ -2002,8 +2040,9 @@
getDisplayContent().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
if (thumbnailAdapter != null) {
- mThumbnail.startAnimation(
- getPendingTransaction(), thumbnailAdapter, !isVisible());
+ mSurfaceFreezer.mSnapshot.startAnimation(getPendingTransaction(),
+ thumbnailAdapter, ANIMATION_TYPE_APP_TRANSITION,
+ (type, anim) -> { });
}
}
} else {
@@ -2049,7 +2088,7 @@
if (controller != null && !mSurfaceAnimator.isAnimationStartDelayed()) {
final RemoteAnimationController.RemoteAnimationRecord adapters =
controller.createRemoteAnimationRecord(this, mTmpPoint, mTmpRect,
- (isChanging ? mTransitStartRect : null));
+ (isChanging ? mSurfaceFreezer.mFreezeBounds : null));
resultAdapters = new Pair<>(adapters.mAdapter, adapters.mThumbnailAdapter);
} else if (isChanging) {
final float durationScale = mWmService.getTransitionAnimationScaleLocked();
@@ -2057,14 +2096,15 @@
mTmpRect.offsetTo(mTmpPoint.x, mTmpPoint.y);
final AnimationAdapter adapter = new LocalAnimationAdapter(
- new WindowChangeAnimationSpec(mTransitStartRect, mTmpRect, displayInfo,
- durationScale, true /* isAppAnimation */, false /* isThumbnail */),
+ new WindowChangeAnimationSpec(mSurfaceFreezer.mFreezeBounds, mTmpRect,
+ displayInfo, durationScale, true /* isAppAnimation */,
+ false /* isThumbnail */),
getSurfaceAnimationRunner());
- final AnimationAdapter thumbnailAdapter = mThumbnail != null
- ? new LocalAnimationAdapter(new WindowChangeAnimationSpec(mTransitStartRect,
- mTmpRect, displayInfo, durationScale, true /* isAppAnimation */,
- true /* isThumbnail */), getSurfaceAnimationRunner())
+ final AnimationAdapter thumbnailAdapter = mSurfaceFreezer.mSnapshot != null
+ ? new LocalAnimationAdapter(new WindowChangeAnimationSpec(
+ mSurfaceFreezer.mFreezeBounds, mTmpRect, displayInfo, durationScale,
+ true /* isAppAnimation */, true /* isThumbnail */), getSurfaceAnimationRunner())
: null;
resultAdapters = new Pair<>(adapter, thumbnailAdapter);
mTransit = transit;
@@ -2133,7 +2173,16 @@
displayConfig.uiMode, displayConfig.orientation, frame, displayFrame, insets,
surfaceInsets, stableInsets, isVoiceInteraction, inFreeformWindowingMode(), this);
if (a != null) {
- if (DEBUG_ANIM) logWithStack(TAG, "Loaded animation " + a + " for " + this);
+ if (a != null) {
+ // Setup the maximum app transition duration to prevent malicious app may set a long
+ // animation duration or infinite repeat counts for the app transition through
+ // ActivityOption#makeCustomAnimation or WindowManager#overridePendingTransition.
+ a.restrictDuration(MAX_APP_TRANSITION_DURATION);
+ }
+ if (DEBUG_ANIM) {
+ logWithStack(TAG, "Loaded animation " + a + " for " + this
+ + ", duration: " + ((a != null) ? a.getDuration() : 0));
+ }
final int containingWidth = frame.width();
final int containingHeight = frame.height();
a.initialize(containingWidth, containingHeight, width, height);
@@ -2182,6 +2231,7 @@
@Override
public void onAnimationLeashLost(Transaction t) {
mLastLayer = -1;
+ mSurfaceFreezer.unfreeze(t);
reassignLayer(t);
}
@@ -2322,6 +2372,10 @@
mSurfaceControl = sc;
}
+ RemoteAnimationDefinition getRemoteAnimationDefinition() {
+ return null;
+ }
+
/** Cheap way of doing cast and instanceof. */
Task asTask() {
return null;
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 240f566..4ac809d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -527,9 +527,9 @@
/**
* Hide IME using imeTargetWindow when requested.
*
- * @param displayId on which IME is shown
+ * @param imeTargetWindowToken token of the (IME target) window on which IME should be hidden.
*/
- public abstract void hideIme(int displayId);
+ public abstract void hideIme(IBinder imeTargetWindowToken);
/**
* Tell window manager about a package that should not be running with high refresh rate
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e0f8f0e..0169a4f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -42,7 +42,6 @@
import static android.provider.Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
-import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
@@ -59,7 +58,6 @@
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
@@ -214,7 +212,6 @@
import android.view.IDisplayWindowInsetsController;
import android.view.IDisplayWindowListener;
import android.view.IDisplayWindowRotationController;
-import android.view.IDockedStackListener;
import android.view.IInputFilter;
import android.view.IOnKeyguardExitResult;
import android.view.IPinnedStackListener;
@@ -1636,14 +1633,6 @@
}
}
- // If the window is being added to a stack that's currently adjusted for IME,
- // make sure to apply the same adjust to this new window.
- win.applyAdjustForImeIfNeeded();
-
- if (type == TYPE_DOCK_DIVIDER) {
- mRoot.getDisplayContent(displayId).getDockedDividerController().setWindow(win);
- }
-
final WindowStateAnimator winAnimator = win.mWinAnimator;
winAnimator.mEnterAnimationPending = true;
winAnimator.mEnteringAnimation = true;
@@ -2816,16 +2805,6 @@
}
}
- void setDockedStackCreateStateLocked(int mode, Rect bounds) {
- mDockedStackCreateMode = mode;
- mDockedStackCreateBounds = bounds;
- }
-
- void checkSplitScreenMinimizedChanged(boolean animate) {
- final DisplayContent displayContent = getDefaultDisplayContentLocked();
- displayContent.getDockedDividerController().checkMinimizeChanged(animate);
- }
-
boolean isValidPictureInPictureAspectRatio(DisplayContent displayContent, float aspectRatio) {
return displayContent.getPinnedStackController().isValidPictureInPictureAspectRatio(
aspectRatio);
@@ -3295,10 +3274,6 @@
// Notify whether the docked stack exists for the current user
final DisplayContent displayContent = getDefaultDisplayContentLocked();
- final ActivityStack stack =
- displayContent.getRootSplitScreenPrimaryTask();
- displayContent.mDividerControllerLocked.notifyDockedStackExistsChanged(
- stack != null && stack.hasTaskForUser(newUserId));
mRoot.forAllDisplays(dc -> dc.mAppTransition.setCurrentUser(newUserId));
@@ -4687,7 +4662,7 @@
public static final int RESET_ANR_MESSAGE = 38;
public static final int WALLPAPER_DRAW_PENDING_TIMEOUT = 39;
- public static final int UPDATE_DOCKED_STACK_DIVIDER = 41;
+ public static final int UPDATE_MULTI_WINDOW_STACKS = 41;
public static final int WINDOW_REPLACEMENT_TIMEOUT = 46;
@@ -4832,7 +4807,7 @@
synchronized (mGlobalLock) {
// Since we're holding both mWindowMap and mAnimator we don't need to
// hold mAnimator.mLayoutToAnim.
- if (mAnimator.isAnimating() || mAnimator.isAnimationScheduled()) {
+ if (mAnimator.isAnimationScheduled()) {
// If we are animating, don't do the gc now but
// delay a bit so we don't interrupt the animation.
sendEmptyMessageDelayed(H.FORCE_GC, 2000);
@@ -4994,11 +4969,10 @@
}
break;
}
- case UPDATE_DOCKED_STACK_DIVIDER: {
+ case UPDATE_MULTI_WINDOW_STACKS: {
synchronized (mGlobalLock) {
final DisplayContent displayContent = getDefaultDisplayContentLocked();
if (displayContent != null) {
- displayContent.getDockedDividerController().reevaluateVisibility(false);
displayContent.adjustForImeIfNeeded();
}
}
@@ -6551,11 +6525,7 @@
@Override
public int getDockedStackSide() {
- synchronized (mGlobalLock) {
- final ActivityStack dockedStack = getDefaultDisplayContentLocked()
- .getRootSplitScreenPrimaryTask();
- return dockedStack == null ? DOCKED_INVALID : dockedStack.getDockSide();
- }
+ return 0;
}
void setDockedStackResizing(boolean resizing) {
@@ -6572,14 +6542,6 @@
}
}
- @Override
- public void setResizeDimLayer(boolean visible, int targetWindowingMode, float alpha) {
- synchronized (mGlobalLock) {
- getDefaultDisplayContentLocked().getDockedDividerController().setResizeDimLayer(
- visible, targetWindowingMode, alpha);
- }
- }
-
void setForceDesktopModeOnExternalDisplays(boolean forceDesktopModeOnExternalDisplays) {
synchronized (mGlobalLock) {
mForceDesktopModeOnExternalDisplays = forceDesktopModeOnExternalDisplays;
@@ -6598,17 +6560,6 @@
}
@Override
- public void registerDockedStackListener(IDockedStackListener listener) {
- mAtmInternal.enforceCallerIsRecentsOrHasPermission(REGISTER_WINDOW_MANAGER_LISTENERS,
- "registerDockedStackListener()");
- synchronized (mGlobalLock) {
- // TODO(multi-display): The listener is registered on the default display only.
- getDefaultDisplayContentLocked().mDividerControllerLocked.registerDockedStackListener(
- listener);
- }
- }
-
- @Override
public void registerPinnedStackListener(int displayId, IPinnedStackListener listener) {
if (!checkCallingPermission(REGISTER_WINDOW_MANAGER_LISTENERS,
"registerPinnedStackListener()")) {
@@ -7490,27 +7441,28 @@
return;
}
imeTarget = imeTarget.getImeControlTarget();
-
- final int displayId = imeTarget.getDisplayId();
- mRoot.getDisplayContent(displayId).getInsetsStateController().getImeSourceProvider()
+ imeTarget.getDisplayContent().getInsetsStateController().getImeSourceProvider()
.scheduleShowImePostLayout(imeTarget);
}
}
@Override
- public void hideIme(int displayId) {
+ public void hideIme(IBinder imeTargetWindowToken) {
synchronized (mGlobalLock) {
- final DisplayContent dc = mRoot.getDisplayContent(displayId);
- if (dc != null) {
- InsetsControlTarget imeControlTarget = dc.mInputMethodControlTarget;
- if (imeControlTarget == null) {
- return;
- }
- // If there was a pending IME show(), reset it as IME has been
- // requested to be hidden.
- dc.getInsetsStateController().getImeSourceProvider().abortShowImePostLayout();
- imeControlTarget.hideInsets(WindowInsets.Type.ime(), true /* fromIme */);
+ WindowState imeTarget = mWindowMap.get(imeTargetWindowToken);
+ if (imeTarget == null) {
+ // The target window no longer exists.
+ return;
}
+ final DisplayContent dc = imeTarget.getImeControlTarget().getDisplayContent();
+ // If there was a pending IME show(), reset it as IME has been
+ // requested to be hidden.
+ dc.getInsetsStateController().getImeSourceProvider().abortShowImePostLayout();
+ if (dc.mInputMethodControlTarget == null) {
+ return;
+ }
+ dc.mInputMethodControlTarget.hideInsets(
+ WindowInsets.Type.ime(), true /* fromIme */);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 37597fb..e452c4a 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1128,7 +1128,6 @@
}
}
} else if (mAttrs.type == TYPE_DOCK_DIVIDER) {
- dc.getDockedDividerController().positionDockedStackedDivider(windowFrames.mFrame);
windowFrames.mContentFrame.set(windowFrames.mFrame);
if (!windowFrames.mFrame.equals(windowFrames.mLastFrame)) {
mMovedByResize = true;
@@ -1937,13 +1936,9 @@
// animating... let's do something.
final int left = mWindowFrames.mFrame.left;
final int top = mWindowFrames.mFrame.top;
- final Task task = getTask();
- final boolean adjustedForMinimizedDockOrIme = task != null
- && (task.getStack().isAdjustedForMinimizedDockedStack()
- || task.getStack().isAdjustedForIme());
if (mToken.okToAnimate()
&& (mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0
- && !isDragResizing() && !adjustedForMinimizedDockOrIme
+ && !isDragResizing()
&& getWindowConfiguration().hasMovementAnimations()
&& !mWinAnimator.mLastHidden
&& !mSeamlesslyRotated) {
@@ -2250,15 +2245,15 @@
}
final ActivityStack stack = getRootTask();
- if (stack != null && stack.shouldIgnoreInput()) {
+ if (stack != null && !stack.isFocusable()) {
// Ignore when the stack shouldn't receive input event.
// (i.e. the minimized stack in split screen mode.)
return false;
}
- if (PixelFormat.formatHasAlpha(mAttrs.format)) {
- // Support legacy use cases where transparent windows can still be ime target with
- // FLAG_NOT_FOCUSABLE and ALT_FOCUSABLE_IM set.
+ if (PixelFormat.formatHasAlpha(mAttrs.format) && mAttrs.alpha == 0) {
+ // Support legacy use cases where completely transparent windows can still be ime target
+ // with FLAG_NOT_FOCUSABLE and ALT_FOCUSABLE_IM set.
// Certain apps listen for IME insets using transparent windows and ADJUST_NOTHING to
// manually synchronize app content to IME animation b/144619551.
// TODO(b/145812508): remove this once new focus management is complete b/141738570
@@ -2412,13 +2407,6 @@
}
}
- void applyAdjustForImeIfNeeded() {
- final Task task = getTask();
- if (task != null && task.getStack() != null && task.getStack().isAdjustedForIme()) {
- task.getStack().applyAdjustForImeIfNeeded(task);
- }
- }
-
@Override
void switchUser(int userId) {
super.switchUser(userId);
@@ -4308,10 +4296,6 @@
}
}
- if (mAttrs.type == TYPE_INPUT_METHOD) {
- getDisplayContent().mDividerControllerLocked.resetImeHideRequested();
- }
-
return true;
}
diff --git a/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java b/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java
index 0c09c88..d02f79f 100644
--- a/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java
+++ b/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java
@@ -26,6 +26,8 @@
import android.view.Surface;
import android.view.SurfaceControl;
+import java.util.Arrays;
+
/** Helper functions for the {@link com.android.server.wm.ScreenRotationAnimation} class*/
public class RotationAnimationUtils {
@@ -35,31 +37,35 @@
* luminance at the borders of the bitmap
* @return the average luminance of all the pixels at the borders of the bitmap
*/
- public static float getAvgBorderLuma(GraphicBuffer graphicBuffer, ColorSpace colorSpace) {
+ public static float getMedianBorderLuma(GraphicBuffer graphicBuffer, ColorSpace colorSpace) {
Bitmap hwBitmap = Bitmap.wrapHardwareBuffer(graphicBuffer, colorSpace);
if (hwBitmap == null) {
return 0;
}
Bitmap swaBitmap = hwBitmap.copy(Bitmap.Config.ARGB_8888, false);
- float totalLuma = 0;
int height = swaBitmap.getHeight();
int width = swaBitmap.getWidth();
+ float[] borderLumas = new float[2 * width + 2 * height];
int i;
- for (i = 0; i < width; i++) {
- totalLuma += swaBitmap.getColor(i, 0).luminance();
- totalLuma += swaBitmap.getColor(i, height - 1).luminance();
+ int index = 0;
+ for (i = 0; i < width; i++, index += 2) {
+ borderLumas[index] = swaBitmap.getColor(i, 0).luminance();
+ borderLumas[index + 1] = swaBitmap.getColor(i, height - 1).luminance();
}
- for (i = 0; i < height; i++) {
- totalLuma += swaBitmap.getColor(0, i).luminance();
- totalLuma += swaBitmap.getColor(width - 1, i).luminance();
+ for (i = 0; i < height; i++, index += 2) {
+ borderLumas[index] = swaBitmap.getColor(0, i).luminance();
+ borderLumas[index + 1] = swaBitmap.getColor(width - 1, i).luminance();
}
- return totalLuma / (2 * width + 2 * height);
+ // Oh, is this too simple and inefficient for you?
+ // How about implementing a O(n) solution? https://en.wikipedia.org/wiki/Median_of_medians
+ Arrays.sort(borderLumas);
+ return borderLumas[borderLumas.length / 2];
}
/**
* Gets the average border luma by taking a screenshot of the {@param surfaceControl}.
- * @see #getAvgBorderLuma(GraphicBuffer, ColorSpace)
+ * @see #getMedianBorderLuma(GraphicBuffer, ColorSpace)
*/
public static float getLumaOfSurfaceControl(Display display, SurfaceControl surfaceControl) {
if (surfaceControl == null) {
@@ -75,7 +81,7 @@
return 0;
}
- return RotationAnimationUtils.getAvgBorderLuma(buffer.getGraphicBuffer(),
+ return RotationAnimationUtils.getMedianBorderLuma(buffer.getGraphicBuffer(),
buffer.getColorSpace());
}
diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp
index 5a8e25e4..05aa359 100644
--- a/services/core/jni/com_android_server_VibratorService.cpp
+++ b/services/core/jni/com_android_server_VibratorService.cpp
@@ -355,10 +355,11 @@
}
static jlong vibratorPerformEffect(JNIEnv* env, jclass, jlong effect, jlong strength,
- jobject vibration) {
+ jobject vibration, jboolean withCallback) {
if (auto hal = getHal<aidl::IVibrator>()) {
int32_t lengthMs;
- sp<AidlVibratorCallback> effectCallback = new AidlVibratorCallback(env, vibration);
+ sp<AidlVibratorCallback> effectCallback =
+ (withCallback != JNI_FALSE ? new AidlVibratorCallback(env, vibration) : nullptr);
aidl::Effect effectType(static_cast<aidl::Effect>(effect));
aidl::EffectStrength effectStrength(static_cast<aidl::EffectStrength>(strength));
@@ -478,24 +479,24 @@
}
static const JNINativeMethod method_table[] = {
- { "vibratorExists", "()Z", (void*)vibratorExists },
- { "vibratorInit", "()V", (void*)vibratorInit },
- { "vibratorOn", "(J)V", (void*)vibratorOn },
- { "vibratorOff", "()V", (void*)vibratorOff },
- { "vibratorSupportsAmplitudeControl", "()Z", (void*)vibratorSupportsAmplitudeControl},
- { "vibratorSetAmplitude", "(I)V", (void*)vibratorSetAmplitude},
- { "vibratorPerformEffect", "(JJLcom/android/server/VibratorService$Vibration;)J",
- (void*)vibratorPerformEffect},
- { "vibratorPerformComposedEffect",
- "([Landroid/os/VibrationEffect$Composition$PrimitiveEffect;Lcom/android/server/VibratorService$Vibration;)V",
- (void*)vibratorPerformComposedEffect},
- { "vibratorGetSupportedEffects", "()[I",
- (void*)vibratorGetSupportedEffects},
- { "vibratorSupportsExternalControl", "()Z", (void*)vibratorSupportsExternalControl},
- { "vibratorSetExternalControl", "(Z)V", (void*)vibratorSetExternalControl},
- { "vibratorGetCapabilities", "()J", (void*)vibratorGetCapabilities},
- { "vibratorAlwaysOnEnable", "(JJJ)V", (void*)vibratorAlwaysOnEnable},
- { "vibratorAlwaysOnDisable", "(J)V", (void*)vibratorAlwaysOnDisable},
+ {"vibratorExists", "()Z", (void*)vibratorExists},
+ {"vibratorInit", "()V", (void*)vibratorInit},
+ {"vibratorOn", "(J)V", (void*)vibratorOn},
+ {"vibratorOff", "()V", (void*)vibratorOff},
+ {"vibratorSupportsAmplitudeControl", "()Z", (void*)vibratorSupportsAmplitudeControl},
+ {"vibratorSetAmplitude", "(I)V", (void*)vibratorSetAmplitude},
+ {"vibratorPerformEffect", "(JJLcom/android/server/VibratorService$Vibration;Z)J",
+ (void*)vibratorPerformEffect},
+ {"vibratorPerformComposedEffect",
+ "([Landroid/os/VibrationEffect$Composition$PrimitiveEffect;Lcom/android/server/"
+ "VibratorService$Vibration;)V",
+ (void*)vibratorPerformComposedEffect},
+ {"vibratorGetSupportedEffects", "()[I", (void*)vibratorGetSupportedEffects},
+ {"vibratorSupportsExternalControl", "()Z", (void*)vibratorSupportsExternalControl},
+ {"vibratorSetExternalControl", "(Z)V", (void*)vibratorSetExternalControl},
+ {"vibratorGetCapabilities", "()J", (void*)vibratorGetCapabilities},
+ {"vibratorAlwaysOnEnable", "(JJJ)V", (void*)vibratorAlwaysOnEnable},
+ {"vibratorAlwaysOnDisable", "(J)V", (void*)vibratorAlwaysOnDisable},
};
int register_android_server_VibratorService(JNIEnv *env) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 440b779..36c0659 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4674,22 +4674,24 @@
return getUserDataUnchecked(userHandle).mAdminList;
}
ArrayList<ActiveAdmin> admins = new ArrayList<>();
- for (UserInfo userInfo : mUserManager.getProfiles(userHandle)) {
- DevicePolicyData policy = getUserDataUnchecked(userInfo.id);
- if (userInfo.id == userHandle) {
- admins.addAll(policy.mAdminList);
- } else if (userInfo.isManagedProfile()) {
- // For managed profiles, policies set on the parent profile will be included
- for (int i = 0; i < policy.mAdminList.size(); i++) {
- ActiveAdmin admin = policy.mAdminList.get(i);
- if (admin.hasParentActiveAdmin()) {
- admins.add(admin.getParentActiveAdmin());
+ mInjector.binderWithCleanCallingIdentity(() -> {
+ for (UserInfo userInfo : mUserManager.getProfiles(userHandle)) {
+ DevicePolicyData policy = getUserDataUnchecked(userInfo.id);
+ if (userInfo.id == userHandle) {
+ admins.addAll(policy.mAdminList);
+ } else if (userInfo.isManagedProfile()) {
+ // For managed profiles, policies set on the parent profile will be included
+ for (int i = 0; i < policy.mAdminList.size(); i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (admin.hasParentActiveAdmin()) {
+ admins.add(admin.getParentActiveAdmin());
+ }
}
+ } else {
+ Slog.w(LOG_TAG, "Unknown user type: " + userInfo);
}
- } else {
- Slog.w(LOG_TAG, "Unknown user type: " + userInfo);
}
- }
+ });
return admins;
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index ff8c209..c1ac55f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -394,6 +394,7 @@
mStartCount = SystemProperties.getInt(SYSPROP_START_COUNT, 0) + 1;
mRuntimeStartElapsedTime = SystemClock.elapsedRealtime();
mRuntimeStartUptime = SystemClock.uptimeMillis();
+ Process.setStartTimes(mRuntimeStartElapsedTime, mRuntimeStartUptime);
// Remember if it's runtime restart(when sys.boot_completed is already set) or reboot
// We don't use "mStartCount > 1" here because it'll be wrong on a FDE device.
diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java
index 5d7f4e9..983a639 100644
--- a/services/people/java/com/android/server/people/PeopleService.java
+++ b/services/people/java/com/android/server/people/PeopleService.java
@@ -17,6 +17,7 @@
package com.android.server.people;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.prediction.AppPredictionContext;
import android.app.prediction.AppPredictionSessionId;
@@ -145,14 +146,15 @@
mDataManager.pruneDataForUser(userId, signal);
}
+ @Nullable
@Override
- public byte[] backupConversationInfos(@UserIdInt int userId) {
- return new byte[0];
+ public byte[] getBackupPayload(@UserIdInt int userId) {
+ return mDataManager.getBackupPayload(userId);
}
@Override
- public void restoreConversationInfos(@UserIdInt int userId, @NonNull String key,
- @NonNull byte[] payload) {
+ public void restore(@UserIdInt int userId, @NonNull byte[] payload) {
+ mDataManager.restore(userId, payload);
}
@VisibleForTesting
diff --git a/services/people/java/com/android/server/people/data/ConversationInfo.java b/services/people/java/com/android/server/people/data/ConversationInfo.java
index 41bc361..dc3fa2a 100644
--- a/services/people/java/com/android/server/people/data/ConversationInfo.java
+++ b/services/people/java/com/android/server/people/data/ConversationInfo.java
@@ -24,6 +24,7 @@
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutInfo.ShortcutFlags;
import android.net.Uri;
+import android.text.TextUtils;
import android.util.Slog;
import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
@@ -31,6 +32,10 @@
import com.android.internal.util.Preconditions;
import com.android.server.people.ConversationInfoProto;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -280,6 +285,25 @@
}
}
+ @Nullable
+ byte[] getBackupPayload() {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream out = new DataOutputStream(baos);
+ try {
+ out.writeUTF(mShortcutId);
+ out.writeUTF(mLocusId != null ? mLocusId.getId() : "");
+ out.writeUTF(mContactUri != null ? mContactUri.toString() : "");
+ out.writeUTF(mNotificationChannelId != null ? mNotificationChannelId : "");
+ out.writeInt(mShortcutFlags);
+ out.writeInt(mConversationFlags);
+ out.writeUTF(mContactPhoneNumber != null ? mContactPhoneNumber : "");
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to write fields to backup payload.", e);
+ return null;
+ }
+ return baos.toByteArray();
+ }
+
/** Reads from {@link ProtoInputStream} and constructs a {@link ConversationInfo}. */
@NonNull
static ConversationInfo readFromProto(@NonNull ProtoInputStream protoInputStream)
@@ -331,6 +355,37 @@
return builder.build();
}
+ @Nullable
+ static ConversationInfo readFromBackupPayload(@NonNull byte[] payload) {
+ ConversationInfo.Builder builder = new ConversationInfo.Builder();
+ DataInputStream in = new DataInputStream(new ByteArrayInputStream(payload));
+ try {
+ builder.setShortcutId(in.readUTF());
+ String locusId = in.readUTF();
+ if (!TextUtils.isEmpty(locusId)) {
+ builder.setLocusId(new LocusId(locusId));
+ }
+ String contactUri = in.readUTF();
+ if (!TextUtils.isEmpty(contactUri)) {
+ builder.setContactUri(Uri.parse(contactUri));
+ }
+ String notificationChannelId = in.readUTF();
+ if (!TextUtils.isEmpty(notificationChannelId)) {
+ builder.setNotificationChannelId(notificationChannelId);
+ }
+ builder.setShortcutFlags(in.readInt());
+ builder.setConversationFlags(in.readInt());
+ String contactPhoneNumber = in.readUTF();
+ if (!TextUtils.isEmpty(contactPhoneNumber)) {
+ builder.setContactPhoneNumber(contactPhoneNumber);
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to read conversation info fields from backup payload.", e);
+ return null;
+ }
+ return builder.build();
+ }
+
/**
* Builder class for {@link ConversationInfo} objects.
*/
diff --git a/services/people/java/com/android/server/people/data/ConversationStore.java b/services/people/java/com/android/server/people/data/ConversationStore.java
index 89c4972..2f2a95c 100644
--- a/services/people/java/com/android/server/people/data/ConversationStore.java
+++ b/services/people/java/com/android/server/people/data/ConversationStore.java
@@ -31,6 +31,10 @@
import com.google.android.collect.Lists;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@@ -48,6 +52,8 @@
private static final String CONVERSATIONS_FILE_NAME = "conversations";
+ private static final int CONVERSATION_INFOS_END_TOKEN = -1;
+
// Shortcut ID -> Conversation Info
@GuardedBy("this")
private final Map<String, ConversationInfo> mConversationInfoMap = new ArrayMap<>();
@@ -195,6 +201,51 @@
mConversationInfosProtoDiskReadWriter.deleteConversationsFile();
}
+ @Nullable
+ synchronized byte[] getBackupPayload() {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream conversationInfosOut = new DataOutputStream(baos);
+ for (ConversationInfo conversationInfo : mConversationInfoMap.values()) {
+ byte[] backupPayload = conversationInfo.getBackupPayload();
+ if (backupPayload == null) {
+ continue;
+ }
+ try {
+ conversationInfosOut.writeInt(backupPayload.length);
+ conversationInfosOut.write(backupPayload);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to write conversation info to backup payload.", e);
+ return null;
+ }
+ }
+ try {
+ conversationInfosOut.writeInt(CONVERSATION_INFOS_END_TOKEN);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to write conversation infos end token to backup payload.", e);
+ return null;
+ }
+ return baos.toByteArray();
+ }
+
+ synchronized void restore(@NonNull byte[] payload) {
+ DataInputStream in = new DataInputStream(new ByteArrayInputStream(payload));
+ try {
+ for (int conversationInfoSize = in.readInt();
+ conversationInfoSize != CONVERSATION_INFOS_END_TOKEN;
+ conversationInfoSize = in.readInt()) {
+ byte[] conversationInfoPayload = new byte[conversationInfoSize];
+ in.readFully(conversationInfoPayload, 0, conversationInfoSize);
+ ConversationInfo conversationInfo = ConversationInfo.readFromBackupPayload(
+ conversationInfoPayload);
+ if (conversationInfo != null) {
+ addOrUpdate(conversationInfo);
+ }
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to read conversation info from payload.", e);
+ }
+ }
+
@MainThread
private synchronized void updateConversationsInMemory(
@NonNull ConversationInfo conversationInfo) {
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index 3a34c6a..e4ce6ba 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -334,6 +334,25 @@
});
}
+ /** Retrieves a backup payload blob for specified user id. */
+ @Nullable
+ public byte[] getBackupPayload(@UserIdInt int userId) {
+ UserData userData = getUnlockedUserData(userId);
+ if (userData == null) {
+ return null;
+ }
+ return userData.getBackupPayload();
+ }
+
+ /** Attempts to restore data for the specified user id. */
+ public void restore(@UserIdInt int userId, @NonNull byte[] payload) {
+ UserData userData = getUnlockedUserData(userId);
+ if (userData == null) {
+ return;
+ }
+ userData.restore(payload);
+ }
+
private int mimeTypeToShareEventType(String mimeType) {
if (mimeType.startsWith("text/")) {
return Event.TYPE_SHARE_TEXT;
diff --git a/services/people/java/com/android/server/people/data/UserData.java b/services/people/java/com/android/server/people/data/UserData.java
index 0f8b91b..ed8c595 100644
--- a/services/people/java/com/android/server/people/data/UserData.java
+++ b/services/people/java/com/android/server/people/data/UserData.java
@@ -22,8 +22,14 @@
import android.os.Environment;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.Slog;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
import java.io.File;
+import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Consumer;
@@ -31,6 +37,10 @@
/** The data associated with a user profile. */
class UserData {
+ private static final String TAG = UserData.class.getSimpleName();
+
+ private static final int CONVERSATIONS_END_TOKEN = -1;
+
private final @UserIdInt int mUserId;
private final File mPerUserPeopleDataDir;
@@ -125,6 +135,48 @@
return mDefaultSmsApp != null ? getPackageData(mDefaultSmsApp) : null;
}
+ @Nullable
+ byte[] getBackupPayload() {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream out = new DataOutputStream(baos);
+ for (PackageData packageData : mPackageDataMap.values()) {
+ try {
+ byte[] conversationsBackupPayload =
+ packageData.getConversationStore().getBackupPayload();
+ out.writeInt(conversationsBackupPayload.length);
+ out.write(conversationsBackupPayload);
+ out.writeUTF(packageData.getPackageName());
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to write conversations to backup payload.", e);
+ return null;
+ }
+ }
+ try {
+ out.writeInt(CONVERSATIONS_END_TOKEN);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to write conversations end token to backup payload.", e);
+ return null;
+ }
+ return baos.toByteArray();
+ }
+
+ void restore(@NonNull byte[] payload) {
+ DataInputStream in = new DataInputStream(new ByteArrayInputStream(payload));
+ try {
+ for (int conversationsPayloadSize = in.readInt();
+ conversationsPayloadSize != CONVERSATIONS_END_TOKEN;
+ conversationsPayloadSize = in.readInt()) {
+ byte[] conversationsPayload = new byte[conversationsPayloadSize];
+ in.readFully(conversationsPayload, 0, conversationsPayloadSize);
+ String packageName = in.readUTF();
+ getOrCreatePackageData(packageName).getConversationStore().restore(
+ conversationsPayload);
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to restore conversations from backup payload.", e);
+ }
+ }
+
private PackageData createPackageData(String packageName) {
return new PackageData(packageName, mUserId, this::isDefaultDialer, this::isDefaultSmsApp,
mScheduledExecutorService, mPerUserPeopleDataDir);
diff --git a/services/robotests/Android.bp b/services/robotests/Android.bp
index d2f86ee..602e4e1 100644
--- a/services/robotests/Android.bp
+++ b/services/robotests/Android.bp
@@ -26,6 +26,8 @@
"services.core",
"services.net",
],
+
+ libs: ["ike-stubs"],
}
//##################################################################
diff --git a/services/robotests/backup/Android.bp b/services/robotests/backup/Android.bp
index ef0ca66..5160eae 100644
--- a/services/robotests/backup/Android.bp
+++ b/services/robotests/backup/Android.bp
@@ -28,6 +28,8 @@
"services.core",
"services.net",
],
+
+ libs: ["ike-stubs"],
}
//##################################################################
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index 00495f3..730f303 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -107,6 +107,7 @@
.when(() -> LocalServices.getService(AppStandbyInternal.class));
doReturn(mock(UsageStatsManagerInternal.class))
.when(() -> LocalServices.getService(UsageStatsManagerInternal.class));
+ when(mContext.getString(anyInt())).thenReturn("some_test_string");
// Called in BackgroundJobsController constructor.
doReturn(mock(AppStateTracker.class))
.when(() -> LocalServices.getService(AppStateTracker.class));
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index 407f67e..44f4ccf 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -24,6 +24,7 @@
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
+import android.app.compat.ChangeIdStateCache;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -74,6 +75,7 @@
// Assume userdebug/eng non-final build
when(mBuildClassifier.isDebuggableBuild()).thenReturn(true);
when(mBuildClassifier.isFinalBuild()).thenReturn(false);
+ ChangeIdStateCache.disable();
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
index 4a686ee..53b90f2 100644
--- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
@@ -62,6 +62,7 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ android.app.compat.ChangeIdStateCache.disable();
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mPackageManager.getPackageUid(eq(PACKAGE_NAME), eq(0))).thenThrow(
new PackageManager.NameNotFoundException());
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
index 40b0e71..de2addf 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
@@ -39,14 +39,13 @@
import com.android.frameworks.servicestests.R;
import com.android.server.LocalServices;
import com.android.server.SystemService;
-import com.android.server.devicepolicy.DevicePolicyManagerServiceTestable.OwnersTestable;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
-// TODO (b/149818286): Fix old test cases and put the whole test into presubmit.
+@Presubmit
public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase {
private static final String USER_TYPE_EMPTY = "";
@@ -78,7 +77,7 @@
// Create the legacy owners & policies file.
DpmTestUtils.writeToFile(
- (new File(getServices().dataDir, OwnersTestable.LEGACY_FILE)).getAbsoluteFile(),
+ (new File(getServices().dataDir, "device_owner.xml")).getAbsoluteFile(),
DpmTestUtils.readAsset(mRealTestContext,
"DevicePolicyManagerServiceMigrationTest/legacy_device_owner.xml"));
@@ -193,8 +192,7 @@
// Check the new owner restrictions.
DpmTestUtils.assertRestrictions(
DpmTestUtils.newRestrictions(
- UserManager.DISALLOW_ADD_USER,
- UserManager.DISALLOW_ADD_MANAGED_PROFILE
+ UserManager.DISALLOW_ADD_USER
),
dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions());
@@ -216,7 +214,7 @@
// Create the legacy owners & policies file.
DpmTestUtils.writeToFile(
- (new File(getServices().dataDir, OwnersTestable.LEGACY_FILE)).getAbsoluteFile(),
+ (new File(getServices().dataDir, "device_owner.xml")).getAbsoluteFile(),
DpmTestUtils.readAsset(mRealTestContext,
"DevicePolicyManagerServiceMigrationTest2/legacy_device_owner.xml"));
@@ -346,7 +344,6 @@
assertTrue(alreadySet.contains(UserManager.DISALLOW_BLUETOOTH_SHARING));
}
- @Presubmit
@SmallTest
public void testCompMigrationUnAffiliated_skipped() throws Exception {
prepareAdmin1AsDo();
@@ -359,7 +356,6 @@
assertTrue(dpms.mOwners.hasDeviceOwner());
}
- @Presubmit
@SmallTest
public void testCompMigrationAffiliated() throws Exception {
prepareAdmin1AsDo();
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index 853151f..5ad81b2 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -66,7 +66,6 @@
* Overrides {@link #Owners} for dependency injection.
*/
public static class OwnersTestable extends Owners {
- public static final String LEGACY_FILE = "legacy.xml";
public OwnersTestable(MockSystemServices services) {
super(services.userManager, services.userManagerInternal,
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index feae1e1..08bd1ee 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -144,4 +144,49 @@
Truth.assertThat(desiredSpecs.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
Truth.assertThat(desiredSpecs.baseModeId).isEqualTo(60);
}
+
+ @Test
+ public void testBrightnessHasLowerPriorityThanUser() {
+ assertTrue(Vote.PRIORITY_LOW_BRIGHTNESS < Vote.PRIORITY_APP_REQUEST_REFRESH_RATE);
+ assertTrue(Vote.PRIORITY_LOW_BRIGHTNESS < Vote.PRIORITY_APP_REQUEST_SIZE);
+
+ int displayId = 0;
+ DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(60, 90);
+ SparseArray<Vote> votes = new SparseArray<>();
+ SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
+ votesByDisplay.put(displayId, votes);
+ votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 90));
+ votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60));
+ director.injectVotesByDisplay(votesByDisplay);
+ DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+ Truth.assertThat(desiredSpecs.refreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
+ Truth.assertThat(desiredSpecs.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
+
+ votes.clear();
+ votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 90));
+ votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(90, 90));
+ director.injectVotesByDisplay(votesByDisplay);
+ desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+ Truth.assertThat(desiredSpecs.refreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
+ Truth.assertThat(desiredSpecs.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90);
+
+
+ votes.clear();
+ votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(90, 90));
+ votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60));
+ director.injectVotesByDisplay(votesByDisplay);
+ desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+ Truth.assertThat(desiredSpecs.refreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
+ Truth.assertThat(desiredSpecs.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90);
+
+ votes.clear();
+ votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 60));
+ votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(90, 90));
+ director.injectVotesByDisplay(votesByDisplay);
+ desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+ Truth.assertThat(desiredSpecs.refreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
+ Truth.assertThat(desiredSpecs.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
+
+
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
index be873bd..d9101bf 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -60,6 +60,7 @@
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
+import android.provider.Settings;
import androidx.test.InstrumentationRegistry;
@@ -119,7 +120,6 @@
private static final String PLAY_STORE_PKG = "com.android.vending";
private static final String ADB_INSTALLER = "adb";
private static final String PLAY_STORE_CERT = "play_store_cert";
- private static final String ADB_CERT = "";
@org.junit.Rule
public MockitoRule mMockitoRule = MockitoJUnit.rule();
@@ -137,11 +137,12 @@
@Mock
Handler mHandler;
+ private final Context mRealContext = InstrumentationRegistry.getTargetContext();
+
private PackageManager mSpyPackageManager;
private File mTestApk;
private File mTestApkTwoCerts;
- private final Context mRealContext = InstrumentationRegistry.getTargetContext();
// under test
private AppIntegrityManagerServiceImpl mService;
@@ -163,8 +164,7 @@
mPackageManagerInternal,
mRuleEvaluationEngine,
mIntegrityFileManager,
- mHandler,
- /* checkIntegrityForRuleProviders= */ true);
+ mHandler);
mSpyPackageManager = spy(mRealContext.getPackageManager());
// setup mocks to prevent NPE
@@ -172,6 +172,9 @@
when(mMockContext.getResources()).thenReturn(mMockResources);
when(mMockResources.getStringArray(anyInt())).thenReturn(new String[]{});
when(mIntegrityFileManager.initialized()).thenReturn(true);
+ // These are needed to override the Settings.Global.get result.
+ when(mMockContext.getContentResolver()).thenReturn(mRealContext.getContentResolver());
+ setIntegrityCheckIncludesRuleProvider(true);
}
@After
@@ -201,6 +204,7 @@
@Test
public void updateRuleSet_notSystemApp() throws Exception {
whitelistUsAsRuleProvider();
+ makeUsSystemApp(false);
Rule rule =
new Rule(
new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true),
@@ -411,14 +415,7 @@
public void verifierAsInstaller_skipIntegrityVerification() throws Exception {
whitelistUsAsRuleProvider();
makeUsSystemApp();
- mService =
- new AppIntegrityManagerServiceImpl(
- mMockContext,
- mPackageManagerInternal,
- mRuleEvaluationEngine,
- mIntegrityFileManager,
- mHandler,
- /* checkIntegrityForRuleProviders= */ false);
+ setIntegrityCheckIncludesRuleProvider(false);
ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
verify(mMockContext, atLeastOnce())
@@ -460,12 +457,21 @@
}
private void makeUsSystemApp() throws Exception {
+ makeUsSystemApp(true);
+ }
+
+ private void makeUsSystemApp(boolean isSystemApp) throws Exception {
PackageInfo packageInfo =
mRealContext.getPackageManager().getPackageInfo(TEST_FRAMEWORK_PACKAGE, 0);
- packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+ if (isSystemApp) {
+ packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+ } else {
+ packageInfo.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
+ }
doReturn(packageInfo)
.when(mSpyPackageManager)
.getPackageInfo(eq(TEST_FRAMEWORK_PACKAGE), anyInt());
+ when(mMockContext.getPackageManager()).thenReturn(mSpyPackageManager);
}
private Intent makeVerificationIntent() throws Exception {
@@ -492,4 +498,13 @@
intent.putExtra(Intent.EXTRA_LONG_VERSION_CODE, VERSION_CODE);
return intent;
}
+
+ private void setIntegrityCheckIncludesRuleProvider(boolean shouldInclude) throws Exception {
+ int value = shouldInclude ? 1 : 0;
+ Settings.Global.putInt(mRealContext.getContentResolver(),
+ Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER, value);
+ assertThat(Settings.Global.getInt(mRealContext.getContentResolver(),
+ Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER, -1) == 1).isEqualTo(
+ shouldInclude);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
index 44d88d2..1cf8525 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
@@ -19,12 +19,16 @@
import static android.content.pm.UserInfo.FLAG_FULL;
import static android.content.pm.UserInfo.FLAG_PRIMARY;
import static android.content.pm.UserInfo.FLAG_PROFILE;
+import static android.os.UserHandle.USER_SYSTEM;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -32,8 +36,10 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertNull;
import android.content.Context;
+import android.content.ContextWrapper;
import android.content.pm.UserInfo;
import android.hardware.rebootescrow.IRebootEscrow;
import android.os.RemoteException;
@@ -49,6 +55,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import java.io.File;
import java.util.ArrayList;
@@ -76,16 +83,26 @@
LockSettingsStorageTestable mStorage;
+ private MockableRebootEscrowInjected mInjected;
private RebootEscrowManager mService;
+ public interface MockableRebootEscrowInjected {
+ int getBootCount();
+
+ void reportMetric(boolean success);
+ }
+
static class MockInjector extends RebootEscrowManager.Injector {
private final IRebootEscrow mRebootEscrow;
private final UserManager mUserManager;
+ private final MockableRebootEscrowInjected mInjected;
- MockInjector(Context context, UserManager userManager, IRebootEscrow rebootEscrow) {
+ MockInjector(Context context, UserManager userManager, IRebootEscrow rebootEscrow,
+ MockableRebootEscrowInjected injected) {
super(context);
mRebootEscrow = rebootEscrow;
mUserManager = userManager;
+ mInjected = injected;
}
@Override
@@ -97,11 +114,21 @@
public IRebootEscrow getRebootEscrow() {
return mRebootEscrow;
}
+
+ @Override
+ public int getBootCount() {
+ return mInjected.getBootCount();
+ }
+
+ @Override
+ public void reportMetric(boolean success) {
+ mInjected.reportMetric(success);
+ }
}
@Before
public void setUp_baseServices() throws Exception {
- mContext = mock(Context.class);
+ mContext = new ContextWrapper(InstrumentationRegistry.getContext());
mUserManager = mock(UserManager.class);
mCallbacks = mock(RebootEscrowManager.Callbacks.class);
mRebootEscrow = mock(IRebootEscrow.class);
@@ -119,8 +146,9 @@
when(mCallbacks.isUserSecure(WORK_PROFILE_USER_ID)).thenReturn(true);
when(mCallbacks.isUserSecure(NONSECURE_SECONDARY_USER_ID)).thenReturn(false);
when(mCallbacks.isUserSecure(SECURE_SECONDARY_USER_ID)).thenReturn(true);
- mService = new RebootEscrowManager(new MockInjector(mContext, mUserManager, mRebootEscrow),
- mCallbacks, mStorage);
+ mInjected = mock(MockableRebootEscrowInjected.class);
+ mService = new RebootEscrowManager(new MockInjector(mContext, mUserManager, mRebootEscrow,
+ mInjected), mCallbacks, mStorage);
}
@Test
@@ -160,7 +188,11 @@
verify(mockListener).onPreparedForReboot(eq(true));
verify(mRebootEscrow, never()).storeKey(any());
+ assertNull(
+ mStorage.getString(RebootEscrowManager.REBOOT_ESCROW_ARMED_KEY, null, USER_SYSTEM));
assertTrue(mService.armRebootEscrowIfNeeded());
+ assertNotNull(
+ mStorage.getString(RebootEscrowManager.REBOOT_ESCROW_ARMED_KEY, null, USER_SYSTEM));
verify(mRebootEscrow).storeKey(any());
assertTrue(mStorage.hasRebootEscrow(PRIMARY_USER_ID));
@@ -180,7 +212,15 @@
FAKE_AUTH_TOKEN);
verify(mRebootEscrow, never()).storeKey(any());
+ assertTrue(mStorage.hasRebootEscrow(PRIMARY_USER_ID));
+ assertTrue(mStorage.hasRebootEscrow(SECURE_SECONDARY_USER_ID));
+ assertFalse(mStorage.hasRebootEscrow(NONSECURE_SECONDARY_USER_ID));
+
+ assertNull(
+ mStorage.getString(RebootEscrowManager.REBOOT_ESCROW_ARMED_KEY, null, USER_SYSTEM));
assertTrue(mService.armRebootEscrowIfNeeded());
+ assertNotNull(
+ mStorage.getString(RebootEscrowManager.REBOOT_ESCROW_ARMED_KEY, null, USER_SYSTEM));
verify(mRebootEscrow, times(1)).storeKey(any());
assertTrue(mStorage.hasRebootEscrow(PRIMARY_USER_ID));
@@ -200,4 +240,105 @@
assertFalse(mService.armRebootEscrowIfNeeded());
verifyNoMoreInteractions(mRebootEscrow);
}
+
+ @Test
+ public void loadRebootEscrowDataIfAvailable_NothingAvailable_Success() throws Exception {
+ mService.loadRebootEscrowDataIfAvailable();
+ }
+
+ @Test
+ public void loadRebootEscrowDataIfAvailable_Success() throws Exception {
+ when(mInjected.getBootCount()).thenReturn(0);
+
+ RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+ mService.setRebootEscrowListener(mockListener);
+ mService.prepareRebootEscrow();
+
+ clearInvocations(mRebootEscrow);
+ mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+ verify(mockListener).onPreparedForReboot(eq(true));
+
+ verify(mRebootEscrow, never()).storeKey(any());
+
+ ArgumentCaptor<byte[]> keyByteCaptor = ArgumentCaptor.forClass(byte[].class);
+ assertTrue(mService.armRebootEscrowIfNeeded());
+ verify(mRebootEscrow).storeKey(keyByteCaptor.capture());
+
+ assertTrue(mStorage.hasRebootEscrow(PRIMARY_USER_ID));
+ assertFalse(mStorage.hasRebootEscrow(NONSECURE_SECONDARY_USER_ID));
+
+ // pretend reboot happens here
+
+ when(mInjected.getBootCount()).thenReturn(1);
+ ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
+ doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture());
+ when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> keyByteCaptor.getValue());
+
+ mService.loadRebootEscrowDataIfAvailable();
+ verify(mRebootEscrow).retrieveKey();
+ assertTrue(metricsSuccessCaptor.getValue());
+ }
+
+ @Test
+ public void loadRebootEscrowDataIfAvailable_TooManyBootsInBetween_NoMetrics() throws Exception {
+ when(mInjected.getBootCount()).thenReturn(0);
+
+ RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+ mService.setRebootEscrowListener(mockListener);
+ mService.prepareRebootEscrow();
+
+ clearInvocations(mRebootEscrow);
+ mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+ verify(mockListener).onPreparedForReboot(eq(true));
+
+ verify(mRebootEscrow, never()).storeKey(any());
+
+ ArgumentCaptor<byte[]> keyByteCaptor = ArgumentCaptor.forClass(byte[].class);
+ assertTrue(mService.armRebootEscrowIfNeeded());
+ verify(mRebootEscrow).storeKey(keyByteCaptor.capture());
+
+ assertTrue(mStorage.hasRebootEscrow(PRIMARY_USER_ID));
+ assertFalse(mStorage.hasRebootEscrow(NONSECURE_SECONDARY_USER_ID));
+
+ // pretend reboot happens here
+
+ when(mInjected.getBootCount()).thenReturn(10);
+ when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> keyByteCaptor.getValue());
+
+ mService.loadRebootEscrowDataIfAvailable();
+ verify(mRebootEscrow).retrieveKey();
+ verify(mInjected, never()).reportMetric(anyBoolean());
+ }
+
+
+ @Test
+ public void loadRebootEscrowDataIfAvailable_RestoreUnsuccessful_Failure() throws Exception {
+ when(mInjected.getBootCount()).thenReturn(0);
+
+ RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+ mService.setRebootEscrowListener(mockListener);
+ mService.prepareRebootEscrow();
+
+ clearInvocations(mRebootEscrow);
+ mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+ verify(mockListener).onPreparedForReboot(eq(true));
+
+ verify(mRebootEscrow, never()).storeKey(any());
+
+ assertTrue(mService.armRebootEscrowIfNeeded());
+ verify(mRebootEscrow).storeKey(any());
+
+ assertTrue(mStorage.hasRebootEscrow(PRIMARY_USER_ID));
+ assertFalse(mStorage.hasRebootEscrow(NONSECURE_SECONDARY_USER_ID));
+
+ // pretend reboot happens here.
+
+ when(mInjected.getBootCount()).thenReturn(1);
+ ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
+ doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture());
+ when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> new byte[32]);
+ mService.loadRebootEscrowDataIfAvailable();
+ verify(mRebootEscrow).retrieveKey();
+ assertFalse(metricsSuccessCaptor.getValue());
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt b/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt
index 233e16c..e08eea2 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt
@@ -24,6 +24,7 @@
import org.junit.Rule
import org.junit.Test
import org.junit.rules.ExpectedException
+import java.lang.UnsupportedOperationException
class OverlayActorEnforcerTests {
companion object {
@@ -159,7 +160,7 @@
private val hasPermission: Boolean = false,
private val overlayableInfo: OverlayableInfo? = null,
private vararg val packageNames: String = arrayOf("com.test.actor.one")
- ) : OverlayActorEnforcer.VerifyCallback {
+ ) : OverlayableInfoCallback {
override fun getNamedActors() = if (isActor) {
mapOf(NAMESPACE to mapOf(ACTOR_NAME to ACTOR_PKG_NAME))
@@ -169,7 +170,7 @@
override fun getOverlayableForTarget(
packageName: String,
- targetOverlayableName: String?,
+ targetOverlayableName: String,
userId: Int
) = overlayableInfo
@@ -193,5 +194,9 @@
throw SecurityException()
}
}
+
+ override fun signaturesMatching(pkgName1: String, pkgName2: String, userId: Int): Boolean {
+ throw UnsupportedOperationException()
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
index a428a97..cd73432 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
@@ -24,30 +24,16 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import android.annotation.NonNull;
import android.content.om.OverlayInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.util.ArraySet;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.content.om.OverlayConfig;
-
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Set;
@RunWith(AndroidJUnit4.class)
public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTestsBase {
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
index a753aac..820e61c 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
@@ -23,10 +23,13 @@
import android.annotation.NonNull;
import android.content.om.OverlayInfo;
import android.content.om.OverlayInfo.State;
+import android.content.om.OverlayableInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.util.ArraySet;
+import androidx.annotation.Nullable;
+
import com.android.internal.content.om.OverlayConfig;
import org.junit.Before;
@@ -35,6 +38,7 @@
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@@ -281,8 +285,8 @@
}
}
- static final class DummyPackageManagerHelper implements
- OverlayManagerServiceImpl.PackageManagerHelper {
+ static final class DummyPackageManagerHelper implements PackageManagerHelper,
+ OverlayableInfoCallback {
private final DummyDeviceState mState;
private DummyPackageManagerHelper(DummyDeviceState state) {
@@ -320,6 +324,35 @@
.map(p -> getPackageInfo(p.packageName, p.userId))
.collect(Collectors.toList());
}
+
+ @Nullable
+ @Override
+ public OverlayableInfo getOverlayableForTarget(@NonNull String packageName,
+ @NonNull String targetOverlayableName, int userId) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Nullable
+ @Override
+ public String[] getPackagesForUid(int uid) {
+ throw new UnsupportedOperationException();
+ }
+
+ @NonNull
+ @Override
+ public Map<String, Map<String, String>> getNamedActors() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean doesTargetDefineOverlayable(String targetPackageName, int userId) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void enforcePermission(String permission, String message) throws SecurityException {
+ throw new UnsupportedOperationException();
+ }
}
static class DummyIdmapManager extends IdmapManager {
diff --git a/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java b/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java
index d138700..4e63237 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java
@@ -284,6 +284,30 @@
assertEquals(in2, out2);
}
+ @Test
+ public void testBackupAndRestore() {
+ ConversationInfo in1 = buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI,
+ PHONE_NUMBER, NOTIFICATION_CHANNEL_ID);
+ ConversationInfo in2 = buildConversationInfo(SHORTCUT_ID_2, LOCUS_ID_2, CONTACT_URI_2,
+ PHONE_NUMBER_2, NOTIFICATION_CHANNEL_ID_2);
+ mConversationStore.addOrUpdate(in1);
+ mConversationStore.addOrUpdate(in2);
+
+ byte[] backupPayload = mConversationStore.getBackupPayload();
+ assertNotNull(backupPayload);
+
+ ConversationStore conversationStore = new ConversationStore(mFile,
+ mMockScheduledExecutorService);
+ ConversationInfo out1 = conversationStore.getConversation(SHORTCUT_ID);
+ assertNull(out1);
+
+ conversationStore.restore(backupPayload);
+ out1 = conversationStore.getConversation(SHORTCUT_ID);
+ ConversationInfo out2 = conversationStore.getConversation(SHORTCUT_ID_2);
+ assertEquals(in1, out1);
+ assertEquals(in2, out2);
+ }
+
private void resetConversationStore() {
mFile.mkdir();
mMockScheduledExecutorService = new MockScheduledExecutorService();
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index f0b7d20..e57df24 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -653,6 +653,33 @@
assertTrue(activeTimeSlots.isEmpty());
}
+ @Test
+ public void testBackupAndRestoration()
+ throws IntentFilter.MalformedMimeTypeException {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ null);
+ AppTarget appTarget = new AppTarget.Builder(new AppTargetId(TEST_SHORTCUT_ID), shortcut)
+ .build();
+ AppTargetEvent appTargetEvent =
+ new AppTargetEvent.Builder(appTarget, AppTargetEvent.ACTION_LAUNCH)
+ .setLaunchLocation(ChooserActivity.LAUNCH_LOCATON_DIRECT_SHARE)
+ .build();
+ IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SEND, "image/jpg");
+
+ mDataManager.reportShareTargetEvent(appTargetEvent, intentFilter);
+ byte[] payload = mDataManager.getBackupPayload(USER_ID_PRIMARY);
+
+ DataManager dataManager = new DataManager(mContext, mInjector);
+ dataManager.onUserUnlocked(USER_ID_PRIMARY);
+ dataManager.restore(USER_ID_PRIMARY, payload);
+ ConversationInfo conversationInfo = dataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY)
+ .getConversationStore()
+ .getConversation(TEST_SHORTCUT_ID);
+ assertNotNull(conversationInfo);
+ assertEquals(conversationInfo.getShortcutId(), TEST_SHORTCUT_ID);
+ }
+
private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
LocalServices.removeServiceForTest(clazz);
LocalServices.addService(clazz, mock);
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
index c56034a..06b5fe4 100644
--- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
@@ -60,6 +60,8 @@
import android.os.IHwBinder;
import android.os.IHwInterface;
import android.os.RemoteException;
+import android.os.SharedMemory;
+import android.system.ErrnoException;
import android.util.Pair;
import org.junit.Before;
@@ -71,6 +73,9 @@
import org.mockito.Mockito;
import org.mockito.stubbing.Answer;
+import java.io.FileDescriptor;
+import java.nio.ByteBuffer;
+
@RunWith(Parameterized.class)
public class SoundTriggerMiddlewareImplTest {
private static final String TAG = "SoundTriggerMiddlewareImplTest";
@@ -104,12 +109,25 @@
return createSoundModel(SoundModelType.GENERIC);
}
+ private static FileDescriptor byteArrayToFileDescriptor(byte[] data) {
+ try {
+ SharedMemory shmem = SharedMemory.create("", data.length);
+ ByteBuffer buffer = shmem.mapReadWrite();
+ buffer.put(data);
+ return shmem.getFileDescriptor();
+ } catch (ErrnoException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
private static SoundModel createSoundModel(int type) {
SoundModel model = new SoundModel();
model.type = type;
model.uuid = "12345678-2345-3456-4567-abcdef987654";
model.vendorUuid = "87654321-5432-6543-7654-456789fedcba";
- model.data = new byte[]{91, 92, 93, 94, 95};
+ byte[] data = new byte[]{91, 92, 93, 94, 95};
+ model.data = byteArrayToFileDescriptor(data);
+ model.dataSize = data.length;
return model;
}
diff --git a/services/tests/uiservicestests/AndroidManifest.xml b/services/tests/uiservicestests/AndroidManifest.xml
index dab0a5f..767857b 100644
--- a/services/tests/uiservicestests/AndroidManifest.xml
+++ b/services/tests/uiservicestests/AndroidManifest.xml
@@ -23,7 +23,6 @@
<uses-permission android:name="android.permission.MANAGE_USERS" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<uses-permission android:name="android.permission.ACCESS_NOTIFICATIONS" />
- <uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
<uses-permission android:name="android.permission.ACCESS_VOICE_INTERACTION_SERVICE" />
<uses-permission android:name="android.permission.DEVICE_POWER" />
diff --git a/services/tests/uiservicestests/src/com/android/internal/logging/InstanceIdSequenceFake.java b/services/tests/uiservicestests/src/com/android/internal/logging/InstanceIdSequenceFake.java
index 1629ef0..6e7df05 100644
--- a/services/tests/uiservicestests/src/com/android/internal/logging/InstanceIdSequenceFake.java
+++ b/services/tests/uiservicestests/src/com/android/internal/logging/InstanceIdSequenceFake.java
@@ -17,7 +17,7 @@
package com.android.internal.logging;
/**
- * A fake implementation of InstanceIdSequence that returns 0, 1, 2, ...
+ * A fake implementation of InstanceIdSequence that returns 1, 2, ...
*/
public class InstanceIdSequenceFake extends InstanceIdSequence {
@@ -25,13 +25,13 @@
super(instanceIdMax);
}
- private int mNextId = 0;
+ private int mNextId = 1;
@Override
public InstanceId newInstanceId() {
synchronized (this) {
if (mNextId >= mInstanceIdMax) {
- mNextId = 0;
+ mNextId = 1;
}
return newInstanceIdInternal(mNextId++);
}
diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
index 22046a5..6a707eb 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
@@ -28,6 +28,8 @@
import android.content.res.Resources;
import android.os.Handler;
import android.os.PowerManager;
+import android.os.PowerManagerInternal;
+import android.os.PowerSaveState;
import android.os.RemoteException;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -42,6 +44,7 @@
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
+import java.util.function.Consumer;
import static android.app.UiModeManager.MODE_NIGHT_AUTO;
import static android.app.UiModeManager.MODE_NIGHT_CUSTOM;
@@ -79,29 +82,34 @@
@Mock
private Resources mResources;
@Mock
- TwilightManager mTwilightManager;
+ private TwilightManager mTwilightManager;
@Mock
- PowerManager.WakeLock mWakeLock;
+ private PowerManager.WakeLock mWakeLock;
@Mock
- AlarmManager mAlarmManager;
+ private AlarmManager mAlarmManager;
@Mock
- PowerManager mPowerManager;
+ private PowerManager mPowerManager;
@Mock
- TwilightState mTwilightState;
+ private TwilightState mTwilightState;
+ @Mock
+ PowerManagerInternal mLocalPowerManager;
private BroadcastReceiver mScreenOffCallback;
private BroadcastReceiver mTimeChangedCallback;
private AlarmManager.OnAlarmListener mCustomListener;
+ private Consumer<PowerSaveState> mPowerSaveConsumer;
@Before
public void setUp() {
initMocks(this);
- mUiManagerService = new UiModeManagerService(mContext,
- mWindowManager, mAlarmManager, mPowerManager,
- mWakeLock, mTwilightManager, true);
- mService = mUiManagerService.getService();
when(mContext.checkCallingOrSelfPermission(anyString()))
.thenReturn(PackageManager.PERMISSION_GRANTED);
+ doAnswer(inv -> {
+ mPowerSaveConsumer = (Consumer<PowerSaveState>) inv.getArgument(1);
+ return null;
+ }).when(mLocalPowerManager).registerLowPowerModeObserver(anyInt(), any());
+ when(mLocalPowerManager.getLowPowerState(anyInt()))
+ .thenReturn(new PowerSaveState.Builder().setBatterySaverEnabled(false).build());
when(mContext.getResources()).thenReturn(mResources);
when(mContext.getContentResolver()).thenReturn(mContentResolver);
when(mPowerManager.isInteractive()).thenReturn(true);
@@ -127,6 +135,14 @@
mCustomListener = () -> {};
return null;
}).when(mAlarmManager).cancel(eq(mCustomListener));
+
+ mUiManagerService = new UiModeManagerService(mContext,
+ mWindowManager, mAlarmManager, mPowerManager,
+ mWakeLock, mTwilightManager, mLocalPowerManager, true);
+ try {
+ mUiManagerService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+ } catch (SecurityException e) {/* ignore for permission denial */}
+ mService = mUiManagerService.getService();
}
@Test
@@ -151,6 +167,22 @@
}
@Test
+ public void autoNightModeSwitch_batterySaverOn() throws RemoteException {
+ mService.setNightMode(MODE_NIGHT_NO);
+ when(mTwilightState.isNight()).thenReturn(false);
+ mService.setNightMode(MODE_NIGHT_AUTO);
+
+ // night NO
+ assertFalse(isNightModeActivated());
+
+ mPowerSaveConsumer.accept(
+ new PowerSaveState.Builder().setBatterySaverEnabled(true).build());
+
+ // night YES
+ assertTrue(isNightModeActivated());
+ }
+
+ @Test
public void setAutoMode_clearCache() throws RemoteException {
try {
mService.setNightMode(MODE_NIGHT_AUTO);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index f029bc8..afd10dd 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -846,6 +846,18 @@
}
@Test
+ public void testPostSilently() throws Exception {
+ NotificationRecord r = getBuzzyNotification();
+ r.setPostSilently(true);
+
+ mService.buzzBeepBlinkLocked(r);
+
+ verifyNeverBeep();
+ assertFalse(r.isInterruptive());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
public void testGroupAlertSummarySilenceChild() throws Exception {
NotificationRecord child = getBeepyNotificationRecord("a", GROUP_ALERT_SUMMARY);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
index 99b4fd9..b7bcbfb 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
@@ -64,7 +64,6 @@
Context mContext;
@Mock
AlarmManager mAlarmManager;
- TestFileAttrProvider mFileAttrProvider;
NotificationHistoryDatabase mDataBase;
@@ -103,11 +102,9 @@
when(mContext.getUser()).thenReturn(getContext().getUser());
when(mContext.getPackageName()).thenReturn(getContext().getPackageName());
- mFileAttrProvider = new TestFileAttrProvider();
mRootDir = new File(mContext.getFilesDir(), "NotificationHistoryDatabaseTest");
- mDataBase = new NotificationHistoryDatabase(
- mContext, mFileWriteHandler, mRootDir, mFileAttrProvider);
+ mDataBase = new NotificationHistoryDatabase(mContext, mFileWriteHandler, mRootDir);
mDataBase.init();
}
@@ -127,7 +124,7 @@
// add 5 files with a creation date of "today"
for (long i = cal.getTimeInMillis(); i >= 5; i--) {
File file = mock(File.class);
- mFileAttrProvider.creationDates.put(file, i);
+ when(file.getName()).thenReturn(String.valueOf(i));
AtomicFile af = new AtomicFile(file);
expectedFiles.add(af);
mDataBase.mHistoryFiles.addLast(af);
@@ -137,7 +134,7 @@
// Add 5 more files more than retainDays old
for (int i = 5; i >= 0; i--) {
File file = mock(File.class);
- mFileAttrProvider.creationDates.put(file, cal.getTimeInMillis() - i);
+ when(file.getName()).thenReturn(String.valueOf(cal.getTimeInMillis() - i));
AtomicFile af = new AtomicFile(file);
mDataBase.mHistoryFiles.addLast(af);
}
@@ -331,13 +328,4 @@
verify(nh).removeConversationFromWrite("pkg", "convo");
verify(af, never()).startWrite();
}
-
- private class TestFileAttrProvider implements NotificationHistoryDatabase.FileAttrProvider {
- public Map<File, Long> creationDates = new HashMap<>();
-
- @Override
- public long getCreationTime(File file) {
- return creationDates.get(file);
- }
- }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index da0e03d..dc8d010 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -35,9 +35,12 @@
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.PendingIntent;
+import android.app.Person;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
+import android.content.pm.ShortcutInfo;
import android.graphics.Bitmap;
import android.graphics.drawable.Icon;
import android.os.Binder;
@@ -118,6 +121,7 @@
assertEquals(canBubble(i), ranking.canBubble());
assertEquals(visuallyInterruptive(i), ranking.visuallyInterruptive());
assertEquals(isConversation(i), ranking.isConversation());
+ assertEquals(getShortcutInfo(i).getId(), ranking.getShortcutInfo().getId());
}
}
@@ -186,7 +190,8 @@
(ArrayList) tweak.getSmartReplies(),
tweak.canBubble(),
tweak.visuallyInterruptive(),
- tweak.isConversation()
+ tweak.isConversation(),
+ tweak.getShortcutInfo()
);
assertNotEquals(nru, nru2);
}
@@ -264,7 +269,8 @@
getSmartReplies(key, i),
canBubble(i),
visuallyInterruptive(i),
- isConversation(i)
+ isConversation(i),
+ getShortcutInfo(i)
);
rankings[i] = ranking;
}
@@ -377,6 +383,17 @@
return index % 4 == 0;
}
+ private ShortcutInfo getShortcutInfo(int index) {
+ ShortcutInfo si = new ShortcutInfo(
+ index, String.valueOf(index), "packageName", new ComponentName("1", "1"), null,
+ "title", 0, "titleResName", "text", 0, "textResName",
+ "disabledMessage", 0, "disabledMessageResName",
+ null, null, 0, null, 0, 0,
+ 0, "iconResName", "bitmapPath", 0,
+ null, null);
+ return si;
+ }
+
private void assertActionsEqual(
List<Notification.Action> expecteds, List<Notification.Action> actuals) {
assertEquals(expecteds.size(), actuals.size());
@@ -411,6 +428,7 @@
assertEquals(comment, a.getSmartReplies(), b.getSmartReplies());
assertEquals(comment, a.canBubble(), b.canBubble());
assertEquals(comment, a.isConversation(), b.isConversation());
+ assertEquals(comment, a.getShortcutInfo().getId(), b.getShortcutInfo().getId());
assertActionsEqual(a.getSmartActions(), b.getSmartActions());
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index d0283f7..b6cdbfb 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -153,7 +153,6 @@
import androidx.annotation.Nullable;
import androidx.test.InstrumentationRegistry;
-import com.android.internal.R;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.logging.InstanceIdSequenceFake;
@@ -360,7 +359,7 @@
@Before
public void setUp() throws Exception {
// Shell permisssions will override permissions of our app, so add all necessary permissions
- // fo this test here:
+ // for this test here:
InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
"android.permission.WRITE_DEVICE_CONFIG",
"android.permission.READ_DEVICE_CONFIG",
@@ -1164,7 +1163,7 @@
assertEquals(PKG, call.r.getSbn().getPackageName());
assertEquals(0, call.r.getSbn().getId());
assertEquals(tag, call.r.getSbn().getTag());
- assertEquals(0, call.getInstanceId()); // Fake instance IDs are assigned in order
+ assertEquals(1, call.getInstanceId()); // Fake instance IDs are assigned in order
}
@Test
@@ -1186,14 +1185,14 @@
assertEquals(
NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
mNotificationRecordLogger.get(0).event);
- assertEquals(0, mNotificationRecordLogger.get(0).getInstanceId());
+ assertEquals(1, mNotificationRecordLogger.get(0).getInstanceId());
assertTrue(mNotificationRecordLogger.get(1).shouldLogReported);
assertEquals(
NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_UPDATED,
mNotificationRecordLogger.get(1).event);
// Instance ID doesn't change on update of an active notification
- assertEquals(0, mNotificationRecordLogger.get(1).getInstanceId());
+ assertEquals(1, mNotificationRecordLogger.get(1).getInstanceId());
}
@Test
@@ -1248,19 +1247,19 @@
NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
mNotificationRecordLogger.get(0).event);
assertTrue(mNotificationRecordLogger.get(0).shouldLogReported);
- assertEquals(0, mNotificationRecordLogger.get(0).getInstanceId());
+ assertEquals(1, mNotificationRecordLogger.get(0).getInstanceId());
assertEquals(
NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_APP_CANCEL,
mNotificationRecordLogger.get(1).event);
- assertEquals(0, mNotificationRecordLogger.get(1).getInstanceId());
+ assertEquals(1, mNotificationRecordLogger.get(1).getInstanceId());
assertEquals(
NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
mNotificationRecordLogger.get(2).event);
assertTrue(mNotificationRecordLogger.get(2).shouldLogReported);
// New instance ID because notification was canceled before re-post
- assertEquals(1, mNotificationRecordLogger.get(2).getInstanceId());
+ assertEquals(2, mNotificationRecordLogger.get(2).getInstanceId());
}
@Test
@@ -3453,6 +3452,7 @@
@Test
public void testStats_dismissalSurface() throws Exception {
final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ r.getSbn().setInstanceId(mNotificationInstanceIdSequence.newInstanceId());
mService.addNotification(r);
final NotificationVisibility nv = NotificationVisibility.obtain(r.getKey(), 0, 1, true);
@@ -3470,7 +3470,7 @@
assertEquals(
NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_USER_AOD,
mNotificationRecordLogger.get(0).event);
- assertEquals(0, mNotificationRecordLogger.get(0).getInstanceId());
+ assertEquals(1, mNotificationRecordLogger.get(0).getInstanceId());
}
@Test
@@ -4344,6 +4344,7 @@
final NotificationRecord r = generateNotificationRecord(
mTestNotificationChannel, 1, null, true);
r.setTextChanged(true);
+ r.getSbn().setInstanceId(mNotificationInstanceIdSequence.newInstanceId());
mService.addNotification(r);
mService.mNotificationDelegate.onNotificationVisibilityChanged(new NotificationVisibility[]
@@ -4353,7 +4354,7 @@
assertEquals(1, mNotificationRecordLogger.getCalls().size());
assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_OPEN,
mNotificationRecordLogger.get(0).event);
- assertEquals(0, mNotificationRecordLogger.get(0).getInstanceId());
+ assertEquals(1, mNotificationRecordLogger.get(0).getInstanceId());
mService.mNotificationDelegate.onNotificationVisibilityChanged(
new NotificationVisibility[]{},
@@ -4364,7 +4365,7 @@
assertEquals(2, mNotificationRecordLogger.getCalls().size());
assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_CLOSE,
mNotificationRecordLogger.get(1).event);
- assertEquals(0, mNotificationRecordLogger.get(1).getInstanceId());
+ assertEquals(1, mNotificationRecordLogger.get(1).getInstanceId());
}
@Test
@@ -6089,7 +6090,9 @@
// Pretend the shortcut exists
List<ShortcutInfo> shortcutInfos = new ArrayList<>();
- shortcutInfos.add(mock(ShortcutInfo.class));
+ ShortcutInfo info = mock(ShortcutInfo.class);
+ when(info.isLongLived()).thenReturn(true);
+ shortcutInfos.add(info);
when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcutInfos);
// Test: Send the bubble notification
@@ -6116,7 +6119,8 @@
verify(mLauncherApps, times(1)).unregisterCallback(launcherAppsCallback.getValue());
// We're no longer a bubble
- Notification notif2 = mService.getNotificationRecord(nr.getSbn().getKey()).getNotification();
+ Notification notif2 = mService.getNotificationRecord(
+ nr.getSbn().getKey()).getNotification();
assertFalse(notif2.isBubbleNotification());
}
@@ -6409,11 +6413,6 @@
convos.add(convo2);
when(mPreferencesHelper.getConversations(anyString(), anyInt())).thenReturn(convos);
- // only one valid shortcut
- LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery()
- .setPackage(PKG_P)
- .setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED)
- .setShortcutIds(Arrays.asList(channel1.getConversationId()));
ShortcutInfo si = mock(ShortcutInfo.class);
when(si.getShortLabel()).thenReturn("Hello");
when(mLauncherApps.getShortcuts(any(), any())).thenReturn(Arrays.asList(si));
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index 2d4b5a7..00b9273 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -51,6 +51,7 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ShortcutInfo;
import android.graphics.Color;
import android.graphics.drawable.Icon;
import android.media.AudioAttributes;
@@ -203,16 +204,13 @@
return new StatusBarNotification(pkg, pkg, id1, tag1, uid, uid, n, mUser, null, uid);
}
- private StatusBarNotification getMessagingStyleNotification(@Nullable String shortcutId) {
+ private StatusBarNotification getMessagingStyleNotification() {
final Builder builder = new Builder(mMockContext)
.setContentTitle("foo")
.setSmallIcon(android.R.drawable.sym_def_app_icon);
Person person = new Person.Builder().setName("Bob").build();
builder.setStyle(new Notification.MessagingStyle(person));
- if (shortcutId != null) {
- builder.setShortcutId(shortcutId);
- }
Notification n = builder.build();
return new StatusBarNotification(pkg, pkg, id1, tag1, uid, uid, n, mUser, null, uid);
@@ -1122,16 +1120,18 @@
@Test
public void testIsConversation() {
- StatusBarNotification sbn = getMessagingStyleNotification("test_shortcut_id");
+ StatusBarNotification sbn = getMessagingStyleNotification();
NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+ record.setShortcutInfo(mock(ShortcutInfo.class));
assertTrue(record.isConversation());
}
@Test
- public void testIsConversation_nullShortcutId() {
- StatusBarNotification sbn = getMessagingStyleNotification(null);
+ public void testIsConversation_nullShortcut() {
+ StatusBarNotification sbn = getMessagingStyleNotification();
NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+ record.setShortcutInfo(null);
assertFalse(record.isConversation());
}
@@ -1140,25 +1140,28 @@
public void testIsConversation_bypassShortcutFlagEnabled() {
Settings.Global.putString(mContentResolver,
FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "true");
- StatusBarNotification sbn = getMessagingStyleNotification(null);
+ StatusBarNotification sbn = getMessagingStyleNotification();
NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+ record.setShortcutInfo(null);
assertTrue(record.isConversation());
}
@Test
public void testIsConversation_channelDemoted() {
- StatusBarNotification sbn = getMessagingStyleNotification("test_shortcut_id");
+ StatusBarNotification sbn = getMessagingStyleNotification();
channel.setDemoted(true);
NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+ record.setShortcutInfo(mock(ShortcutInfo.class));
assertFalse(record.isConversation());
}
@Test
public void testIsConversation_withAdjustmentOverride() {
- StatusBarNotification sbn = getMessagingStyleNotification("test_shortcut_id");
+ StatusBarNotification sbn = getMessagingStyleNotification();
NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+ record.setShortcutInfo(mock(ShortcutInfo.class));
Bundle bundle = new Bundle();
bundle.putBoolean(KEY_NOT_CONVERSATION, true);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
index 27f72a1..e4d50c0 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
@@ -55,6 +55,8 @@
import android.util.AtomicFile;
import android.util.Pair;
+import androidx.test.InstrumentationRegistry;
+
import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.logging.InstanceIdSequenceFake;
import com.android.server.LocalServices;
@@ -65,6 +67,7 @@
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.wm.WindowManagerInternal;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -116,6 +119,10 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ // Shell permisssions will override permissions of our app, so add all necessary permissions
+ // for this test here:
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
+ "android.permission.READ_CONTACTS");
LocalServices.removeServiceForTest(WindowManagerInternal.class);
LocalServices.addService(WindowManagerInternal.class, mock(WindowManagerInternal.class));
@@ -153,6 +160,12 @@
mService.setPreferencesHelper(mPreferencesHelper);
}
+ @After
+ public void tearDown() {
+ InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation().dropShellPermissionIdentity();
+ }
+
@Test
public void testInit() throws Exception {
List<String> dialer0 = new ArrayList<>();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
index 816e8e5..3deeea2 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -22,6 +22,7 @@
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
@@ -249,7 +250,7 @@
ArgumentCaptor<Long> captor = ArgumentCaptor.forClass(Long.class);
verify(mAm, times(1)).setExactAndAllowWhileIdle(
anyInt(), captor.capture(), any(PendingIntent.class));
- long actualSnoozedUntilDuration = captor.getValue() - SystemClock.elapsedRealtime();
+ long actualSnoozedUntilDuration = captor.getValue() - System.currentTimeMillis();
assertTrue(Math.abs(actualSnoozedUntilDuration - 1000) < 250);
assertTrue(mSnoozeHelper.isSnoozed(
UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
@@ -363,8 +364,8 @@
mSnoozeHelper.cancel(UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), "one", 1);
- mSnoozeHelper.repost(r.getKey(), UserHandle.USER_SYSTEM);
- verify(mCallback, never()).repost(UserHandle.USER_SYSTEM, r);
+ mSnoozeHelper.repost(r.getKey(), UserHandle.USER_SYSTEM, false);
+ verify(mCallback, never()).repost(UserHandle.USER_SYSTEM, r, false);
}
@Test
@@ -374,8 +375,8 @@
NotificationRecord r2 = getNotificationRecord("pkg", 2, "one", UserHandle.ALL);
mSnoozeHelper.snooze(r2, 1000);
reset(mAm);
- mSnoozeHelper.repost(r.getKey(), UserHandle.USER_SYSTEM);
- verify(mCallback, times(1)).repost(UserHandle.USER_SYSTEM, r);
+ mSnoozeHelper.repost(r.getKey(), UserHandle.USER_SYSTEM, false);
+ verify(mCallback, times(1)).repost(UserHandle.USER_SYSTEM, r, false);
ArgumentCaptor<PendingIntent> captor = ArgumentCaptor.forClass(PendingIntent.class);
verify(mAm).cancel(captor.capture());
assertEquals(r.getKey(), captor.getValue().getIntent().getStringExtra(EXTRA_KEY));
@@ -388,8 +389,8 @@
NotificationRecord r2 = getNotificationRecord("pkg", 2, "one", UserHandle.ALL);
mSnoozeHelper.snooze(r2, 1000);
reset(mAm);
- mSnoozeHelper.repost(r.getKey());
- verify(mCallback, times(1)).repost(UserHandle.USER_SYSTEM, r);
+ mSnoozeHelper.repost(r.getKey(), false);
+ verify(mCallback, times(1)).repost(UserHandle.USER_SYSTEM, r, false);
verify(mAm).cancel(any(PendingIntent.class));
}
@@ -400,10 +401,10 @@
r.getNotification().category = "NEW CATEGORY";
mSnoozeHelper.update(UserHandle.USER_SYSTEM, r);
- verify(mCallback, never()).repost(anyInt(), any(NotificationRecord.class));
+ verify(mCallback, never()).repost(anyInt(), any(NotificationRecord.class), anyBoolean());
- mSnoozeHelper.repost(r.getKey(), UserHandle.USER_SYSTEM);
- verify(mCallback, times(1)).repost(UserHandle.USER_SYSTEM, r);
+ mSnoozeHelper.repost(r.getKey(), UserHandle.USER_SYSTEM, false);
+ verify(mCallback, times(1)).repost(UserHandle.USER_SYSTEM, r, false);
}
@Test
@@ -420,13 +421,23 @@
mSnoozeHelper.update(UserHandle.USER_SYSTEM, r);
// verify callback is called when repost (snooze is expired)
- verify(mCallback, never()).repost(anyInt(), any(NotificationRecord.class));
- mSnoozeHelper.repost(r.getKey(), UserHandle.USER_SYSTEM);
- verify(mCallback, times(1)).repost(UserHandle.USER_SYSTEM, r);
+ verify(mCallback, never()).repost(anyInt(), any(NotificationRecord.class), anyBoolean());
+ mSnoozeHelper.repost(r.getKey(), UserHandle.USER_SYSTEM, false);
+ verify(mCallback, times(1)).repost(UserHandle.USER_SYSTEM, r, false);
assertFalse(r.isCanceled);
}
@Test
+ public void testReport_passesFlag() throws Exception {
+ // snooze a notification
+ NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
+ mSnoozeHelper.snooze(r , 1000);
+
+ mSnoozeHelper.repost(r.getKey(), UserHandle.USER_SYSTEM, true);
+ verify(mCallback, times(1)).repost(UserHandle.USER_SYSTEM, r, true);
+ }
+
+ @Test
public void testGetSnoozedBy() throws Exception {
NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM);
@@ -523,7 +534,7 @@
mSnoozeHelper.snooze(r2, 1000);
mSnoozeHelper.repostGroupSummary("pkg", UserHandle.USER_SYSTEM, "group1");
- verify(mCallback, never()).repost(UserHandle.USER_SYSTEM, r);
+ verify(mCallback, never()).repost(eq(UserHandle.USER_SYSTEM), eq(r), anyBoolean());
}
@Test
@@ -542,8 +553,8 @@
mSnoozeHelper.repostGroupSummary("pkg", UserHandle.USER_SYSTEM, r.getGroupKey());
- verify(mCallback, times(1)).repost(UserHandle.USER_SYSTEM, r);
- verify(mCallback, never()).repost(UserHandle.USER_SYSTEM, r2);
+ verify(mCallback, times(1)).repost(UserHandle.USER_SYSTEM, r, false);
+ verify(mCallback, never()).repost(UserHandle.USER_SYSTEM, r2, false);
assertEquals(1, mSnoozeHelper.getSnoozed().size());
assertEquals(1, mSnoozeHelper.getSnoozed(UserHandle.USER_SYSTEM, "pkg").size());
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 7204a81..e1ce431f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -255,7 +255,7 @@
}
private void notifyTransitionStarting(ActivityRecord activity) {
- final ArrayMap<ActivityRecord, Integer> reasons = new ArrayMap<>();
+ final ArrayMap<WindowContainer, Integer> reasons = new ArrayMap<>();
reasons.put(activity, ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN);
mActivityMetricsLogger.notifyTransitionStarting(reasons);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
index 8c2b293..4cb50c7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
@@ -103,12 +103,12 @@
setUpOnDisplay(mDisplayContent);
mTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
- assertEquals(1, mDisplayContent.mChangingApps.size());
+ assertEquals(1, mDisplayContent.mChangingContainers.size());
// Verify we are in a change transition, but without a snapshot.
// Though, the test will actually have crashed by now if a snapshot is attempted.
- assertNull(mActivity.getThumbnail());
- assertTrue(mActivity.isInChangeTransition());
+ assertNull(mTask.mSurfaceFreezer.mSnapshot);
+ assertTrue(mTask.isInChangeTransition());
waitUntilHandlersIdle();
mActivity.removeImmediately();
@@ -121,14 +121,14 @@
setUpOnDisplay(mDisplayContent);
mTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
- assertEquals(1, mDisplayContent.mChangingApps.size());
- assertTrue(mActivity.isInChangeTransition());
+ assertEquals(1, mDisplayContent.mChangingContainers.size());
+ assertTrue(mTask.isInChangeTransition());
// Removing the app-token from the display should clean-up the
// the change leash.
mDisplayContent.removeAppToken(mActivity.token);
- assertEquals(0, mDisplayContent.mChangingApps.size());
- assertFalse(mActivity.isInChangeTransition());
+ assertEquals(0, mDisplayContent.mChangingContainers.size());
+ assertFalse(mTask.isInChangeTransition());
waitUntilHandlersIdle();
mActivity.removeImmediately();
@@ -152,8 +152,8 @@
assertEquals(WINDOWING_MODE_FULLSCREEN, mTask.getWindowingMode());
// Make sure we're not waiting for a change animation (no leash)
- assertFalse(mActivity.isInChangeTransition());
- assertNull(mActivity.getThumbnail());
+ assertFalse(mTask.isInChangeTransition());
+ assertNull(mActivity.mSurfaceFreezer.mSnapshot);
waitUntilHandlersIdle();
mActivity.removeImmediately();
@@ -165,13 +165,13 @@
setUpOnDisplay(mDisplayContent);
mTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
- assertEquals(1, mDisplayContent.mChangingApps.size());
- assertTrue(mActivity.isInChangeTransition());
+ assertEquals(1, mDisplayContent.mChangingContainers.size());
+ assertTrue(mTask.isInChangeTransition());
// Changing visibility should cancel the change transition and become closing
mActivity.setVisibility(false, false);
- assertEquals(0, mDisplayContent.mChangingApps.size());
- assertFalse(mActivity.isInChangeTransition());
+ assertEquals(0, mDisplayContent.mChangingContainers.size());
+ assertFalse(mTask.isInChangeTransition());
waitUntilHandlersIdle();
mActivity.removeImmediately();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DockedStackDividerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DockedStackDividerControllerTests.java
deleted file mode 100644
index 3206208..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/DockedStackDividerControllerTests.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2018 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.WindowManager.DOCKED_BOTTOM;
-import static android.view.WindowManager.DOCKED_LEFT;
-import static android.view.WindowManager.DOCKED_RIGHT;
-import static android.view.WindowManager.DOCKED_TOP;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-
-@SmallTest
-@Presubmit
-public class DockedStackDividerControllerTests {
-
- @Test
- public void testIsDockSideAllowedDockTop() {
- // Docked top is always allowed
- assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_TOP, DOCKED_LEFT,
- NAV_BAR_BOTTOM, true /* navigationBarCanMove */));
- assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_TOP, DOCKED_LEFT,
- NAV_BAR_BOTTOM, false /* navigationBarCanMove */));
- }
-
- @Test
- public void testIsDockSideAllowedDockBottom() {
- // Cannot dock bottom
- assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_BOTTOM, DOCKED_LEFT,
- NAV_BAR_BOTTOM, true /* navigationBarCanMove */));
- }
-
- @Test
- public void testIsDockSideAllowedNavigationBarMovable() {
- assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT,
- NAV_BAR_BOTTOM, true /* navigationBarCanMove */));
- assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT,
- NAV_BAR_LEFT, true /* navigationBarCanMove */));
- assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT,
- NAV_BAR_RIGHT, true /* navigationBarCanMove */));
- assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT,
- NAV_BAR_BOTTOM, true /* navigationBarCanMove */));
- assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT,
- NAV_BAR_RIGHT, true /* navigationBarCanMove */));
- assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT,
- NAV_BAR_LEFT, true /* navigationBarCanMove */));
- }
-
- @Test
- public void testIsDockSideAllowedNavigationBarNotMovable() {
- // Navigation bar is not movable such as tablets
- assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT,
- NAV_BAR_BOTTOM, false /* navigationBarCanMove */));
- assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_TOP,
- NAV_BAR_BOTTOM, false /* navigationBarCanMove */));
- assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_RIGHT,
- NAV_BAR_BOTTOM, false /* navigationBarCanMove */));
- assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT,
- NAV_BAR_BOTTOM, false /* navigationBarCanMove */));
- assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_TOP,
- NAV_BAR_BOTTOM, false /* navigationBarCanMove */));
- assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_RIGHT,
- NAV_BAR_BOTTOM, false /* navigationBarCanMove */));
- }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index d9c5c4c..9a898fd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -17,7 +17,6 @@
package com.android.server.wm;
import static android.app.ActivityManager.RECENT_WITH_EXCLUDED;
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
@@ -978,10 +977,7 @@
() -> mService.setTaskWindowingMode(taskId, WINDOWING_MODE_FULLSCREEN,
false/* toTop */));
assertNotRestoreTask(
- () -> mService.setTaskWindowingModeSplitScreenPrimary(taskId,
- SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT,
- false /* toTop */, false /* animate */, null /* initialBounds */,
- true /* showRecents */));
+ () -> mService.setTaskWindowingModeSplitScreenPrimary(taskId, false /* toTop */));
}
@Test
@@ -1096,14 +1092,10 @@
assertSecurityException(expectCallable,
() -> mService.moveTaskToStack(0, INVALID_STACK_ID, true));
assertSecurityException(expectCallable,
- () -> mService.setTaskWindowingModeSplitScreenPrimary(0,
- SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, true, true, new Rect(), true));
+ () -> mService.setTaskWindowingModeSplitScreenPrimary(0, true));
assertSecurityException(expectCallable, () -> mService.dismissPip(true, 0));
assertSecurityException(expectCallable,
() -> mService.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect()));
- assertSecurityException(expectCallable,
- () -> mService.resizeDockedStack(new Rect(), new Rect(), new Rect(), new Rect(),
- new Rect()));
assertSecurityException(expectCallable, () -> mService.getAllStackInfos());
assertSecurityException(expectCallable,
() -> mService.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED));
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 3a724a1..c7f94ef 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -247,7 +247,7 @@
@Test
public void testChange() throws Exception {
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
- mDisplayContent.mChangingApps.add(win.mActivityRecord);
+ mDisplayContent.mChangingContainers.add(win.mActivityRecord);
try {
final RemoteAnimationRecord record = mController.createRemoteAnimationRecord(
win.mActivityRecord, new Point(50, 100), new Rect(50, 100, 150, 150),
@@ -290,7 +290,7 @@
verify(mThumbnailFinishedCallback).onAnimationFinished(
eq(ANIMATION_TYPE_WINDOW_ANIMATION), eq(record.mThumbnailAdapter));
} finally {
- mDisplayContent.mChangingApps.clear();
+ mDisplayContent.mChangingContainers.clear();
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index ec20262..0ef2582 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -52,7 +52,6 @@
import static org.mockito.ArgumentMatchers.refEq;
import android.app.ActivityOptions;
-import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
@@ -251,13 +250,12 @@
final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
.setStack(stack).build();
- // Under split screen primary we should be focusable when not minimized
- mRootWindowContainer.setDockedStackMinimized(false);
+ // Created stacks are focusable by default.
assertTrue(stack.isTopActivityFocusable());
assertTrue(activity.isFocusable());
- // Under split screen primary we should not be focusable when minimized
- mRootWindowContainer.setDockedStackMinimized(true);
+ // If the stack is made unfocusable, its activities should inherit that.
+ stack.setFocusable(false);
assertFalse(stack.isTopActivityFocusable());
assertFalse(activity.isFocusable());
@@ -308,33 +306,6 @@
}
/**
- * Verify split-screen primary stack & task can resized by
- * {@link android.app.IActivityTaskManager#resizeDockedStack} as expect.
- */
- @Test
- public void testResizeDockedStackForSplitScreenPrimary() {
- final Rect configSize = new Rect(0, 0, 1000, 1000);
- final Rect displayedSize = new Rect(0, 0, 300, 300);
-
- // Create primary split-screen stack with a task.
- final ActivityStack primaryStack = new StackBuilder(mRootWindowContainer)
- .setActivityType(ACTIVITY_TYPE_STANDARD)
- .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
- .setOnTop(true)
- .build();
- final Task task = primaryStack.getTopMostTask();
-
- // Resize dock stack.
- mService.resizeDockedStack(displayedSize, configSize, null, null, null);
-
- // Verify dock stack & its task bounds if is equal as resized result.
- assertEquals(displayedSize, primaryStack.getDisplayedBounds());
- assertEquals(displayedSize, primaryStack.getDisplayedBounds());
- assertEquals(configSize, primaryStack.getBounds());
- assertEquals(configSize, task.getBounds());
- }
-
- /**
* Verify that home stack would be moved to front when the top activity is Recents.
*/
@Test
@@ -517,7 +488,7 @@
*/
@Test
public void testStartHomeOnAllDisplays() {
- mockResolveHomeActivity();
+ mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
mockResolveSecondaryHomeActivity();
// Create secondary displays.
@@ -644,30 +615,26 @@
}
/**
- * Tests that secondary home should be selected if default home not set.
+ * Tests that secondary home should be selected if primary home not set.
*/
@Test
- public void testResolveSecondaryHomeActivityWhenDefaultHomeNotSet() {
- final Intent defaultHomeIntent = mService.getHomeIntent();
- final ActivityInfo aInfoDefault = new ActivityInfo();
- aInfoDefault.name = ResolverActivity.class.getName();
- doReturn(aInfoDefault).when(mRootWindowContainer).resolveHomeActivity(anyInt(),
- refEq(defaultHomeIntent));
+ public void testResolveSecondaryHomeActivityWhenPrimaryHomeNotSet() {
+ // Setup: primary home not set.
+ final Intent primaryHomeIntent = mService.getHomeIntent();
+ final ActivityInfo aInfoPrimary = new ActivityInfo();
+ aInfoPrimary.name = ResolverActivity.class.getName();
+ doReturn(aInfoPrimary).when(mRootWindowContainer).resolveHomeActivity(anyInt(),
+ refEq(primaryHomeIntent));
+ // Setup: set secondary home.
+ mockResolveHomeActivity(false /* primaryHome */, false /* forceSystemProvided */);
- final String secondaryHomeComponent = mService.mContext.getResources().getString(
- com.android.internal.R.string.config_secondaryHomeComponent);
- final ComponentName comp = ComponentName.unflattenFromString(secondaryHomeComponent);
- final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null);
- final ActivityInfo aInfoSecondary = new ActivityInfo();
- aInfoSecondary.name = comp.getClassName();
- doReturn(aInfoSecondary).when(mRootWindowContainer).resolveHomeActivity(anyInt(),
- refEq(secondaryHomeIntent));
-
- // Should fallback to secondary home if default home not set.
+ // Run the test.
final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
.resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
-
- assertEquals(comp.getClassName(), resolvedInfo.first.name);
+ final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/);
+ assertEquals(aInfoSecondary.name, resolvedInfo.first.name);
+ assertEquals(aInfoSecondary.applicationInfo.packageName,
+ resolvedInfo.first.applicationInfo.packageName);
}
/**
@@ -675,103 +642,60 @@
* config_useSystemProvidedLauncherForSecondary.
*/
@Test
- public void testResolveSecondaryHomeActivityForced() throws Exception {
- Resources resources = mContext.getResources();
- spyOn(resources);
- try {
- // setUp: set secondary launcher and force it.
- final String defaultSecondaryHome =
- "com.android.test/com.android.test.TestDefaultSecondaryHome";
- final ComponentName secondaryComp = ComponentName.unflattenFromString(
- defaultSecondaryHome);
- doReturn(defaultSecondaryHome).when(resources).getString(
- com.android.internal.R.string.config_secondaryHomeComponent);
- doReturn(true).when(resources).getBoolean(
- com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary);
- final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null);
- assertEquals(secondaryComp, secondaryHomeIntent.getComponent());
- final ActivityInfo aInfoSecondary = new ActivityInfo();
- aInfoSecondary.name = secondaryComp.getClassName();
- aInfoSecondary.applicationInfo = new ApplicationInfo();
- aInfoSecondary.applicationInfo.packageName = secondaryComp.getPackageName();
- doReturn(aInfoSecondary).when(mRootWindowContainer).resolveHomeActivity(anyInt(),
- refEq(secondaryHomeIntent));
- final Intent homeIntent = mService.getHomeIntent();
- final ActivityInfo aInfoDefault = new ActivityInfo();
- aInfoDefault.name = "fakeHomeActivity";
- aInfoDefault.applicationInfo = new ApplicationInfo();
- aInfoDefault.applicationInfo.packageName = "fakeHomePackage";
- doReturn(aInfoDefault).when(mRootWindowContainer).resolveHomeActivity(anyInt(),
- refEq(homeIntent));
- // Let resolveActivities call to validate both main launcher and second launcher so that
- // resolveActivities call does not work as enabler for secondary.
- final List<ResolveInfo> resolutions1 = new ArrayList<>();
- final ResolveInfo resolveInfo1 = new ResolveInfo();
- resolveInfo1.activityInfo = new ActivityInfo();
- resolveInfo1.activityInfo.name = aInfoDefault.name;
- resolveInfo1.activityInfo.applicationInfo = aInfoDefault.applicationInfo;
- resolutions1.add(resolveInfo1);
- doReturn(resolutions1).when(mRootWindowContainer).resolveActivities(anyInt(),
- refEq(homeIntent));
- final List<ResolveInfo> resolutions2 = new ArrayList<>();
- final ResolveInfo resolveInfo2 = new ResolveInfo();
- resolveInfo2.activityInfo = new ActivityInfo();
- resolveInfo2.activityInfo.name = aInfoSecondary.name;
- resolveInfo2.activityInfo.applicationInfo = aInfoSecondary.applicationInfo;
- resolutions2.add(resolveInfo2);
- doReturn(resolutions2).when(mRootWindowContainer).resolveActivities(anyInt(),
- refEq(secondaryHomeIntent));
- doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplay(
- any(), anyInt(), anyBoolean());
-
- // Run the test
- final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
- .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
- assertEquals(secondaryComp.getClassName(), resolvedInfo.first.name);
- assertEquals(secondaryComp.getPackageName(),
- resolvedInfo.first.applicationInfo.packageName);
- assertEquals(aInfoSecondary.name, resolvedInfo.first.name);
- } finally {
- // tearDown
- reset(resources);
- }
- }
-
- /**
- * Tests that secondary home should be selected if default home not support secondary displays
- * or there is no matched activity in the same package as selected default home.
- */
- @Test
- public void testResolveSecondaryHomeActivityWhenDefaultHomeNotSupportMultiDisplay() {
- mockResolveHomeActivity();
-
+ public void testResolveSecondaryHomeActivityForced() {
+ // SetUp: set primary home.
+ mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
+ // SetUp: set secondary home and force it.
+ mockResolveHomeActivity(false /* primaryHome */, true /* forceSystemProvided */);
+ final Intent secondaryHomeIntent =
+ mService.getSecondaryHomeIntent(null /* preferredPackage */);
final List<ResolveInfo> resolutions = new ArrayList<>();
- doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any());
-
- final String secondaryHomeComponent = mService.mContext.getResources().getString(
- com.android.internal.R.string.config_secondaryHomeComponent);
- final ComponentName comp = ComponentName.unflattenFromString(secondaryHomeComponent);
- final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null);
- final ActivityInfo aInfoSecondary = new ActivityInfo();
- aInfoSecondary.name = comp.getClassName();
- doReturn(aInfoSecondary).when(mRootWindowContainer).resolveHomeActivity(anyInt(),
+ final ResolveInfo resolveInfo = new ResolveInfo();
+ final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/);
+ resolveInfo.activityInfo = aInfoSecondary;
+ resolutions.add(resolveInfo);
+ doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(),
refEq(secondaryHomeIntent));
+ doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplay(
+ any(), anyInt(), anyBoolean());
- // Should fallback to secondary home if selected default home not support secondary displays
- // or there is no matched activity in the same package as selected default home.
+ // Run the test.
final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
.resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
-
- assertEquals(comp.getClassName(), resolvedInfo.first.name);
+ assertEquals(aInfoSecondary.name, resolvedInfo.first.name);
+ assertEquals(aInfoSecondary.applicationInfo.packageName,
+ resolvedInfo.first.applicationInfo.packageName);
}
/**
- * Tests that default home activity should be selected if it already support secondary displays.
+ * Tests that secondary home should be selected if primary home not support secondary displays
+ * or there is no matched activity in the same package as selected primary home.
*/
@Test
- public void testResolveSecondaryHomeActivityWhenDefaultHomeSupportMultiDisplay() {
- final ActivityInfo aInfoDefault = mockResolveHomeActivity();
+ public void testResolveSecondaryHomeActivityWhenPrimaryHomeNotSupportMultiDisplay() {
+ // Setup: there is no matched activity in the same package as selected primary home.
+ mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
+ final List<ResolveInfo> resolutions = new ArrayList<>();
+ doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any());
+ // Setup: set secondary home.
+ mockResolveHomeActivity(false /* primaryHome */, false /* forceSystemProvided */);
+ // Run the test.
+ final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
+ .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
+ final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/);
+ assertEquals(aInfoSecondary.name, resolvedInfo.first.name);
+ assertEquals(aInfoSecondary.applicationInfo.packageName,
+ resolvedInfo.first.applicationInfo.packageName);
+ }
+ /**
+ * Tests that primary home activity should be selected if it already support secondary displays.
+ */
+ @Test
+ public void testResolveSecondaryHomeActivityWhenPrimaryHomeSupportMultiDisplay() {
+ // SetUp: set primary home.
+ mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
+ // SetUp: put primary home info on 2nd item
final List<ResolveInfo> resolutions = new ArrayList<>();
final ResolveInfo infoFake1 = new ResolveInfo();
infoFake1.activityInfo = new ActivityInfo();
@@ -779,7 +703,8 @@
infoFake1.activityInfo.applicationInfo = new ApplicationInfo();
infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1";
final ResolveInfo infoFake2 = new ResolveInfo();
- infoFake2.activityInfo = aInfoDefault;
+ final ActivityInfo aInfoPrimary = getFakeHomeActivityInfo(true /* primaryHome */);
+ infoFake2.activityInfo = aInfoPrimary;
resolutions.add(infoFake1);
resolutions.add(infoFake2);
doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any());
@@ -787,13 +712,12 @@
doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplay(
any(), anyInt(), anyBoolean());
- // Use default home activity if it support secondary displays.
+ // Run the test.
final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
.resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
-
- assertEquals(aInfoDefault.applicationInfo.packageName,
+ assertEquals(aInfoPrimary.name, resolvedInfo.first.name);
+ assertEquals(aInfoPrimary.applicationInfo.packageName,
resolvedInfo.first.applicationInfo.packageName);
- assertEquals(aInfoDefault.name, resolvedInfo.first.name);
}
/**
@@ -801,8 +725,9 @@
*/
@Test
public void testResolveSecondaryHomeActivityWhenOtherActivitySupportMultiDisplay() {
- mockResolveHomeActivity();
-
+ // SetUp: set primary home.
+ mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
+ // Setup: prepare two eligible activity info.
final List<ResolveInfo> resolutions = new ArrayList<>();
final ResolveInfo infoFake1 = new ResolveInfo();
infoFake1.activityInfo = new ActivityInfo();
@@ -821,7 +746,7 @@
doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplay(
any(), anyInt(), anyBoolean());
- // Use the first one of matched activities in the same package as selected default home.
+ // Use the first one of matched activities in the same package as selected primary home.
final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
.resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
@@ -884,32 +809,48 @@
/**
* Mock {@link RootWindowContainer#resolveHomeActivity} for returning consistent activity
- * info for test cases (the original implementation will resolve from the real package manager).
+ * info for test cases.
+ *
+ * @param primaryHome Indicate to use primary home intent as parameter, otherwise, use
+ * secondary home intent.
+ * @param forceSystemProvided Indicate to force using system provided home activity.
*/
- private ActivityInfo mockResolveHomeActivity() {
- final Intent homeIntent = mService.getHomeIntent();
- final ActivityInfo aInfoDefault = new ActivityInfo();
- aInfoDefault.name = "fakeHomeActivity";
- aInfoDefault.applicationInfo = new ApplicationInfo();
- aInfoDefault.applicationInfo.packageName = "fakeHomePackage";
- doReturn(aInfoDefault).when(mRootWindowContainer).resolveHomeActivity(anyInt(),
- refEq(homeIntent));
- return aInfoDefault;
+ private void mockResolveHomeActivity(boolean primaryHome, boolean forceSystemProvided) {
+ ActivityInfo targetActivityInfo = getFakeHomeActivityInfo(primaryHome);
+ Intent targetIntent;
+ if (primaryHome) {
+ targetIntent = mService.getHomeIntent();
+ } else {
+ Resources resources = mContext.getResources();
+ spyOn(resources);
+ doReturn(targetActivityInfo.applicationInfo.packageName).when(resources).getString(
+ com.android.internal.R.string.config_secondaryHomePackage);
+ doReturn(forceSystemProvided).when(resources).getBoolean(
+ com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary);
+ targetIntent = mService.getSecondaryHomeIntent(null /* preferredPackage */);
+ }
+ doReturn(targetActivityInfo).when(mRootWindowContainer).resolveHomeActivity(anyInt(),
+ refEq(targetIntent));
}
/**
* Mock {@link RootWindowContainer#resolveSecondaryHomeActivity} for returning consistent
- * activity info for test cases (the original implementation will resolve from the real package
- * manager).
+ * activity info for test cases.
*/
private void mockResolveSecondaryHomeActivity() {
final Intent secondaryHomeIntent = mService
.getSecondaryHomeIntent(null /* preferredPackage */);
- final ActivityInfo aInfoSecondary = new ActivityInfo();
- aInfoSecondary.name = "fakeSecondaryHomeActivity";
- aInfoSecondary.applicationInfo = new ApplicationInfo();
- aInfoSecondary.applicationInfo.packageName = "fakeSecondaryHomePackage";
+ final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false);
doReturn(Pair.create(aInfoSecondary, secondaryHomeIntent)).when(mRootWindowContainer)
.resolveSecondaryHomeActivity(anyInt(), anyInt());
}
+
+ private ActivityInfo getFakeHomeActivityInfo(boolean primaryHome) {
+ final ActivityInfo aInfo = new ActivityInfo();
+ aInfo.name = primaryHome ? "fakeHomeActivity" : "fakeSecondaryHomeActivity";
+ aInfo.applicationInfo = new ApplicationInfo();
+ aInfo.applicationInfo.packageName =
+ primaryHome ? "fakeHomePackage" : "fakeSecondaryHomePackage";
+ return aInfo;
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 2c68cc7..85e4a16 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -251,9 +251,11 @@
// b/145812508: special legacy use-case for transparent/translucent windows.
appWindow.mAttrs.format = PixelFormat.TRANSPARENT;
+ appWindow.mAttrs.alpha = 0;
assertTrue(appWindow.canBeImeTarget());
appWindow.mAttrs.format = PixelFormat.OPAQUE;
+ appWindow.mAttrs.alpha = 1;
appWindow.mAttrs.flags &= ~FLAG_ALT_FOCUSABLE_IM;
assertFalse(appWindow.canBeImeTarget());
appWindow.mAttrs.flags &= ~FLAG_NOT_FOCUSABLE;
@@ -276,8 +278,7 @@
spyOn(appWindow);
spyOn(controller);
spyOn(stack);
- doReturn(true).when(controller).isMinimizedDock();
- doReturn(true).when(controller).isHomeStackResizable();
+ stack.setFocusable(false);
doReturn(stack).when(appWindow).getRootTask();
// Make sure canBeImeTarget is false due to shouldIgnoreInput is true;
@@ -619,9 +620,10 @@
}
@Test
- public void testCantReceiveTouchWhenShouldIgnoreInput() {
+ public void testCantReceiveTouchWhenNotFocusable() {
final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0");
- win0.mActivityRecord.getStack().setAdjustedForMinimizedDock(1 /* Any non 0 value works */);
+ win0.mActivityRecord.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ win0.mActivityRecord.getStack().setFocusable(false);
assertTrue(win0.cantReceiveTouchInput());
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/RotationAnimationUtilsTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/RotationAnimationUtilsTest.java
index 9cda084..e5497a2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/utils/RotationAnimationUtilsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/RotationAnimationUtilsTest.java
@@ -50,7 +50,7 @@
public void blackLuma() {
Bitmap swBitmap = createBitmap(0);
GraphicBuffer gb = swBitmapToGraphicsBuffer(swBitmap);
- float borderLuma = RotationAnimationUtils.getAvgBorderLuma(gb, mColorSpace);
+ float borderLuma = RotationAnimationUtils.getMedianBorderLuma(gb, mColorSpace);
assertEquals(0, borderLuma, 0);
}
@@ -58,7 +58,15 @@
public void whiteLuma() {
Bitmap swBitmap = createBitmap(1);
GraphicBuffer gb = swBitmapToGraphicsBuffer(swBitmap);
- float borderLuma = RotationAnimationUtils.getAvgBorderLuma(gb, mColorSpace);
+ float borderLuma = RotationAnimationUtils.getMedianBorderLuma(gb, mColorSpace);
+ assertEquals(1, borderLuma, 0);
+ }
+
+ @Test
+ public void unevenBitmapDimens() {
+ Bitmap swBitmap = createBitmap(1, BITMAP_WIDTH + 1, BITMAP_HEIGHT + 1);
+ GraphicBuffer gb = swBitmapToGraphicsBuffer(swBitmap);
+ float borderLuma = RotationAnimationUtils.getMedianBorderLuma(gb, mColorSpace);
assertEquals(1, borderLuma, 0);
}
@@ -67,7 +75,7 @@
Bitmap swBitmap = createBitmap(1);
setBorderLuma(swBitmap, 0);
GraphicBuffer gb = swBitmapToGraphicsBuffer(swBitmap);
- float borderLuma = RotationAnimationUtils.getAvgBorderLuma(gb, mColorSpace);
+ float borderLuma = RotationAnimationUtils.getMedianBorderLuma(gb, mColorSpace);
assertEquals(0, borderLuma, 0);
}
@@ -76,7 +84,7 @@
Bitmap swBitmap = createBitmap(0);
setBorderLuma(swBitmap, 1);
GraphicBuffer gb = swBitmapToGraphicsBuffer(swBitmap);
- float borderLuma = RotationAnimationUtils.getAvgBorderLuma(gb, mColorSpace);
+ float borderLuma = RotationAnimationUtils.getMedianBorderLuma(gb, mColorSpace);
assertEquals(1, borderLuma, 0);
}
@@ -123,9 +131,13 @@
}
private Bitmap createBitmap(float luma) {
- Bitmap bitmap = Bitmap.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, ARGB_8888);
- for (int i = 0; i < BITMAP_WIDTH; i++) {
- for (int j = 0; j < BITMAP_HEIGHT; j++) {
+ return createBitmap(luma, BITMAP_WIDTH, BITMAP_HEIGHT);
+ }
+
+ private Bitmap createBitmap(float luma, int width, int height) {
+ Bitmap bitmap = Bitmap.createBitmap(width, height, ARGB_8888);
+ for (int i = 0; i < width; i++) {
+ for (int j = 0; j < height; j++) {
bitmap.setPixel(i, j, Color.argb(1, luma, luma, luma));
}
}
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 07cc2d4..8e85bb2 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -1917,7 +1917,19 @@
return;
}
try {
- // Adbd will be started by AdbService once Global.ADB_ENABLED is set.
+ if ((config & UsbManager.FUNCTION_ADB) != 0) {
+ /**
+ * Start adbd if ADB function is included in the configuration.
+ */
+ LocalServices.getService(AdbManagerInternal.class)
+ .startAdbdForTransport(AdbTransportType.USB);
+ } else {
+ /**
+ * Stop adbd otherwise
+ */
+ LocalServices.getService(AdbManagerInternal.class)
+ .stopAdbdForTransport(AdbTransportType.USB);
+ }
UsbGadgetCallback usbGadgetCallback = new UsbGadgetCallback(mCurrentRequest,
config, chargingFunctions);
mGadgetProxy.setCurrentUsbFunctions(config, usbGadgetCallback,
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 3c0e0af..0b24dd2 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -60,7 +60,6 @@
import android.os.UserHandle;
import android.os.UserManagerInternal;
import android.provider.Settings;
-import android.service.voice.IVoiceInteractionService;
import android.service.voice.IVoiceInteractionSession;
import android.service.voice.VoiceInteractionManagerInternal;
import android.service.voice.VoiceInteractionService;
@@ -684,9 +683,9 @@
}
@Override
- public void showSession(IVoiceInteractionService service, Bundle args, int flags) {
+ public void showSession(Bundle args, int flags) {
synchronized (this) {
- enforceIsCurrentVoiceInteractionService(service);
+ enforceIsCurrentVoiceInteractionService();
final long caller = Binder.clearCallingIdentity();
try {
@@ -928,12 +927,10 @@
}
//----------------- Model management APIs --------------------------------//
- // TODO: add check to only allow active voice interaction service or keyphrase enrollment
- // application to manage voice models
@Override
public KeyphraseSoundModel getKeyphraseSoundModel(int keyphraseId, String bcp47Locale) {
- enforceCallingPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES);
+ enforceCallerAllowedToEnrollVoiceModel();
if (bcp47Locale == null) {
throw new IllegalArgumentException("Illegal argument(s) in getKeyphraseSoundModel");
@@ -950,7 +947,7 @@
@Override
public int updateKeyphraseSoundModel(KeyphraseSoundModel model) {
- enforceCallingPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES);
+ enforceCallerAllowedToEnrollVoiceModel();
if (model == null) {
throw new IllegalArgumentException("Model must not be null");
}
@@ -975,7 +972,7 @@
@Override
public int deleteKeyphraseSoundModel(int keyphraseId, String bcp47Locale) {
- enforceCallingPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES);
+ enforceCallerAllowedToEnrollVoiceModel();
if (bcp47Locale == null) {
throw new IllegalArgumentException(
@@ -1008,10 +1005,9 @@
//----------------- SoundTrigger APIs --------------------------------//
@Override
- public boolean isEnrolledForKeyphrase(IVoiceInteractionService service, int keyphraseId,
- String bcp47Locale) {
+ public boolean isEnrolledForKeyphrase(int keyphraseId, String bcp47Locale) {
synchronized (this) {
- enforceIsCurrentVoiceInteractionService(service);
+ enforceIsCurrentVoiceInteractionService();
}
if (bcp47Locale == null) {
@@ -1030,10 +1026,10 @@
}
@Nullable
- public KeyphraseMetadata getEnrolledKeyphraseMetadata(IVoiceInteractionService service,
- String keyphrase, String bcp47Locale) {
+ public KeyphraseMetadata getEnrolledKeyphraseMetadata(String keyphrase,
+ String bcp47Locale) {
synchronized (this) {
- enforceIsCurrentVoiceInteractionService(service);
+ enforceIsCurrentVoiceInteractionService();
}
if (bcp47Locale == null) {
@@ -1065,10 +1061,10 @@
}
@Override
- public ModuleProperties getDspModuleProperties(IVoiceInteractionService service) {
+ public ModuleProperties getDspModuleProperties() {
// Allow the call if this is the current voice interaction service.
synchronized (this) {
- enforceIsCurrentVoiceInteractionService(service);
+ enforceIsCurrentVoiceInteractionService();
final long caller = Binder.clearCallingIdentity();
try {
@@ -1080,12 +1076,11 @@
}
@Override
- public int startRecognition(IVoiceInteractionService service, int keyphraseId,
- String bcp47Locale, IRecognitionStatusCallback callback,
- RecognitionConfig recognitionConfig) {
+ public int startRecognition(int keyphraseId, String bcp47Locale,
+ IRecognitionStatusCallback callback, RecognitionConfig recognitionConfig) {
// Allow the call if this is the current voice interaction service.
synchronized (this) {
- enforceIsCurrentVoiceInteractionService(service);
+ enforceIsCurrentVoiceInteractionService();
if (callback == null || recognitionConfig == null || bcp47Locale == null) {
throw new IllegalArgumentException("Illegal argument(s) in startRecognition");
@@ -1117,11 +1112,10 @@
}
@Override
- public int stopRecognition(IVoiceInteractionService service, int keyphraseId,
- IRecognitionStatusCallback callback) {
+ public int stopRecognition(int keyphraseId, IRecognitionStatusCallback callback) {
// Allow the call if this is the current voice interaction service.
synchronized (this) {
- enforceIsCurrentVoiceInteractionService(service);
+ enforceIsCurrentVoiceInteractionService();
}
final long caller = Binder.clearCallingIdentity();
@@ -1133,11 +1127,10 @@
}
@Override
- public int setParameter(IVoiceInteractionService service, int keyphraseId,
- @ModelParams int modelParam, int value) {
+ public int setParameter(int keyphraseId, @ModelParams int modelParam, int value) {
// Allow the call if this is the current voice interaction service.
synchronized (this) {
- enforceIsCurrentVoiceInteractionService(service);
+ enforceIsCurrentVoiceInteractionService();
}
final long caller = Binder.clearCallingIdentity();
@@ -1149,11 +1142,10 @@
}
@Override
- public int getParameter(IVoiceInteractionService service, int keyphraseId,
- @ModelParams int modelParam) {
+ public int getParameter(int keyphraseId, @ModelParams int modelParam) {
// Allow the call if this is the current voice interaction service.
synchronized (this) {
- enforceIsCurrentVoiceInteractionService(service);
+ enforceIsCurrentVoiceInteractionService();
}
final long caller = Binder.clearCallingIdentity();
@@ -1166,11 +1158,10 @@
@Override
@Nullable
- public ModelParamRange queryParameter(IVoiceInteractionService service,
- int keyphraseId, @ModelParams int modelParam) {
+ public ModelParamRange queryParameter(int keyphraseId, @ModelParams int modelParam) {
// Allow the call if this is the current voice interaction service.
synchronized (this) {
- enforceIsCurrentVoiceInteractionService(service);
+ enforceIsCurrentVoiceInteractionService();
}
final long caller = Binder.clearCallingIdentity();
@@ -1400,9 +1391,9 @@
}
@Override
- public void setUiHints(IVoiceInteractionService service, Bundle hints) {
+ public void setUiHints(Bundle hints) {
synchronized (this) {
- enforceIsCurrentVoiceInteractionService(service);
+ enforceIsCurrentVoiceInteractionService();
final int size = mVoiceInteractionSessionListeners.beginBroadcast();
for (int i = 0; i < size; ++i) {
@@ -1425,14 +1416,32 @@
}
}
- private void enforceIsCurrentVoiceInteractionService(IVoiceInteractionService service) {
- if (mImpl == null || mImpl.mService == null
- || service.asBinder() != mImpl.mService.asBinder()) {
+ private void enforceIsCurrentVoiceInteractionService() {
+ if (!isCallerCurrentVoiceInteractionService()) {
throw new
SecurityException("Caller is not the current voice interaction service");
}
}
+ private void enforceCallerAllowedToEnrollVoiceModel() {
+ enforceCallingPermission(Manifest.permission.MANAGE_VOICE_KEYPHRASES);
+ if (!isCallerCurrentVoiceInteractionService()
+ && !isCallerTrustedEnrollmentApplication()) {
+ throw new SecurityException("Caller is required to be the current voice interaction"
+ + " service or a system enrollment application to enroll voice models");
+ }
+ }
+
+ private boolean isCallerCurrentVoiceInteractionService() {
+ return mImpl != null
+ && mImpl.mInfo.getServiceInfo().applicationInfo.uid == Binder.getCallingUid();
+ }
+
+ private boolean isCallerTrustedEnrollmentApplication() {
+ return mImpl.mEnrollmentApplicationInfo.isUidSupportedEnrollmentApplication(
+ Binder.getCallingUid());
+ }
+
private void setImplLocked(VoiceInteractionManagerServiceImpl impl) {
mImpl = impl;
mAtmInternal.notifyActiveVoiceInteractionServiceChanged(
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index a62b03c..b813f87 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -36,6 +36,7 @@
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
+import android.hardware.soundtrigger.KeyphraseEnrollmentInfo;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -78,6 +79,7 @@
final IActivityManager mAm;
final IActivityTaskManager mAtm;
final VoiceInteractionServiceInfo mInfo;
+ final KeyphraseEnrollmentInfo mEnrollmentApplicationInfo;
final ComponentName mSessionComponentName;
final IWindowManager mIWindowManager;
boolean mBound = false;
@@ -133,6 +135,7 @@
mComponent = service;
mAm = ActivityManager.getService();
mAtm = ActivityTaskManager.getService();
+ mEnrollmentApplicationInfo = new KeyphraseEnrollmentInfo(context.getPackageManager());
VoiceInteractionServiceInfo info;
try {
info = new VoiceInteractionServiceInfo(context.getPackageManager(), service, mUser);
@@ -403,6 +406,7 @@
pw.println(" Active session:");
mActiveSession.dump(" ", pw);
}
+ pw.println(" " + mEnrollmentApplicationInfo.toString());
}
void startLocked() {
diff --git a/startop/iorap/functional_tests/AndroidTest.xml b/startop/iorap/functional_tests/AndroidTest.xml
index ef56fc8..3d5a229 100644
--- a/startop/iorap/functional_tests/AndroidTest.xml
+++ b/startop/iorap/functional_tests/AndroidTest.xml
@@ -48,6 +48,8 @@
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.google.android.startop.iorap.tests" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <!-- test-timeout unit is ms, value = 30 min -->
+ <option name="test-timeout" value="1800000" />
</test>
</configuration>
diff --git a/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java b/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java
index 4002387..9abbcd7 100644
--- a/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java
+++ b/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java
@@ -70,7 +70,7 @@
private static final Duration TIMEOUT = Duration.ofSeconds(300L);
private static final String READAHEAD_INDICATOR =
- "Description = /data/misc/iorapd/com.android.settings/none/com.android.settings.Settings/compiled_traces/compiled_trace.pb";
+ "Description = /data/misc/iorapd/com.android.settings/-?\\d+/com.android.settings.Settings/compiled_traces/compiled_trace.pb";
private UiDevice mDevice;
@@ -326,14 +326,14 @@
return false;
}
- String log = executeShellCommand("logcat -s iorapd -d");
+ String log = executeShellCommand("logcat -d");
Pattern p = Pattern.compile(
- ".*" + READAHEAD_INDICATOR
- + ".*Total File Paths=(\\d+) \\(good: (\\d+)%\\)\n"
- + ".*Total Entries=(\\d+) \\(good: (\\d+)%\\)\n"
- + ".*Total Bytes=(\\d+) \\(good: (\\d+)%\\).*",
- Pattern.DOTALL);
+ ".*" + READAHEAD_INDICATOR
+ + ".*Total File Paths=(\\d+) \\(good: (\\d+[.]?\\d*)%\\)\n"
+ + ".*Total Entries=(\\d+) \\(good: (\\d+[.]?\\d*)%\\)\n"
+ + ".*Total Bytes=(\\d+) \\(good: (\\d+[.]?\\d*)%\\).*",
+ Pattern.DOTALL);
Matcher m = p.matcher(log);
if (!m.matches()) {
diff --git a/startop/iorap/src/com/google/android/startop/iorap/JobScheduledEvent.java b/startop/iorap/src/com/google/android/startop/iorap/JobScheduledEvent.java
index 9b3bfcb..2055b20 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/JobScheduledEvent.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/JobScheduledEvent.java
@@ -40,7 +40,7 @@
public static final int TYPE_START_JOB = 0;
/** JobService#onJobStopped */
public static final int TYPE_STOP_JOB = 1;
- private static final int TYPE_MAX = 0;
+ private static final int TYPE_MAX = 1;
/** @hide */
@IntDef(flag = true, prefix = { "TYPE_" }, value = {
diff --git a/startop/scripts/app_startup/parse_metrics b/startop/scripts/app_startup/parse_metrics
index 036609f..3fa1462 100755
--- a/startop/scripts/app_startup/parse_metrics
+++ b/startop/scripts/app_startup/parse_metrics
@@ -42,12 +42,14 @@
-h, --help usage information (this)
-v, --verbose enable extra verbose printing
-t, --timeout <sec> how many seconds to timeout when trying to wait for logcat to change
+ -rfd, --reportfullydrawn wait for report fully drawn (default: off)
EOF
}
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$DIR/lib/common"
+report_fully_drawn="n"
package=""
activity=""
timeout=5
@@ -81,6 +83,11 @@
-s|--simulate)
simulate="y"
;;
+ -rfd|--reportfullydrawn)
+ report_fully_drawn="y"
+ ;;
+
+
*)
echo "Invalid argument: $1" >&2
exit 1
@@ -190,12 +197,15 @@
parse_metric_from_logcat "Displayed_ms" "$pattern" "$re_pattern"
-# 01-16 17:31:44.550 11172 11204 I ActivityTaskManager: Fully drawn com.google.android.GoogleCamera/com.android.camera.CameraLauncher: +10s897ms
-pattern="ActivityTaskManager: Fully drawn ${package}"
-#re_pattern='.*Fully drawn[[:blank:]]\+'"${package}"'[/][^[:blank:]]\+[[:blank:]]+\([[:digit:]]\+\).*'
-re_pattern='.*Fully drawn[[:blank:]]\+'"${package}"'[/][^[:blank:]]\+[[:blank:]]+\([[:digit:]]\+ms\|[[:digit:]]\+s[[:digit:]]\+ms\).*'
+# Only track ReportFullyDrawn with --reportfullydrawn/-rfd flags
+if [[ $report_fully_drawn == y ]]; then
+ # 01-16 17:31:44.550 11172 11204 I ActivityTaskManager: Fully drawn com.google.android.GoogleCamera/com.android.camera.CameraLauncher: +10s897ms
+ pattern="ActivityTaskManager: Fully drawn ${package}"
+ #re_pattern='.*Fully drawn[[:blank:]]\+'"${package}"'[/][^[:blank:]]\+[[:blank:]]+\([[:digit:]]\+\).*'
+ re_pattern='.*Fully drawn[[:blank:]]\+'"${package}"'[/][^[:blank:]]\+[[:blank:]]+\([[:digit:]]\+ms\|[[:digit:]]\+s[[:digit:]]\+ms\).*'
-parse_metric_from_logcat "Fully_drawn_ms" "$pattern" "$re_pattern"
+ parse_metric_from_logcat "Fully_drawn_ms" "$pattern" "$re_pattern"
+fi
# also call into package-specific scripts if there are additional metrics
if [[ -x "$DIR/metrics/$package" ]]; then
diff --git a/startop/scripts/app_startup/run_app_with_prefetch b/startop/scripts/app_startup/run_app_with_prefetch
index 92a31c3..31f6253 100755
--- a/startop/scripts/app_startup/run_app_with_prefetch
+++ b/startop/scripts/app_startup/run_app_with_prefetch
@@ -35,6 +35,7 @@
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
source "$DIR/../iorap/common"
+report_fully_drawn="n"
needs_trace_file="n"
input_file=""
package=""
@@ -70,6 +71,10 @@
mode="$2"
shift
;;
+ -rfd|--reportfullydrawn)
+ report_fully_drawn="y"
+ shift
+ ;;
-c|--count)
count="$2"
((count+=1))
@@ -403,7 +408,11 @@
join_by ',' "${all_metrics[@]}"
}
-metrics_header="$("$DIR/parse_metrics" --package "$package" --activity "$activity" --simulate | parse_metrics_header)"
+if [[ $report_fully_drawn == y ]]; then
+ metrics_header="$("$DIR/parse_metrics" --package "$package" --activity "$activity" --simulate --reportfullydrawn | parse_metrics_header)"
+else
+ metrics_header="$("$DIR/parse_metrics" --package "$package" --activity "$activity" --simulate | parse_metrics_header)"
+fi
# TODO: This loop logic could probably be moved into app_startup_runner.py
for ((i=0;i<count;++i)) do
@@ -411,6 +420,9 @@
verbose_print "==== ITERATION $i ===="
verbose_print "=========================================="
if [[ $mode != "warm" ]]; then
+ # The package must be killed **before** we drop caches, otherwise pages will stay resident.
+ verbose_print "Kill package for non-warm start."
+ remote_pkill "$package"
verbose_print "Drop caches for non-warm start."
# Drop all caches to get cold starts.
adb shell "echo 3 > /proc/sys/vm/drop_caches"
@@ -423,7 +435,12 @@
pre_launch_timestamp="$(logcat_save_timestamp)"
# TODO: multiple metrics output.
+
+if [[ $report_fully_drawn == y ]]; then
+ total_time="$(timeout $timeout "$DIR/launch_application" "$package" "$activity" | "$DIR/parse_metrics" --package "$package" --activity "$activity" --timestamp "$pre_launch_timestamp" --reportfullydrawn | parse_metrics_output)"
+else
total_time="$(timeout $timeout "$DIR/launch_application" "$package" "$activity" | "$DIR/parse_metrics" --package "$package" --activity "$activity" --timestamp "$pre_launch_timestamp" | parse_metrics_output)"
+fi
if [[ $? -ne 0 ]]; then
echo "WARNING: Skip bad result, try iteration again." >&2
diff --git a/startop/scripts/app_startup/run_app_with_prefetch.py b/startop/scripts/app_startup/run_app_with_prefetch.py
old mode 100644
new mode 100755
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 9d77623..6a40487 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4096,7 +4096,8 @@
/** Prefix of all Wifi.KEY_* constants. */
public static final String KEY_PREFIX = "wifi.";
/**
- * It contains the maximum client count definition that the carrier owns.
+ * It contains the maximum client count definition that the carrier sets.
+ * The default is 0, which means that the carrier hasn't set a requirement.
*/
public static final String KEY_HOTSPOT_MAX_CLIENT_COUNT =
KEY_PREFIX + "hotspot_maximum_client_count";
diff --git a/telephony/java/android/telephony/RadioAccessFamily.java b/telephony/java/android/telephony/RadioAccessFamily.java
index bc84738..90ddf2c 100644
--- a/telephony/java/android/telephony/RadioAccessFamily.java
+++ b/telephony/java/android/telephony/RadioAccessFamily.java
@@ -260,24 +260,6 @@
return raf;
}
- /**
- * Returns the highest capability of the RadioAccessFamily (4G > 3G > 2G).
- * @param raf The RadioAccessFamily that we wish to filter
- * @return The highest radio capability
- */
- public static int getHighestRafCapability(int raf) {
- if ((LTE & raf) > 0) {
- return TelephonyManager.NETWORK_CLASS_4_G;
- }
- if ((EVDO|HS|WCDMA & raf) > 0) {
- return TelephonyManager.NETWORK_CLASS_3_G;
- }
- if((GSM|CDMA & raf) > 0) {
- return TelephonyManager.NETWORK_CLASS_2_G;
- }
- return TelephonyManager.NETWORK_CLASS_UNKNOWN;
- }
-
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@PrefNetworkMode
public static int getNetworkTypeFromRaf(int raf) {
@@ -395,4 +377,34 @@
}
return result;
}
+
+ /**
+ * Compare two sets of network types to see which is more capable.
+ *
+ * This algorithm first tries to see see if a set has a strict superset of RAT support for
+ * each generation, from newest to oldest; if that results in a tie, then it returns the set
+ * that supports the most RAT types.
+ */
+ public static int compare(long networkTypeBitmaskL, long networkTypeBitmaskR) {
+ final long[] prioritizedNetworkClassBitmasks = new long[] {
+ TelephonyManager.NETWORK_CLASS_BITMASK_5G,
+ TelephonyManager.NETWORK_CLASS_BITMASK_4G,
+ TelephonyManager.NETWORK_CLASS_BITMASK_3G,
+ TelephonyManager.NETWORK_CLASS_BITMASK_2G,
+ };
+
+ long lhsUnique = networkTypeBitmaskL & ~networkTypeBitmaskR;
+ long rhsUnique = networkTypeBitmaskR & ~networkTypeBitmaskL;
+
+ // See if one has a strict super-set of capabilities, generation by generation.
+ for (long classBitmask : prioritizedNetworkClassBitmasks) {
+ int result = 0;
+ if ((lhsUnique & classBitmask) != 0) ++result;
+ if ((rhsUnique & classBitmask) != 0) --result;
+ if (result != 0) return result;
+ }
+
+ // Without a clear winner, return the one that supports the most types.
+ return Long.bitCount(networkTypeBitmaskL) - Long.bitCount(networkTypeBitmaskR);
+ }
}
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index a5a1ebc..e957f3e 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -1992,7 +1992,6 @@
* @return the copied ServiceState with location info sanitized.
* @hide
*/
- @SystemApi
@NonNull
public ServiceState createLocationInfoSanitizedCopy(boolean removeCoarseLocation) {
ServiceState state = new ServiceState(this);
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 336aa0e..0ec3d55 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -826,7 +826,7 @@
+ " hplmns=" + Arrays.toString(mHplmns)
+ " subscriptionType=" + mSubscriptionType
+ " mGroupOwner=" + mGroupOwner
- + " carrierConfigAccessRules=" + mCarrierConfigAccessRules
+ + " carrierConfigAccessRules=" + Arrays.toString(mCarrierConfigAccessRules)
+ " mAreUiccApplicationsEnabled=" + mAreUiccApplicationsEnabled + "}";
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index b32e9d7..8ac9023b 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2575,8 +2575,8 @@
* @hide
*/
@SystemApi
- public boolean canManageSubscription(@Nullable SubscriptionInfo info,
- @Nullable String packageName) {
+ public boolean canManageSubscription(@NonNull SubscriptionInfo info,
+ @NonNull String packageName) {
if (info == null || info.getAllAccessRules() == null || packageName == null) {
return false;
}
@@ -3081,13 +3081,13 @@
*
* Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
*
- * @param enabled whether uicc applications are enabled or disabled.
* @param subscriptionId which subscription to operate on.
+ * @param enabled whether uicc applications are enabled or disabled.
* @hide
*/
@SystemApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
- public void setUiccApplicationsEnabled(boolean enabled, int subscriptionId) {
+ public void setUiccApplicationsEnabled(int subscriptionId, boolean enabled) {
if (VDBG) {
logd("setUiccApplicationsEnabled subId= " + subscriptionId + " enable " + enabled);
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index a36df49..51e777e 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -3075,64 +3075,6 @@
}
/**
- * Network Class Definitions.
- * Do not change this order, it is used for sorting during emergency calling in
- * {@link TelephonyConnectionService#getFirstPhoneForEmergencyCall()}. Any newer technologies
- * should be added after the current definitions.
- */
- /** Unknown network class. {@hide} */
- public static final int NETWORK_CLASS_UNKNOWN = 0;
- /** Class of broadly defined "2G" networks. {@hide} */
- @UnsupportedAppUsage
- public static final int NETWORK_CLASS_2_G = 1;
- /** Class of broadly defined "3G" networks. {@hide} */
- @UnsupportedAppUsage
- public static final int NETWORK_CLASS_3_G = 2;
- /** Class of broadly defined "4G" networks. {@hide} */
- @UnsupportedAppUsage
- public static final int NETWORK_CLASS_4_G = 3;
- /** Class of broadly defined "5G" networks. {@hide} */
- public static final int NETWORK_CLASS_5_G = 4;
-
- /**
- * Return general class of network type, such as "3G" or "4G". In cases
- * where classification is contentious, this method is conservative.
- *
- * @hide
- */
- @UnsupportedAppUsage
- public static int getNetworkClass(int networkType) {
- switch (networkType) {
- case NETWORK_TYPE_GPRS:
- case NETWORK_TYPE_GSM:
- case NETWORK_TYPE_EDGE:
- case NETWORK_TYPE_CDMA:
- case NETWORK_TYPE_1xRTT:
- case NETWORK_TYPE_IDEN:
- return NETWORK_CLASS_2_G;
- case NETWORK_TYPE_UMTS:
- case NETWORK_TYPE_EVDO_0:
- case NETWORK_TYPE_EVDO_A:
- case NETWORK_TYPE_HSDPA:
- case NETWORK_TYPE_HSUPA:
- case NETWORK_TYPE_HSPA:
- case NETWORK_TYPE_EVDO_B:
- case NETWORK_TYPE_EHRPD:
- case NETWORK_TYPE_HSPAP:
- case NETWORK_TYPE_TD_SCDMA:
- return NETWORK_CLASS_3_G;
- case NETWORK_TYPE_LTE:
- case NETWORK_TYPE_IWLAN:
- case NETWORK_TYPE_LTE_CA:
- return NETWORK_CLASS_4_G;
- case NETWORK_TYPE_NR:
- return NETWORK_CLASS_5_G;
- default:
- return NETWORK_CLASS_UNKNOWN;
- }
- }
-
- /**
* Returns a string representation of the radio technology (network type)
* currently in use on the device.
* @return the name of the radio technology
@@ -8007,21 +7949,19 @@
* app has carrier privileges (see {@link #hasCarrierPrivileges}).
*
* @param operatorNumeric the PLMN ID of the network to select.
- * @param ran the initial suggested radio access network type.
- * If registration fails, the RAN is not available after, the RAN is not within the
- * network types specified by {@link #setPreferredNetworkTypeBitmask}, or the value is
- * {@link AccessNetworkConstants.AccessNetworkType#UNKNOWN}, modem will select
- * the next best RAN for network registration.
* @param persistSelection whether the selection will persist until reboot.
* If true, only allows attaching to the selected PLMN until reboot; otherwise,
* attach to the chosen PLMN and resume normal network selection next time.
+ * @param ran the initial suggested radio access network type.
+ * If registration fails, the RAN is not available after, the RAN is not within the
+ * network types specified by the preferred network types, or the value is
+ * {@link AccessNetworkConstants.AccessNetworkType#UNKNOWN}, modem will select
+ * the next best RAN for network registration.
* @return {@code true} on success; {@code false} on any failure.
- * @hide
*/
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- @SystemApi
public boolean setNetworkSelectionModeManual(@NonNull String operatorNumeric,
- @AccessNetworkConstants.RadioAccessNetworkType int ran, boolean persistSelection) {
+ boolean persistSelection, @AccessNetworkConstants.RadioAccessNetworkType int ran) {
return setNetworkSelectionModeManual(new OperatorInfo("" /* operatorAlphaLong */,
"" /* operatorAlphaShort */, operatorNumeric, ran), persistSelection);
}
@@ -11071,7 +11011,6 @@
* @param enabled True if enabling the data, otherwise disabling.
* @hide
*/
- @SystemApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setPolicyDataEnabled(boolean enabled) {
try {
@@ -11174,7 +11113,6 @@
* @param isEnabled {@code true} for enabling; {@code false} for disabling.
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void setAlwaysReportSignalStrength(boolean isEnabled) {
try {
@@ -11602,6 +11540,55 @@
@SystemApi
public static final long NETWORK_TYPE_BITMASK_IWLAN = (1 << (NETWORK_TYPE_IWLAN -1));
+ /** @hide */
+ public static final long NETWORK_CLASS_BITMASK_2G = NETWORK_TYPE_BITMASK_GSM
+ | NETWORK_TYPE_BITMASK_GPRS
+ | NETWORK_TYPE_BITMASK_EDGE
+ | NETWORK_TYPE_BITMASK_CDMA
+ | NETWORK_TYPE_BITMASK_1xRTT;
+
+ /** @hide */
+ public static final long NETWORK_CLASS_BITMASK_3G = NETWORK_TYPE_BITMASK_EVDO_0
+ | NETWORK_TYPE_BITMASK_EVDO_A
+ | NETWORK_TYPE_BITMASK_EVDO_B
+ | NETWORK_TYPE_BITMASK_EHRPD
+ | NETWORK_TYPE_BITMASK_HSUPA
+ | NETWORK_TYPE_BITMASK_HSDPA
+ | NETWORK_TYPE_BITMASK_HSPA
+ | NETWORK_TYPE_BITMASK_HSPAP
+ | NETWORK_TYPE_BITMASK_UMTS
+ | NETWORK_TYPE_BITMASK_TD_SCDMA;
+
+ /** @hide */
+ public static final long NETWORK_CLASS_BITMASK_4G = NETWORK_TYPE_BITMASK_LTE
+ | NETWORK_TYPE_BITMASK_LTE_CA
+ | NETWORK_TYPE_BITMASK_IWLAN;
+
+ /** @hide */
+ public static final long NETWORK_CLASS_BITMASK_5G = NETWORK_TYPE_BITMASK_NR;
+
+ /** @hide */
+ public static final long NETWORK_STANDARDS_FAMILY_BITMASK_3GPP = NETWORK_TYPE_BITMASK_GSM
+ | NETWORK_TYPE_BITMASK_GPRS
+ | NETWORK_TYPE_BITMASK_EDGE
+ | NETWORK_TYPE_BITMASK_HSUPA
+ | NETWORK_TYPE_BITMASK_HSDPA
+ | NETWORK_TYPE_BITMASK_HSPA
+ | NETWORK_TYPE_BITMASK_HSPAP
+ | NETWORK_TYPE_BITMASK_UMTS
+ | NETWORK_TYPE_BITMASK_TD_SCDMA
+ | NETWORK_TYPE_BITMASK_LTE
+ | NETWORK_TYPE_BITMASK_LTE_CA
+ | NETWORK_TYPE_BITMASK_NR;
+
+ /** @hide */
+ public static final long NETWORK_STANDARDS_FAMILY_BITMASK_3GPP2 = NETWORK_TYPE_BITMASK_CDMA
+ | NETWORK_TYPE_BITMASK_1xRTT
+ | NETWORK_TYPE_BITMASK_EVDO_0
+ | NETWORK_TYPE_BITMASK_EVDO_A
+ | NETWORK_TYPE_BITMASK_EVDO_B
+ | NETWORK_TYPE_BITMASK_EHRPD;
+
/**
* @return Modem supported radio access family bitmask
*
diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java
index 2c66047..784ee85 100644
--- a/test-mock/src/android/test/mock/MockContext.java
+++ b/test-mock/src/android/test/mock/MockContext.java
@@ -17,7 +17,6 @@
package android.test.mock;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.IApplicationThread;
import android.app.IServiceConnection;
@@ -480,10 +479,11 @@
throw new UnsupportedOperationException();
}
+ /** @hide */
@Override
- public void sendOrderedBroadcast(Intent intent, String receiverPermission, String receiverAppOp,
- Bundle options, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
- String initialData, Bundle initialExtras) {
+ public void sendOrderedBroadcast(Intent intent, int initialCode, String receiverPermission,
+ String receiverAppOp, BroadcastReceiver resultReceiver, Handler scheduler,
+ String initialData, Bundle initialExtras, Bundle options) {
throw new UnsupportedOperationException();
}
diff --git a/tests/BootImageProfileTest/DISABLED_TEST_MAPPING b/tests/BootImageProfileTest/TEST_MAPPING
similarity index 100%
rename from tests/BootImageProfileTest/DISABLED_TEST_MAPPING
rename to tests/BootImageProfileTest/TEST_MAPPING
diff --git a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
index 1c8b6be..61f3dba 100644
--- a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
+++ b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
@@ -30,6 +30,7 @@
private ITestDevice mTestDevice;
private static final String SYSTEM_SERVER_PROFILE =
"/data/misc/profiles/cur/0/android/primary.prof";
+ private static final boolean USE_PHENOTYPE = false;
@Override
public void setDevice(ITestDevice testDevice) {
@@ -41,16 +42,33 @@
return mTestDevice;
}
+ private String getProperty(String property) throws Exception {
+ if (USE_PHENOTYPE) {
+ return mTestDevice.getProperty("persist.device_config.runtime_native_boot."
+ + property);
+ } else {
+ return mTestDevice.executeShellCommand("getprop dalvik.vm." + property).trim();
+ }
+ }
+
+ private String setProperty(String property, String value) throws Exception {
+ if (USE_PHENOTYPE) {
+ return mTestDevice.executeShellCommand(
+ "device_config put runtime_native_boot " + property + " " + value);
+ } else {
+ return mTestDevice.executeShellCommand(
+ "setprop dalvik.vm." + property + " " + value);
+ }
+ }
+
/**
* Validate that the boot image profile properties are set.
*/
public void validateProperties() throws Exception {
- String res = mTestDevice.getProperty(
- "persist.device_config.runtime_native_boot.profilebootclasspath");
- assertTrue("profile boot class path not enabled", res != null && res.equals("true"));
- res = mTestDevice.getProperty(
- "persist.device_config.runtime_native_boot.profilesystemserver");
- assertTrue("profile system server not enabled", res != null && res.equals("true"));
+ String res = getProperty("profilebootclasspath");
+ assertTrue("profile boot class path not enabled: " + res, "true".equals(res));
+ res = getProperty("profilesystemserver");
+ assertTrue("profile system server not enabled: " + res, "true".equals(res));
}
private boolean forceSaveProfile(String pkg) throws Exception {
@@ -67,33 +85,48 @@
@Test
public void testSystemServerProfile() throws Exception {
final int numIterations = 20;
+ String res;
+ // Set properties and wait for them to be readable.
for (int i = 1; i <= numIterations; ++i) {
- String res;
- res = mTestDevice.getProperty(
- "persist.device_config.runtime_native_boot.profilebootclasspath");
- boolean profileBootClassPath = res != null && res.equals("true");
- res = mTestDevice.getProperty(
- "persist.device_config.runtime_native_boot.profilesystemserver");
- boolean profileSystemServer = res != null && res.equals("true");
+ String pbcp = getProperty("profilebootclasspath");
+ boolean profileBootClassPath = "true".equals(pbcp);
+ String pss = getProperty("profilesystemserver");
+ boolean profileSystemServer = "true".equals(pss);
if (profileBootClassPath && profileSystemServer) {
break;
}
if (i == numIterations) {
- assertTrue("profile system server not enabled", profileSystemServer);
- assertTrue("profile boot class path not enabled", profileSystemServer);
+ assertTrue("profile system server not enabled: " + pss, profileSystemServer);
+ assertTrue("profile boot class path not enabled: " + pbcp, profileBootClassPath);
}
- res = mTestDevice.executeShellCommand(
- "device_config put runtime_native_boot profilebootclasspath true");
- res = mTestDevice.executeShellCommand(
- "device_config put runtime_native_boot profilesystemserver true");
- res = mTestDevice.executeShellCommand("stop");
- res = mTestDevice.executeShellCommand("start");
- Thread.sleep(5000);
+ setProperty("profilebootclasspath", "true");
+ setProperty("profilesystemserver", "true");
+ Thread.sleep(1000);
}
+
+ // Restart shell and wait for system boot.
+ res = mTestDevice.executeShellCommand("stop");
+ assertTrue("stop shell: " + res, res.length() == 0);
+ res = mTestDevice.executeShellCommand("start");
+ assertTrue("start shell: " + res, res.length() == 0);
+ for (int i = 1; i <= numIterations; ++i) {
+ String pbcp = getProperty("profilebootclasspath");
+ boolean profileBootClassPath = "true".equals(pbcp);
+ String pss = getProperty("profilesystemserver");
+ boolean profileSystemServer = "true".equals(pss);
+ if (profileBootClassPath && profileSystemServer) {
+ break;
+ }
+ if (i == numIterations) {
+ assertTrue("profile system server not enabled: " + pss, profileSystemServer);
+ assertTrue("profile boot class path not enabled: " + pbcp, profileBootClassPath);
+ }
+ Thread.sleep(1000);
+ }
+
// Trunacte the profile before force it to be saved to prevent previous profiles
// causing the test to pass.
- String res;
res = mTestDevice.executeShellCommand("truncate -s 0 " + SYSTEM_SERVER_PROFILE).trim();
assertTrue(res, res.length() == 0);
// Wait up to 20 seconds for the profile to be saved.
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamIncompleteValueTest.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamIncompleteValueTest.java
new file mode 100644
index 0000000..167d5a4
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamIncompleteValueTest.java
@@ -0,0 +1,180 @@
+/*
+ * 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.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoParseException;
+import android.util.proto.ProtoStream;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamIncompleteValueTest extends TestCase {
+
+ /**
+ * Test that an incomplete varint at the end of a stream throws an exception
+ */
+ public void testIncompleteVarint() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> invalid varint value
+ (byte) 0x08,
+ (byte) 0xff,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readInt(fieldId1);
+ fail("Should have thrown a ProtoParseException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (ProtoParseException ppe) {
+ // good
+ stream.close();
+ return;
+ }
+ }
+ stream.close();
+ fail("Test should not have reached this point...");
+ }
+
+ /**
+ * Test that an incomplete fixed64 at the end of a stream throws an exception
+ */
+ public void testIncompleteFixed64() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED64;
+
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 2 -> invalid fixed64
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId2:
+ pi.readLong(fieldId2);
+ fail("Should have thrown a ProtoParseException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (ProtoParseException ppe) {
+ // good
+ stream.close();
+ return;
+ }
+ }
+ stream.close();
+ fail("Test should not have reached this point...");
+ }
+
+ /**
+ * Test that an incomplete length delimited value at the end of a stream throws an exception
+ */
+ public void testIncompleteLengthDelimited() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BYTES;
+
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 5 -> invalid byte array (has size 5 but only 4 values)
+ (byte) 0x2a,
+ (byte) 0x05,
+ (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId5:
+ pi.readBytes(fieldId5);
+ fail("Should have thrown a ProtoParseException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (ProtoParseException ppe) {
+ // good
+ stream.close();
+ return;
+ }
+ }
+ stream.close();
+ fail("Test should not have reached this point...");
+ }
+
+ /**
+ * Test that an incomplete fixed32 at the end of a stream throws an exception
+ */
+ public void testIncompleteFixed32() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED32;
+
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 2 -> invalid fixed32
+ (byte) 0x15,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId2:
+ pi.readInt(fieldId2);
+ fail("Should have thrown a ProtoParseException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (ProtoParseException ppe) {
+ // good
+ stream.close();
+ return;
+ }
+ }
+ stream.close();
+ fail("Test should not have reached this point...");
+ }
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoTests.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoTests.java
index cdf6ae2..685110c 100644
--- a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoTests.java
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoTests.java
@@ -39,6 +39,7 @@
suite.addTestSuite(ProtoInputStreamBytesTest.class);
suite.addTestSuite(ProtoInputStreamEnumTest.class);
suite.addTestSuite(ProtoInputStreamObjectTest.class);
+ suite.addTestSuite(ProtoInputStreamIncompleteValueTest.class);
return suite;
}
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 6f44230..c1d05e4 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -59,6 +59,7 @@
"libprotobuf-cpp-full",
"libz",
"libbuildversion",
+ "libidmap2_policies",
],
stl: "libc++_static",
group_static_libs: true,
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 137fbd6..1eb7d95 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -32,10 +32,16 @@
#include "text/Printer.h"
#include "util/Util.h"
+#include "idmap2/Policies.h"
+
using ::aapt::text::Printer;
using ::android::StringPiece;
using ::android::base::StringPrintf;
+using android::idmap2::policy::kPolicyStringToFlag;
+
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace aapt {
namespace {
@@ -246,32 +252,25 @@
Printer* printer_;
};
-std::string OverlayablePoliciesToString(OverlayableItem::PolicyFlags policies) {
- static const std::map<OverlayableItem::PolicyFlags, std::string> kFlagToString = {
- {OverlayableItem::kPublic, "public"},
- {OverlayableItem::kSystem, "system"},
- {OverlayableItem::kVendor, "vendor"},
- {OverlayableItem::kProduct, "product"},
- {OverlayableItem::kSignature, "signature"},
- {OverlayableItem::kOdm, "odm"},
- {OverlayableItem::kOem, "oem"},
- };
+std::string OverlayablePoliciesToString(PolicyFlags policies) {
std::string str;
- for (auto const& policy : kFlagToString) {
- if ((policies & policy.first) != policy.first) {
+
+ uint32_t remaining = policies;
+ for (auto const& policy : kPolicyStringToFlag) {
+ if ((policies & policy.second) != policy.second) {
continue;
}
if (!str.empty()) {
str.append("|");
}
- str.append(policy.second);
- policies &= ~policy.first;
+ str.append(policy.first.data());
+ remaining &= ~policy.second;
}
- if (policies != 0) {
+ if (remaining != 0) {
if (!str.empty()) {
str.append("|");
}
- str.append(StringPrintf("0x%08x", policies));
+ str.append(StringPrintf("0x%08x", remaining));
}
return !str.empty() ? str : "none";
}
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 74e2a098..234cbc4 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -32,11 +32,15 @@
#include "util/Util.h"
#include "xml/XmlPullParser.h"
+#include "idmap2/Policies.h"
+
using ::aapt::ResourceUtils::StringBuilder;
using ::aapt::text::Utf8Iterator;
using ::android::ConfigDescription;
using ::android::StringPiece;
+using android::idmap2::policy::kPolicyStringToFlag;
+
namespace aapt {
constexpr const char* sXliffNamespaceUri = "urn:oasis:names:tc:xliff:document:1.2";
@@ -1063,7 +1067,7 @@
bool error = false;
std::string comment;
- OverlayableItem::PolicyFlags current_policies = OverlayableItem::Policy::kNone;
+ PolicyFlags current_policies = PolicyFlags::NONE;
const size_t start_depth = parser->depth();
while (xml::XmlPullParser::IsGoodEvent(parser->Next())) {
xml::XmlPullParser::Event event = parser->event();
@@ -1073,7 +1077,7 @@
} else if (event == xml::XmlPullParser::Event::kEndElement
&& parser->depth() == start_depth + 1) {
// Clear the current policies when exiting the <policy> tags
- current_policies = OverlayableItem::Policy::kNone;
+ current_policies = PolicyFlags::NONE;
continue;
} else if (event == xml::XmlPullParser::Event::kComment) {
// Retrieve the comment of individual <item> tags
@@ -1088,7 +1092,7 @@
const std::string& element_name = parser->element_name();
const std::string& element_namespace = parser->element_namespace();
if (element_namespace.empty() && element_name == "item") {
- if (current_policies == OverlayableItem::Policy::kNone) {
+ if (current_policies == PolicyFlags::NONE) {
diag_->Error(DiagMessage(element_source)
<< "<item> within an <overlayable> must be inside a <policy> block");
error = true;
@@ -1133,7 +1137,7 @@
out_resource->child_resources.push_back(std::move(child_resource));
} else if (element_namespace.empty() && element_name == "policy") {
- if (current_policies != OverlayableItem::Policy::kNone) {
+ if (current_policies != PolicyFlags::NONE) {
// If the policy list is not empty, then we are currently inside a policy element
diag_->Error(DiagMessage(element_source) << "<policy> blocks cannot be recursively nested");
error = true;
@@ -1141,21 +1145,14 @@
} else if (Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
// Parse the polices separated by vertical bar characters to allow for specifying multiple
// policies. Items within the policy tag will have the specified policy.
- static const auto kPolicyMap =
- ImmutableMap<StringPiece, OverlayableItem::Policy>::CreatePreSorted({
- {"odm", OverlayableItem::Policy::kOdm},
- {"oem", OverlayableItem::Policy::kOem},
- {"product", OverlayableItem::Policy::kProduct},
- {"public", OverlayableItem::Policy::kPublic},
- {"signature", OverlayableItem::Policy::kSignature},
- {"system", OverlayableItem::Policy::kSystem},
- {"vendor", OverlayableItem::Policy::kVendor},
- });
-
for (const StringPiece& part : util::Tokenize(maybe_type.value(), '|')) {
StringPiece trimmed_part = util::TrimWhitespace(part);
- const auto policy = kPolicyMap.find(trimmed_part);
- if (policy == kPolicyMap.end()) {
+ const auto policy = std::find_if(kPolicyStringToFlag.begin(),
+ kPolicyStringToFlag.end(),
+ [trimmed_part](const auto& it) {
+ return trimmed_part == it.first;
+ });
+ if (policy == kPolicyStringToFlag.end()) {
diag_->Error(DiagMessage(element_source)
<< "<policy> has unsupported type '" << trimmed_part << "'");
error = true;
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 24531bc..9b70079 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -41,6 +41,8 @@
using ::testing::SizeIs;
using ::testing::StrEq;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace aapt {
constexpr const char* kXmlPreamble = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
@@ -959,7 +961,7 @@
OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSignature));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::SIGNATURE));
search_result = table_.FindResource(test::ParseNameOrDie("drawable/bar"));
ASSERT_TRUE(search_result);
@@ -968,7 +970,7 @@
result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSignature));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::SIGNATURE));
}
TEST_F(ResourceParserTest, ParseOverlayableRequiresName) {
@@ -1005,6 +1007,9 @@
<policy type="oem">
<item type="string" name="buz" />
</policy>
+ <policy type="actor">
+ <item type="string" name="actor" />
+ </policy>
</overlayable>)";
ASSERT_TRUE(TestParse(input));
@@ -1014,7 +1019,7 @@
ASSERT_TRUE(search_result.value().entry->overlayable_item);
OverlayableItem result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::PRODUCT_PARTITION));
search_result = table_.FindResource(test::ParseNameOrDie("string/fiz"));
ASSERT_TRUE(search_result);
@@ -1022,7 +1027,7 @@
ASSERT_TRUE(search_result.value().entry->overlayable_item);
result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSystem));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::SYSTEM_PARTITION));
search_result = table_.FindResource(test::ParseNameOrDie("string/fuz"));
ASSERT_TRUE(search_result);
@@ -1030,7 +1035,7 @@
ASSERT_TRUE(search_result.value().entry->overlayable_item);
result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kVendor));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::VENDOR_PARTITION));
search_result = table_.FindResource(test::ParseNameOrDie("string/faz"));
ASSERT_TRUE(search_result);
@@ -1038,7 +1043,7 @@
ASSERT_TRUE(search_result.value().entry->overlayable_item);
result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::PUBLIC));
search_result = table_.FindResource(test::ParseNameOrDie("string/foz"));
ASSERT_TRUE(search_result);
@@ -1046,7 +1051,7 @@
ASSERT_TRUE(search_result.value().entry->overlayable_item);
result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSignature));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::SIGNATURE));
search_result = table_.FindResource(test::ParseNameOrDie("string/biz"));
ASSERT_TRUE(search_result);
@@ -1054,7 +1059,7 @@
ASSERT_TRUE(search_result.value().entry->overlayable_item);
result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kOdm));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::ODM_PARTITION));
search_result = table_.FindResource(test::ParseNameOrDie("string/buz"));
ASSERT_TRUE(search_result);
@@ -1062,7 +1067,15 @@
ASSERT_TRUE(search_result.value().entry->overlayable_item);
result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kOem));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::OEM_PARTITION));
+
+ search_result = table_.FindResource(test::ParseNameOrDie("string/actor"));
+ ASSERT_TRUE(search_result);
+ ASSERT_THAT(search_result.value().entry, NotNull());
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ result_overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::ACTOR_SIGNATURE));
}
TEST_F(ResourceParserTest, ParseOverlayableNoPolicyError) {
@@ -1125,8 +1138,8 @@
ASSERT_TRUE(search_result.value().entry->overlayable_item);
OverlayableItem result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kVendor
- | OverlayableItem::Policy::kPublic));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::VENDOR_PARTITION
+ | PolicyFlags::PUBLIC));
search_result = table_.FindResource(test::ParseNameOrDie("string/bar"));
ASSERT_TRUE(search_result);
@@ -1134,8 +1147,8 @@
ASSERT_TRUE(search_result.value().entry->overlayable_item);
result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct
- | OverlayableItem::Policy::kSystem));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::PRODUCT_PARTITION
+ | PolicyFlags::SYSTEM_PARTITION));
}
TEST_F(ResourceParserTest, DuplicateOverlayableIsError) {
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index 30ba1ae..93a7a31 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -36,6 +36,8 @@
#include <unordered_map>
#include <vector>
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace aapt {
// The Public status of a resource.
@@ -75,36 +77,8 @@
struct OverlayableItem {
explicit OverlayableItem(const std::shared_ptr<Overlayable>& overlayable)
: overlayable(overlayable) {}
-
- // Represents the types overlays that are allowed to overlay the resource.
- typedef uint32_t PolicyFlags;
- enum Policy : uint32_t {
- kNone = 0x00000000,
-
- // The resource can be overlaid by any overlay.
- kPublic = 0x00000001,
-
- // The resource can be overlaid by any overlay on the system partition.
- kSystem = 0x00000002,
-
- // The resource can be overlaid by any overlay on the vendor partition.
- kVendor = 0x00000004,
-
- // The resource can be overlaid by any overlay on the product partition.
- kProduct = 0x00000008,
-
- // The resource can be overlaid by any overlay signed with the same signature as its actor.
- kSignature = 0x00000010,
-
- // The resource can be overlaid by any overlay on the odm partition.
- kOdm = 0x00000020,
-
- // The resource can be overlaid by any overlay on the oem partition.
- kOem = 0x00000040,
- };
-
std::shared_ptr<Overlayable> overlayable;
- PolicyFlags policies = Policy::kNone;
+ PolicyFlags policies = PolicyFlags::NONE;
std::string comment;
Source source;
};
diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp
index b97dc6b..9271a7e 100644
--- a/tools/aapt2/ResourceTable_test.cpp
+++ b/tools/aapt2/ResourceTable_test.cpp
@@ -30,6 +30,8 @@
using ::testing::NotNull;
using ::testing::StrEq;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace aapt {
TEST(ResourceTableTest, FailToAddResourceWithBadName) {
@@ -247,8 +249,8 @@
auto overlayable = std::make_shared<Overlayable>("Name", "overlay://theme",
Source("res/values/overlayable.xml", 40));
OverlayableItem overlayable_item(overlayable);
- overlayable_item.policies |= OverlayableItem::Policy::kProduct;
- overlayable_item.policies |= OverlayableItem::Policy::kVendor;
+ overlayable_item.policies |= PolicyFlags::PRODUCT_PARTITION;
+ overlayable_item.policies |= PolicyFlags::VENDOR_PARTITION;
overlayable_item.comment = "comment";
overlayable_item.source = Source("res/values/overlayable.xml", 42);
@@ -264,8 +266,8 @@
EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
EXPECT_THAT(result_overlayable_item.overlayable->source.path, Eq("res/values/overlayable.xml"));
EXPECT_THAT(result_overlayable_item.overlayable->source.line, 40);
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct
- | OverlayableItem::Policy::kVendor));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::PRODUCT_PARTITION
+ | PolicyFlags::VENDOR_PARTITION));
ASSERT_THAT(result_overlayable_item.comment, StrEq("comment"));
EXPECT_THAT(result_overlayable_item.source.path, Eq("res/values/overlayable.xml"));
EXPECT_THAT(result_overlayable_item.source.line, 42);
@@ -277,17 +279,17 @@
const ResourceName foo = test::ParseNameOrDie("android:string/foo");
auto group = std::make_shared<Overlayable>("Name", "overlay://theme");
OverlayableItem overlayable(group);
- overlayable.policies = OverlayableItem::Policy::kProduct;
+ overlayable.policies = PolicyFlags::PRODUCT_PARTITION;
ASSERT_TRUE(table.SetOverlayable(foo, overlayable, test::GetDiagnostics()));
const ResourceName bar = test::ParseNameOrDie("android:string/bar");
OverlayableItem overlayable2(group);
- overlayable2.policies = OverlayableItem::Policy::kProduct;
+ overlayable2.policies = PolicyFlags::PRODUCT_PARTITION;
ASSERT_TRUE(table.SetOverlayable(bar, overlayable2, test::GetDiagnostics()));
const ResourceName baz = test::ParseNameOrDie("android:string/baz");
OverlayableItem overlayable3(group);
- overlayable3.policies = OverlayableItem::Policy::kVendor;
+ overlayable3.policies = PolicyFlags::VENDOR_PARTITION;
ASSERT_TRUE(table.SetOverlayable(baz, overlayable3, test::GetDiagnostics()));
}
@@ -296,12 +298,12 @@
const ResourceName foo = test::ParseNameOrDie("android:string/foo");
OverlayableItem overlayable_item(std::make_shared<Overlayable>("Name", "overlay://theme"));
- overlayable_item.policies = OverlayableItem::Policy::kProduct;
+ overlayable_item.policies = PolicyFlags::PRODUCT_PARTITION;
ASSERT_TRUE(table.SetOverlayable(foo, overlayable_item, test::GetDiagnostics()));
const ResourceName bar = test::ParseNameOrDie("android:string/bar");
OverlayableItem overlayable_item2(std::make_shared<Overlayable>("Name2", "overlay://theme"));
- overlayable_item2.policies = OverlayableItem::Policy::kProduct;
+ overlayable_item2.policies = PolicyFlags::PRODUCT_PARTITION;
ASSERT_TRUE(table.SetOverlayable(bar, overlayable_item2, test::GetDiagnostics()));
}
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index 8a2f5af..ab9ce66 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -167,6 +167,7 @@
SIGNATURE = 5;
ODM = 6;
OEM = 7;
+ ACTOR = 8;
}
// The location of the <item> declaration in source.
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 5b6935b..bf886c2 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -1766,9 +1766,12 @@
return 1;
}
- // First extract the Package name without modifying it (via --rename-manifest-package).
- if (Maybe<AppInfo> maybe_app_info =
+ // Determine the package name under which to merge resources.
+ if (options_.rename_resources_package) {
+ context_->SetCompilationPackage(options_.rename_resources_package.value());
+ } else if (Maybe<AppInfo> maybe_app_info =
ExtractAppInfoFromManifest(manifest_xml.get(), context_->GetDiagnostics())) {
+ // Extract the package name from the manifest ignoring the value of --rename-manifest-package.
const AppInfo& app_info = maybe_app_info.value();
context_->SetCompilationPackage(app_info.package);
}
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
index 4722358..e7be434 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -45,6 +45,7 @@
bool auto_add_overlay = false;
bool override_styles_instead_of_overlaying = false;
OutputFormat output_format = OutputFormat::kApk;
+ Maybe<std::string> rename_resources_package;
// Java/Proguard options.
Maybe<std::string> generate_java_class_path;
@@ -256,6 +257,8 @@
&options_.override_styles_instead_of_overlaying);
AddOptionalFlag("--rename-manifest-package", "Renames the package in AndroidManifest.xml.",
&options_.manifest_fixer_options.rename_manifest_package);
+ AddOptionalFlag("--rename-resources-package", "Renames the package in resources table",
+ &options_.rename_resources_package);
AddOptionalFlag("--rename-instrumentation-target-package",
"Changes the name of the target package for instrumentation. Most useful\n"
"when used in conjunction with --rename-manifest-package.",
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index fcd6aaa..f362744 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -455,35 +455,6 @@
const ResTable_overlayable_policy_header* policy_header =
ConvertTo<ResTable_overlayable_policy_header>(parser.chunk());
- OverlayableItem::PolicyFlags policies = OverlayableItem::Policy::kNone;
- if (policy_header->policy_flags & ResTable_overlayable_policy_header::POLICY_PUBLIC) {
- policies |= OverlayableItem::Policy::kPublic;
- }
- if (policy_header->policy_flags
- & ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION) {
- policies |= OverlayableItem::Policy::kSystem;
- }
- if (policy_header->policy_flags
- & ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION) {
- policies |= OverlayableItem::Policy::kVendor;
- }
- if (policy_header->policy_flags
- & ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION) {
- policies |= OverlayableItem::Policy::kProduct;
- }
- if (policy_header->policy_flags
- & ResTable_overlayable_policy_header::POLICY_SIGNATURE) {
- policies |= OverlayableItem::Policy::kSignature;
- }
- if (policy_header->policy_flags
- & ResTable_overlayable_policy_header::POLICY_ODM_PARTITION) {
- policies |= OverlayableItem::Policy::kOdm;
- }
- if (policy_header->policy_flags
- & ResTable_overlayable_policy_header::POLICY_OEM_PARTITION) {
- policies |= OverlayableItem::Policy::kOem;
- }
-
const ResTable_ref* const ref_begin = reinterpret_cast<const ResTable_ref*>(
((uint8_t *)policy_header) + util::DeviceToHost32(policy_header->header.headerSize));
const ResTable_ref* const ref_end = ref_begin
@@ -501,7 +472,7 @@
}
OverlayableItem overlayable_item(overlayable);
- overlayable_item.policies = policies;
+ overlayable_item.policies = policy_header->policy_flags;
if (!table_->SetOverlayable(iter->second, overlayable_item, diag_)) {
return false;
}
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index cbce8a5..4784ecf 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -233,7 +233,7 @@
struct OverlayableChunk {
std::string actor;
Source source;
- std::map<OverlayableItem::PolicyFlags, std::set<ResourceId>> policy_ids;
+ std::map<PolicyFlags, std::set<ResourceId>> policy_ids;
};
class PackageFlattener {
@@ -493,35 +493,12 @@
return false;
}
- uint32_t policy_flags = 0;
- if (item.policies & OverlayableItem::Policy::kPublic) {
- policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
- }
- if (item.policies & OverlayableItem::Policy::kSystem) {
- policy_flags |= ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION;
- }
- if (item.policies & OverlayableItem::Policy::kVendor) {
- policy_flags |= ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION;
- }
- if (item.policies & OverlayableItem::Policy::kProduct) {
- policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION;
- }
- if (item.policies & OverlayableItem::Policy::kSignature) {
- policy_flags |= ResTable_overlayable_policy_header::POLICY_SIGNATURE;
- }
- if (item.policies & OverlayableItem::Policy::kOdm) {
- policy_flags |= ResTable_overlayable_policy_header::POLICY_ODM_PARTITION;
- }
- if (item.policies & OverlayableItem::Policy::kOem) {
- policy_flags |= ResTable_overlayable_policy_header::POLICY_OEM_PARTITION;
- }
-
- auto policy = overlayable_chunk->policy_ids.find(policy_flags);
+ auto policy = overlayable_chunk->policy_ids.find(item.policies);
if (policy != overlayable_chunk->policy_ids.end()) {
policy->second.insert(id);
} else {
overlayable_chunk->policy_ids.insert(
- std::make_pair(policy_flags, std::set<ResourceId>{id}));
+ std::make_pair(item.policies, std::set<ResourceId>{id}));
}
}
}
@@ -559,7 +536,8 @@
ChunkWriter policy_writer(buffer);
auto* policy_type = policy_writer.StartChunk<ResTable_overlayable_policy_header>(
RES_TABLE_OVERLAYABLE_POLICY_TYPE);
- policy_type->policy_flags = util::HostToDevice32(static_cast<uint32_t>(policy_ids.first));
+ policy_type->policy_flags =
+ static_cast<PolicyFlags>(util::HostToDevice32(static_cast<uint32_t>(policy_ids.first)));
policy_type->entry_count = util::HostToDevice32(static_cast<uint32_t>(
policy_ids.second.size()));
// Write the ids after the policy header
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index af2293f..59627ce 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -32,6 +32,8 @@
using ::testing::IsNull;
using ::testing::NotNull;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace aapt {
class TableFlattenerTest : public ::testing::Test {
@@ -671,9 +673,9 @@
TEST_F(TableFlattenerTest, FlattenOverlayable) {
OverlayableItem overlayable_item(std::make_shared<Overlayable>("TestName", "overlay://theme"));
- overlayable_item.policies |= OverlayableItem::Policy::kProduct;
- overlayable_item.policies |= OverlayableItem::Policy::kSystem;
- overlayable_item.policies |= OverlayableItem::Policy::kVendor;
+ overlayable_item.policies |= PolicyFlags::PRODUCT_PARTITION;
+ overlayable_item.policies |= PolicyFlags::SYSTEM_PARTITION;
+ overlayable_item.policies |= PolicyFlags::VENDOR_PARTITION;
std::string name = "com.app.test:integer/overlayable";
std::unique_ptr<ResourceTable> table =
@@ -691,27 +693,27 @@
ASSERT_THAT(search_result.value().entry, NotNull());
ASSERT_TRUE(search_result.value().entry->overlayable_item);
OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
- EXPECT_EQ(result_overlayable_item.policies, OverlayableItem::Policy::kSystem
- | OverlayableItem::Policy::kVendor
- | OverlayableItem::Policy::kProduct);
+ EXPECT_EQ(result_overlayable_item.policies, PolicyFlags::SYSTEM_PARTITION
+ | PolicyFlags::VENDOR_PARTITION
+ | PolicyFlags::PRODUCT_PARTITION);
}
TEST_F(TableFlattenerTest, FlattenMultipleOverlayablePolicies) {
auto overlayable = std::make_shared<Overlayable>("TestName", "overlay://theme");
std::string name_zero = "com.app.test:integer/overlayable_zero_item";
OverlayableItem overlayable_item_zero(overlayable);
- overlayable_item_zero.policies |= OverlayableItem::Policy::kProduct;
- overlayable_item_zero.policies |= OverlayableItem::Policy::kSystem;
+ overlayable_item_zero.policies |= PolicyFlags::PRODUCT_PARTITION;
+ overlayable_item_zero.policies |= PolicyFlags::SYSTEM_PARTITION;
std::string name_one = "com.app.test:integer/overlayable_one_item";
OverlayableItem overlayable_item_one(overlayable);
- overlayable_item_one.policies |= OverlayableItem::Policy::kPublic;
+ overlayable_item_one.policies |= PolicyFlags::PUBLIC;
std::string name_two = "com.app.test:integer/overlayable_two_item";
OverlayableItem overlayable_item_two(overlayable);
- overlayable_item_two.policies |= OverlayableItem::Policy::kProduct;
- overlayable_item_two.policies |= OverlayableItem::Policy::kSystem;
- overlayable_item_two.policies |= OverlayableItem::Policy::kVendor;
+ overlayable_item_two.policies |= PolicyFlags::PRODUCT_PARTITION;
+ overlayable_item_two.policies |= PolicyFlags::SYSTEM_PARTITION;
+ overlayable_item_two.policies |= PolicyFlags::VENDOR_PARTITION;
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
@@ -732,47 +734,48 @@
ASSERT_THAT(search_result.value().entry, NotNull());
ASSERT_TRUE(search_result.value().entry->overlayable_item);
OverlayableItem& overlayable_item = search_result.value().entry->overlayable_item.value();
- EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kSystem
- | OverlayableItem::Policy::kProduct);
+ EXPECT_EQ(overlayable_item.policies, PolicyFlags::SYSTEM_PARTITION
+ | PolicyFlags::PRODUCT_PARTITION);
search_result = output_table.FindResource(test::ParseNameOrDie(name_one));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
ASSERT_TRUE(search_result.value().entry->overlayable_item);
overlayable_item = search_result.value().entry->overlayable_item.value();
- EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kPublic);
+ EXPECT_EQ(overlayable_item.policies, PolicyFlags::PUBLIC);
search_result = output_table.FindResource(test::ParseNameOrDie(name_two));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
ASSERT_TRUE(search_result.value().entry->overlayable_item);
overlayable_item = search_result.value().entry->overlayable_item.value();
- EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kSystem
- | OverlayableItem::Policy::kProduct
- | OverlayableItem::Policy::kVendor);
+ EXPECT_EQ(overlayable_item.policies, PolicyFlags::SYSTEM_PARTITION
+ | PolicyFlags::PRODUCT_PARTITION
+ | PolicyFlags::VENDOR_PARTITION);
}
TEST_F(TableFlattenerTest, FlattenMultipleOverlayable) {
auto group = std::make_shared<Overlayable>("TestName", "overlay://theme");
std::string name_zero = "com.app.test:integer/overlayable_zero";
OverlayableItem overlayable_item_zero(group);
- overlayable_item_zero.policies |= OverlayableItem::Policy::kProduct;
- overlayable_item_zero.policies |= OverlayableItem::Policy::kSystem;
+ overlayable_item_zero.policies |= PolicyFlags::PRODUCT_PARTITION;
+ overlayable_item_zero.policies |= PolicyFlags::SYSTEM_PARTITION;
auto group_one = std::make_shared<Overlayable>("OtherName", "overlay://customization");
std::string name_one = "com.app.test:integer/overlayable_one";
OverlayableItem overlayable_item_one(group_one);
- overlayable_item_one.policies |= OverlayableItem::Policy::kPublic;
+ overlayable_item_one.policies |= PolicyFlags::PUBLIC;
std::string name_two = "com.app.test:integer/overlayable_two";
OverlayableItem overlayable_item_two(group);
- overlayable_item_two.policies |= OverlayableItem::Policy::kOdm;
- overlayable_item_two.policies |= OverlayableItem::Policy::kOem;
- overlayable_item_two.policies |= OverlayableItem::Policy::kVendor;
+ overlayable_item_two.policies |= PolicyFlags::ODM_PARTITION;
+ overlayable_item_two.policies |= PolicyFlags::OEM_PARTITION;
+ overlayable_item_two.policies |= PolicyFlags::VENDOR_PARTITION;
std::string name_three = "com.app.test:integer/overlayable_three";
OverlayableItem overlayable_item_three(group_one);
- overlayable_item_three.policies |= OverlayableItem::Policy::kSignature;
+ overlayable_item_three.policies |= PolicyFlags::SIGNATURE;
+ overlayable_item_three.policies |= PolicyFlags::ACTOR_SIGNATURE;
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
@@ -796,8 +799,8 @@
OverlayableItem& result_overlayable = search_result.value().entry->overlayable_item.value();
EXPECT_EQ(result_overlayable.overlayable->name, "TestName");
EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://theme");
- EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kSystem
- | OverlayableItem::Policy::kProduct);
+ EXPECT_EQ(result_overlayable.policies, PolicyFlags::SYSTEM_PARTITION
+ | PolicyFlags::PRODUCT_PARTITION);
search_result = output_table.FindResource(test::ParseNameOrDie(name_one));
ASSERT_TRUE(search_result);
@@ -806,7 +809,7 @@
result_overlayable = search_result.value().entry->overlayable_item.value();
EXPECT_EQ(result_overlayable.overlayable->name, "OtherName");
EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://customization");
- EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kPublic);
+ EXPECT_EQ(result_overlayable.policies, PolicyFlags::PUBLIC);
search_result = output_table.FindResource(test::ParseNameOrDie(name_two));
ASSERT_TRUE(search_result);
@@ -815,9 +818,9 @@
result_overlayable = search_result.value().entry->overlayable_item.value();
EXPECT_EQ(result_overlayable.overlayable->name, "TestName");
EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://theme");
- EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kOdm
- | OverlayableItem::Policy::kOem
- | OverlayableItem::Policy::kVendor);
+ EXPECT_EQ(result_overlayable.policies, PolicyFlags::ODM_PARTITION
+ | PolicyFlags::OEM_PARTITION
+ | PolicyFlags::VENDOR_PARTITION);
search_result = output_table.FindResource(test::ParseNameOrDie(name_three));
ASSERT_TRUE(search_result);
@@ -826,7 +829,8 @@
result_overlayable = search_result.value().entry->overlayable_item.value();
EXPECT_EQ(result_overlayable.overlayable->name, "OtherName");
EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://customization");
- EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kSignature);
+ EXPECT_EQ(result_overlayable.policies, PolicyFlags::SIGNATURE
+ | PolicyFlags::ACTOR_SIGNATURE);
}
TEST_F(TableFlattenerTest, FlattenOverlayableNoPolicyFails) {
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index 4cd6e93..2fd01d7 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -30,6 +30,8 @@
using ::android::LocaleValue;
using ::android::ResStringPool;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace aapt {
namespace {
@@ -379,25 +381,28 @@
for (const int policy : pb_overlayable.policy()) {
switch (policy) {
case pb::OverlayableItem::PUBLIC:
- out_overlayable->policies |= OverlayableItem::Policy::kPublic;
+ out_overlayable->policies |= PolicyFlags::PUBLIC;
break;
case pb::OverlayableItem::SYSTEM:
- out_overlayable->policies |= OverlayableItem::Policy::kSystem;
+ out_overlayable->policies |= PolicyFlags::SYSTEM_PARTITION;
break;
case pb::OverlayableItem::VENDOR:
- out_overlayable->policies |= OverlayableItem::Policy::kVendor;
+ out_overlayable->policies |= PolicyFlags::VENDOR_PARTITION;
break;
case pb::OverlayableItem::PRODUCT:
- out_overlayable->policies |= OverlayableItem::Policy::kProduct;
+ out_overlayable->policies |= PolicyFlags::PRODUCT_PARTITION;
break;
case pb::OverlayableItem::SIGNATURE:
- out_overlayable->policies |= OverlayableItem::Policy::kSignature;
+ out_overlayable->policies |= PolicyFlags::SIGNATURE;
break;
case pb::OverlayableItem::ODM:
- out_overlayable->policies |= OverlayableItem::Policy::kOdm;
+ out_overlayable->policies |= PolicyFlags::ODM_PARTITION;
break;
case pb::OverlayableItem::OEM:
- out_overlayable->policies |= OverlayableItem::Policy::kOem;
+ out_overlayable->policies |= PolicyFlags::OEM_PARTITION;
+ break;
+ case pb::OverlayableItem::ACTOR:
+ out_overlayable->policies |= PolicyFlags::ACTOR_SIGNATURE;
break;
default:
*out_error = "unknown overlayable policy";
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index d9f6c19..ba6df22 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -21,6 +21,8 @@
using android::ConfigDescription;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace aapt {
void SerializeStringPoolToPb(const StringPool& pool, pb::StringPool* out_pb_pool, IDiagnostics* diag) {
@@ -299,27 +301,30 @@
pb::OverlayableItem* pb_overlayable_item = pb_entry->mutable_overlayable_item();
pb_overlayable_item->set_overlayable_idx(i);
- if (overlayable_item.policies & OverlayableItem::Policy::kPublic) {
+ if (overlayable_item.policies & PolicyFlags::PUBLIC) {
pb_overlayable_item->add_policy(pb::OverlayableItem::PUBLIC);
}
- if (overlayable_item.policies & OverlayableItem::Policy::kProduct) {
+ if (overlayable_item.policies & PolicyFlags::PRODUCT_PARTITION) {
pb_overlayable_item->add_policy(pb::OverlayableItem::PRODUCT);
}
- if (overlayable_item.policies & OverlayableItem::Policy::kSystem) {
+ if (overlayable_item.policies & PolicyFlags::SYSTEM_PARTITION) {
pb_overlayable_item->add_policy(pb::OverlayableItem::SYSTEM);
}
- if (overlayable_item.policies & OverlayableItem::Policy::kVendor) {
+ if (overlayable_item.policies & PolicyFlags::VENDOR_PARTITION) {
pb_overlayable_item->add_policy(pb::OverlayableItem::VENDOR);
}
- if (overlayable_item.policies & OverlayableItem::Policy::kSignature) {
+ if (overlayable_item.policies & PolicyFlags::SIGNATURE) {
pb_overlayable_item->add_policy(pb::OverlayableItem::SIGNATURE);
}
- if (overlayable_item.policies & OverlayableItem::Policy::kOdm) {
+ if (overlayable_item.policies & PolicyFlags::ODM_PARTITION) {
pb_overlayable_item->add_policy(pb::OverlayableItem::ODM);
}
- if (overlayable_item.policies & OverlayableItem::Policy::kOem) {
+ if (overlayable_item.policies & PolicyFlags::OEM_PARTITION) {
pb_overlayable_item->add_policy(pb::OverlayableItem::OEM);
}
+ if (overlayable_item.policies & PolicyFlags::ACTOR_SIGNATURE) {
+ pb_overlayable_item->add_policy(pb::OverlayableItem::ACTOR);
+ }
if (source_pool != nullptr) {
SerializeSourceToPb(overlayable_item.source, source_pool,
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index 61a8335..1a7de6d 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -28,6 +28,8 @@
using ::testing::SizeIs;
using ::testing::StrEq;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace aapt {
class MockFileCollection : public io::IFileCollection {
@@ -171,7 +173,7 @@
EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
EXPECT_THAT(result_overlayable_item.overlayable->source.path, Eq("res/values/overlayable.xml"));
EXPECT_THAT(result_overlayable_item.overlayable->source.line, Eq(40));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::NONE));
EXPECT_THAT(result_overlayable_item.source.path, Eq("res/values/overlayable.xml"));
EXPECT_THAT(result_overlayable_item.source.line, Eq(42));
}
@@ -516,23 +518,28 @@
TEST(ProtoSerializeTest, SerializeAndDeserializeOverlayable) {
OverlayableItem overlayable_item_foo(std::make_shared<Overlayable>(
"CustomizableResources", "overlay://customization"));
- overlayable_item_foo.policies |= OverlayableItem::Policy::kSystem;
- overlayable_item_foo.policies |= OverlayableItem::Policy::kProduct;
+ overlayable_item_foo.policies |= PolicyFlags::SYSTEM_PARTITION;
+ overlayable_item_foo.policies |= PolicyFlags::PRODUCT_PARTITION;
OverlayableItem overlayable_item_bar(std::make_shared<Overlayable>(
"TaskBar", "overlay://theme"));
- overlayable_item_bar.policies |= OverlayableItem::Policy::kPublic;
- overlayable_item_bar.policies |= OverlayableItem::Policy::kVendor;
+ overlayable_item_bar.policies |= PolicyFlags::PUBLIC;
+ overlayable_item_bar.policies |= PolicyFlags::VENDOR_PARTITION;
OverlayableItem overlayable_item_baz(std::make_shared<Overlayable>(
"FontPack", "overlay://theme"));
- overlayable_item_baz.policies |= OverlayableItem::Policy::kPublic;
+ overlayable_item_baz.policies |= PolicyFlags::PUBLIC;
OverlayableItem overlayable_item_boz(std::make_shared<Overlayable>(
"IconPack", "overlay://theme"));
- overlayable_item_boz.policies |= OverlayableItem::Policy::kSignature;
- overlayable_item_boz.policies |= OverlayableItem::Policy::kOdm;
- overlayable_item_boz.policies |= OverlayableItem::Policy::kOem;
+ overlayable_item_boz.policies |= PolicyFlags::SIGNATURE;
+ overlayable_item_boz.policies |= PolicyFlags::ODM_PARTITION;
+ overlayable_item_boz.policies |= PolicyFlags::OEM_PARTITION;
+
+ OverlayableItem overlayable_item_actor_config(std::make_shared<Overlayable>(
+ "ActorConfig", "overlay://theme"));
+ overlayable_item_actor_config.policies |= PolicyFlags::SIGNATURE;
+ overlayable_item_actor_config.policies |= PolicyFlags::ACTOR_SIGNATURE;
OverlayableItem overlayable_item_biz(std::make_shared<Overlayable>(
"Other", "overlay://customization"));
@@ -546,6 +553,7 @@
.SetOverlayable("com.app.a:bool/baz", overlayable_item_baz)
.SetOverlayable("com.app.a:bool/boz", overlayable_item_boz)
.SetOverlayable("com.app.a:bool/biz", overlayable_item_biz)
+ .SetOverlayable("com.app.a:bool/actor_config", overlayable_item_actor_config)
.AddValue("com.app.a:bool/fiz", ResourceUtils::TryParseBool("true"))
.Build();
@@ -565,8 +573,8 @@
OverlayableItem& overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(overlayable_item.overlayable->name, Eq("CustomizableResources"));
EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://customization"));
- EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kSystem
- | OverlayableItem::Policy::kProduct));
+ EXPECT_THAT(overlayable_item.policies, Eq(PolicyFlags::SYSTEM_PARTITION
+ | PolicyFlags::PRODUCT_PARTITION));
search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/bar"));
ASSERT_TRUE(search_result);
@@ -574,8 +582,8 @@
overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(overlayable_item.overlayable->name, Eq("TaskBar"));
EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme"));
- EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic
- | OverlayableItem::Policy::kVendor));
+ EXPECT_THAT(overlayable_item.policies, Eq(PolicyFlags::PUBLIC
+ | PolicyFlags::VENDOR_PARTITION));
search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/baz"));
ASSERT_TRUE(search_result);
@@ -583,7 +591,7 @@
overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(overlayable_item.overlayable->name, Eq("FontPack"));
EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme"));
- EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic));
+ EXPECT_THAT(overlayable_item.policies, Eq(PolicyFlags::PUBLIC));
search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/boz"));
ASSERT_TRUE(search_result);
@@ -591,16 +599,25 @@
overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(overlayable_item.overlayable->name, Eq("IconPack"));
EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme"));
- EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kSignature
- | OverlayableItem::Policy::kOdm
- | OverlayableItem::Policy::kOem));
+ EXPECT_THAT(overlayable_item.policies, Eq(PolicyFlags::SIGNATURE
+ | PolicyFlags::ODM_PARTITION
+ | PolicyFlags::OEM_PARTITION));
+
+ search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/actor_config"));
+ ASSERT_TRUE(search_result);
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(overlayable_item.overlayable->name, Eq("ActorConfig"));
+ EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme"));
+ EXPECT_THAT(overlayable_item.policies, Eq(PolicyFlags::SIGNATURE
+ | PolicyFlags::ACTOR_SIGNATURE));
search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/biz"));
ASSERT_TRUE(search_result);
ASSERT_TRUE(search_result.value().entry->overlayable_item);
overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(overlayable_item.overlayable->name, Eq("Other"));
- EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
+ EXPECT_THAT(overlayable_item.policies, Eq(PolicyFlags::NONE));
EXPECT_THAT(overlayable_item.comment, Eq("comment"));
search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/fiz"));
diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp
index 0be4ccf..69cf5ee 100644
--- a/tools/aapt2/link/TableMerger_test.cpp
+++ b/tools/aapt2/link/TableMerger_test.cpp
@@ -29,6 +29,8 @@
using ::testing::StrEq;
using ::testing::UnorderedElementsAreArray;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace aapt {
struct TableMergerTest : public ::testing::Test {
@@ -487,8 +489,8 @@
auto overlayable = std::make_shared<Overlayable>("CustomizableResources",
"overlay://customization");
OverlayableItem overlayable_item(overlayable);
- overlayable_item.policies |= OverlayableItem::Policy::kProduct;
- overlayable_item.policies |= OverlayableItem::Policy::kVendor;
+ overlayable_item.policies |= PolicyFlags::PRODUCT_PARTITION;
+ overlayable_item.policies |= PolicyFlags::VENDOR_PARTITION;
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
@@ -516,8 +518,8 @@
OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("CustomizableResources"));
EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://customization"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct
- | OverlayableItem::Policy::kVendor));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::PRODUCT_PARTITION
+ | PolicyFlags::VENDOR_PARTITION));
}
TEST_F(TableMergerTest, SetOverlayableLater) {
@@ -530,8 +532,8 @@
.Build();
OverlayableItem overlayable_item(overlayable);
- overlayable_item.policies |= OverlayableItem::Policy::kPublic;
- overlayable_item.policies |= OverlayableItem::Policy::kSystem;
+ overlayable_item.policies |= PolicyFlags::PUBLIC;
+ overlayable_item.policies |= PolicyFlags::SYSTEM_PARTITION;
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
@@ -552,15 +554,15 @@
OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("CustomizableResources"));
EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://customization"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic
- | OverlayableItem::Policy::kSystem));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::PUBLIC
+ | PolicyFlags::SYSTEM_PARTITION));
}
TEST_F(TableMergerTest, SameResourceDifferentNameFail) {
auto overlayable_first = std::make_shared<Overlayable>("CustomizableResources",
"overlay://customization");
OverlayableItem overlayable_item_first(overlayable_first);
- overlayable_item_first.policies |= OverlayableItem::Policy::kProduct;
+ overlayable_item_first.policies |= PolicyFlags::PRODUCT_PARTITION;
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
@@ -570,7 +572,7 @@
auto overlayable_second = std::make_shared<Overlayable>("ThemeResources",
"overlay://customization");
OverlayableItem overlayable_item_second(overlayable_second);
- overlayable_item_second.policies |= OverlayableItem::Policy::kProduct;
+ overlayable_item_second.policies |= PolicyFlags::PRODUCT_PARTITION;
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
@@ -589,7 +591,7 @@
auto overlayable_first = std::make_shared<Overlayable>("CustomizableResources",
"overlay://customization");
OverlayableItem overlayable_item_first(overlayable_first);
- overlayable_item_first.policies |= OverlayableItem::Policy::kProduct;
+ overlayable_item_first.policies |= PolicyFlags::PRODUCT_PARTITION;
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
@@ -599,7 +601,7 @@
auto overlayable_second = std::make_shared<Overlayable>("CustomizableResources",
"overlay://theme");
OverlayableItem overlayable_item_second(overlayable_second);
- overlayable_item_second.policies |= OverlayableItem::Policy::kProduct;
+ overlayable_item_second.policies |= PolicyFlags::PRODUCT_PARTITION;
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
@@ -618,7 +620,7 @@
auto overlayable_first = std::make_shared<Overlayable>("CustomizableResources",
"overlay://customization");
OverlayableItem overlayable_item_first(overlayable_first);
- overlayable_item_first.policies |= OverlayableItem::Policy::kProduct;
+ overlayable_item_first.policies |= PolicyFlags::PRODUCT_PARTITION;
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
@@ -628,7 +630,7 @@
auto overlayable_second = std::make_shared<Overlayable>("CustomizableResources",
"overlay://customization");
OverlayableItem overlayable_item_second(overlayable_second);
- overlayable_item_second.policies |= OverlayableItem::Policy::kSignature;
+ overlayable_item_second.policies |= PolicyFlags::SIGNATURE;
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
@@ -648,7 +650,7 @@
"overlay://customization");
OverlayableItem overlayable_item_first(overlayable);
- overlayable_item_first.policies |= OverlayableItem::Policy::kProduct;
+ overlayable_item_first.policies |= PolicyFlags::PRODUCT_PARTITION;
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
@@ -656,7 +658,7 @@
.Build();
OverlayableItem overlayable_item_second(overlayable);
- overlayable_item_second.policies |= OverlayableItem::Policy::kProduct;
+ overlayable_item_second.policies |= PolicyFlags::PRODUCT_PARTITION;
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp
index d5031d7..843e820 100644
--- a/tools/stats_log_api_gen/Android.bp
+++ b/tools/stats_log_api_gen/Android.bp
@@ -121,7 +121,6 @@
shared_libs: [
"liblog",
"libcutils",
- "libstatssocket",
],
apex_available: [
"//apex_available:platform",
@@ -129,5 +128,13 @@
"com.android.os.statsd",
"test_com.android.os.statsd",
],
+ target: {
+ android: {
+ shared_libs: ["libstatssocket"],
+ },
+ host: {
+ static_libs: ["libstatssocket"],
+ },
+ },
}
diff --git a/wifi/Android.bp b/wifi/Android.bp
index e253d6d..91174d3 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -47,7 +47,7 @@
// framework-wifi.jar. This is not a good idea, should move WifiNetworkScoreCache
// to a separate package.
"java/android/net/wifi/WifiNetworkScoreCache.java",
- "java/android/net/wifi/WifiOemMigrationHook.java",
+ "java/android/net/wifi/WifiMigration.java",
"java/android/net/wifi/nl80211/*.java",
":libwificond_ipc_aidl",
],
diff --git a/wifi/java/android/net/wifi/IScoreChangeCallback.aidl b/wifi/java/android/net/wifi/IScoreChangeCallback.aidl
index 462a978..d691f41 100644
--- a/wifi/java/android/net/wifi/IScoreChangeCallback.aidl
+++ b/wifi/java/android/net/wifi/IScoreChangeCallback.aidl
@@ -16,16 +16,14 @@
package android.net.wifi;
-import android.net.NetworkScore;
-
/**
- * Interface for Wi-Fi network score callback.
+ * Interface for Wi-Fi score callback.
*
* @hide
*/
oneway interface IScoreChangeCallback
{
- void onScoreChange(int sessionId, in NetworkScore score);
+ void onScoreChange(int sessionId, int score);
void onTriggerUpdateOfWifiUsabilityStats(int sessionId);
}
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 3025caf..30a76e4 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -97,7 +97,7 @@
void setMacRandomizationSettingPasspointEnabled(String fqdn, boolean enable);
- void setMeteredOverridePasspoint(String fqdn, int meteredOverride);
+ void setPasspointMeteredOverride(String fqdn, int meteredOverride);
boolean startScan(String packageName, String featureId);
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index 51927af..9256c57 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -24,8 +24,6 @@
import android.os.Parcel;
import android.os.Parcelable;
-import com.android.internal.annotations.VisibleForTesting;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.ByteBuffer;
@@ -861,19 +859,7 @@
}
}
- /**
- * Construct an empty scan result.
- *
- * Test code has a need to construct a ScanResult in a specific state.
- * (Note that mocking using Mockito does not work if the object needs to be parceled and
- * unparceled.)
- * Export a @SystemApi default constructor to allow tests to construct an empty ScanResult
- * object. The test can then directly set the fields it cares about.
- *
- * @hide
- */
- @SystemApi
- @VisibleForTesting
+ /** Construct an empty scan result. */
public ScanResult() {
}
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index c48e895..0229b84 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -1713,7 +1713,7 @@
* @return network disable reason string, or null if the reason is invalid.
*/
@Nullable
- public static String getNetworkDisableReasonString(
+ public static String getNetworkSelectionDisableReasonString(
@NetworkSelectionDisableReason int reason) {
DisableReasonInfo info = DISABLE_REASON_INFOS.get(reason);
if (info == null) {
@@ -1727,8 +1727,8 @@
* @return current network disable reason in String (for debug purpose)
* @hide
*/
- public String getNetworkDisableReasonString() {
- return getNetworkDisableReasonString(mNetworkSelectionDisableReason);
+ public String getNetworkSelectionDisableReasonString() {
+ return getNetworkSelectionDisableReasonString(mNetworkSelectionDisableReason);
}
/**
@@ -2189,17 +2189,21 @@
sbuf.append(" NetworkSelectionStatus ")
- .append(mNetworkSelectionStatus.getNetworkStatusString() + "\n");
+ .append(mNetworkSelectionStatus.getNetworkStatusString())
+ .append("\n");
if (mNetworkSelectionStatus.getNetworkSelectionDisableReason() > 0) {
sbuf.append(" mNetworkSelectionDisableReason ")
- .append(mNetworkSelectionStatus.getNetworkDisableReasonString() + "\n");
+ .append(mNetworkSelectionStatus.getNetworkSelectionDisableReasonString())
+ .append("\n");
for (int index = NetworkSelectionStatus.DISABLED_NONE;
index < NetworkSelectionStatus.NETWORK_SELECTION_DISABLED_MAX; index++) {
if (mNetworkSelectionStatus.getDisableReasonCounter(index) != 0) {
- sbuf.append(NetworkSelectionStatus.getNetworkDisableReasonString(index)
- + " counter:" + mNetworkSelectionStatus.getDisableReasonCounter(index)
- + "\n");
+ sbuf.append(
+ NetworkSelectionStatus.getNetworkSelectionDisableReasonString(index))
+ .append(" counter:")
+ .append(mNetworkSelectionStatus.getDisableReasonCounter(index))
+ .append("\n");
}
}
}
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index 0c306b4..142854a 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -28,8 +28,6 @@
import android.os.Parcelable;
import android.text.TextUtils;
-import com.android.internal.annotations.VisibleForTesting;
-
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
@@ -380,20 +378,7 @@
}
}
- /**
- * WifiInfo exports an immutable public API.
- * However, test code has a need to construct a WifiInfo in a specific state.
- * (Note that mocking using Mockito does not work if the object needs to be parceled and
- * unparceled.)
- * Export a @SystemApi Builder to allow tests to construct a WifiInfo object
- * in the desired state, without sacrificing WifiInfo's immutability.
- *
- * @hide
- */
- // This builder was not made public to reduce confusion for external developers as there are
- // no legitimate uses for this builder except for testing.
- @SystemApi
- @VisibleForTesting
+ /** Builder for WifiInfo */
public static final class Builder {
private final WifiInfo mWifiInfo = new WifiInfo();
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index b1a4cac..6487e83 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -39,7 +39,6 @@
import android.net.DhcpInfo;
import android.net.MacAddress;
import android.net.Network;
-import android.net.NetworkScore;
import android.net.NetworkStack;
import android.net.wifi.hotspot2.IProvisioningCallback;
import android.net.wifi.hotspot2.OsuProvider;
@@ -1881,9 +1880,11 @@
* NOTE:
* <li> These networks are just a suggestion to the platform. The platform will ultimately
* decide on which network the device connects to. </li>
- * <li> When an app is uninstalled, all its suggested networks are discarded. If the device is
- * currently connected to a suggested network which is being removed then the device will
- * disconnect from that network.</li>
+ * <li> When an app is uninstalled or disabled, all its suggested networks are discarded.
+ * If the device is currently connected to a suggested network which is being removed then the
+ * device will disconnect from that network.</li>
+ * <li> If user reset network settings, all added suggestions will be discarded. Apps can use
+ * {@link #getNetworkSuggestions()} to check if their suggestions are in the device.</li>
* <li> In-place modification of existing suggestions are allowed.
* If the provided suggestions {@link WifiNetworkSuggestion#equals(Object)} any previously
* provided suggestions by the app. Previous suggestions will be updated</li>
@@ -4429,9 +4430,9 @@
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
- public void setMeteredOverridePasspoint(@NonNull String fqdn, int meteredOverride) {
+ public void setPasspointMeteredOverride(@NonNull String fqdn, int meteredOverride) {
try {
- mService.setMeteredOverridePasspoint(fqdn, meteredOverride);
+ mService.setPasspointMeteredOverride(fqdn, meteredOverride);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -5994,11 +5995,10 @@
*
* @param sessionId The ID to indicate current Wi-Fi network connection obtained from
* {@link WifiConnectedNetworkScorer#start(int)}.
- * @param score The {@link android.net.NetworkScore} object representing the
- * characteristics of current Wi-Fi network. Populated by connected network
- * scorer in applications.
+ * @param score The score representing link quality of current Wi-Fi network connection.
+ * Populated by connected network scorer in applications..
*/
- void onScoreChange(int sessionId, @NonNull NetworkScore score);
+ void onScoreChange(int sessionId, int score);
/**
* Called by applications to trigger an update of {@link WifiUsabilityStatsEntry}.
@@ -6024,7 +6024,7 @@
}
@Override
- public void onScoreChange(int sessionId, @NonNull NetworkScore score) {
+ public void onScoreChange(int sessionId, int score) {
try {
mScoreChangeCallback.onScoreChange(sessionId, score);
} catch (RemoteException e) {
diff --git a/wifi/java/android/net/wifi/WifiOemMigrationHook.java b/wifi/java/android/net/wifi/WifiMigration.java
similarity index 93%
rename from wifi/java/android/net/wifi/WifiOemMigrationHook.java
rename to wifi/java/android/net/wifi/WifiMigration.java
index 5301dd0..a3482d7 100755
--- a/wifi/java/android/net/wifi/WifiOemMigrationHook.java
+++ b/wifi/java/android/net/wifi/WifiMigration.java
@@ -34,9 +34,9 @@
* @hide
*/
@SystemApi
-public final class WifiOemMigrationHook {
+public final class WifiMigration {
- private WifiOemMigrationHook() { }
+ private WifiMigration() { }
/**
* Container for all the wifi config data to migrate.
@@ -161,16 +161,16 @@
* Load data from OEM's config store.
* <p>
* Note:
- * <li> OEM's need to implement {@link #loadFromConfigStore()} ()} only if their
- * existing config store format or file locations differs from the vanilla AOSP implementation (
- * which is what the wifi mainline module understands).
+ * <li>OEMs need to implement {@link #loadFromConfigStore()} ()} only if their
+ * existing config store format or file locations differs from the vanilla AOSP implementation.
* </li>
- * <li> The wifi mainline module will invoke {@link #loadFromConfigStore()} method on every
+ * <li>The wifi mainline module will invoke {@link #loadFromConfigStore()} method on every
* bootup, its the responsibility of the OEM implementation to ensure that this method returns
* non-null data only on the first bootup. Once the migration is done, the OEM can safely delete
- * their config store files and then return null on any subsequent reboots. The first & only
- * relevant invocation of {@link #loadFromConfigStore()} occurs when a previously released
- * device upgrades to the wifi mainline module from an OEM implementation of the wifi stack.
+ * their config store files when {@link #removeConfigStore()} is invoked.
+ * <li>The first & only relevant invocation of {@link #loadFromConfigStore()} occurs when a
+ * previously released device upgrades to the wifi mainline module from an OEM implementation
+ * of the wifi stack.
* </li>
*
* @return Instance of {@link ConfigStoreMigrationData} for migrating data, null if no
@@ -178,11 +178,27 @@
*/
@Nullable
public static ConfigStoreMigrationData loadFromConfigStore() {
- // Note: OEM's should add code to parse data from their config store format here!
+ // Note: OEMs should add code to parse data from their config store format here!
return null;
}
/**
+ * Remove OEM's config store.
+ * <p>
+ * Note:
+ * <li>OEMs need to implement {@link #removeConfigStore()} only if their
+ * existing config store format or file locations differs from the vanilla AOSP implementation (
+ * which is what the wifi mainline module understands).
+ * </li>
+ * <li> The wifi mainline module will invoke {@link #removeConfigStore()} after it migrates
+ * all the existing data retrieved from {@link #loadFromConfigStore()}.
+ * </li>
+ */
+ public static void removeConfigStore() {
+ // Note: OEMs should remove their custom config store files here!
+ }
+
+ /**
* Container for all the wifi settings data to migrate.
*/
public static final class SettingsMigrationData implements Parcelable {
diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
index a854a4b..6dbb0bd 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
@@ -753,7 +753,7 @@
boolean isUserInteractionRequired,
boolean isUserAllowedToManuallyConnect,
boolean isInitialAutoJoinEnabled,
- boolean isNetworkUntrusted) {
+ boolean isNetworkUntrusted) {
checkNotNull(networkConfiguration);
this.wifiConfiguration = networkConfiguration;
this.passpointConfiguration = passpointConfiguration;
@@ -858,13 +858,106 @@
}
/**
+ * Get the BSSID, or null if unset.
+ * @see Builder#setBssid(MacAddress)
+ */
+ @Nullable
+ public MacAddress getBssid() {
+ if (wifiConfiguration.BSSID == null) {
+ return null;
+ }
+ return MacAddress.fromString(wifiConfiguration.BSSID);
+ }
+
+ /** @see Builder#setCredentialSharedWithUser(boolean) */
+ public boolean isCredentialSharedWithUser() {
+ return isUserAllowedToManuallyConnect;
+ }
+
+ /** @see Builder#setIsAppInteractionRequired(boolean) */
+ public boolean isAppInteractionRequired() {
+ return isAppInteractionRequired;
+ }
+
+ /** @see Builder#setIsEnhancedOpen(boolean) */
+ public boolean isEnhancedOpen() {
+ return wifiConfiguration.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.OWE);
+ }
+
+ /** @see Builder#setIsHiddenSsid(boolean) */
+ public boolean isHiddenSsid() {
+ return wifiConfiguration.hiddenSSID;
+ }
+
+ /** @see Builder#setIsInitialAutojoinEnabled(boolean) */
+ public boolean isInitialAutojoinEnabled() {
+ return isInitialAutoJoinEnabled;
+ }
+
+ /** @see Builder#setIsMetered(boolean) */
+ public boolean isMetered() {
+ return wifiConfiguration.meteredOverride == WifiConfiguration.METERED_OVERRIDE_METERED;
+ }
+
+ /** @see Builder#setIsUserInteractionRequired(boolean) */
+ public boolean isUserInteractionRequired() {
+ return isUserInteractionRequired;
+ }
+
+ /**
* Get the {@link PasspointConfiguration} associated with this Suggestion, or null if this
* Suggestion is not for a Passpoint network.
- * @hide
*/
- @SystemApi
@Nullable
- public PasspointConfiguration getPasspointConfiguration() {
+ public PasspointConfiguration getPasspointConfig() {
return passpointConfiguration;
}
+
+ /** @see Builder#setPriority(int) */
+ @IntRange(from = 0)
+ public int getPriority() {
+ return wifiConfiguration.priority;
+ }
+
+ /**
+ * Return the SSID of the network, or null if this is a Passpoint network.
+ * @see Builder#setSsid(String)
+ */
+ @Nullable
+ public String getSsid() {
+ if (wifiConfiguration.SSID == null) {
+ return null;
+ }
+ return WifiInfo.sanitizeSsid(wifiConfiguration.SSID);
+ }
+
+ /** @see Builder#setUntrusted(boolean) */
+ public boolean isUntrusted() {
+ return isNetworkUntrusted;
+ }
+
+ /**
+ * Get the WifiEnterpriseConfig, or null if unset.
+ * @see Builder#setWapiEnterpriseConfig(WifiEnterpriseConfig)
+ * @see Builder#setWpa2EnterpriseConfig(WifiEnterpriseConfig)
+ * @see Builder#setWpa3EnterpriseConfig(WifiEnterpriseConfig)
+ */
+ @Nullable
+ public WifiEnterpriseConfig getEnterpriseConfig() {
+ return wifiConfiguration.enterpriseConfig;
+ }
+
+ /**
+ * Get the passphrase, or null if unset.
+ * @see Builder#setWapiPassphrase(String)
+ * @see Builder#setWpa2Passphrase(String)
+ * @see Builder#setWpa3Passphrase(String)
+ */
+ @Nullable
+ public String getPassphrase() {
+ if (wifiConfiguration.preSharedKey == null) {
+ return null;
+ }
+ return WifiInfo.removeDoubleQuotes(wifiConfiguration.preSharedKey);
+ }
}
diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
index 4507cc2..d1d1780 100644
--- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
+++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
@@ -722,7 +722,7 @@
if (mSubscriptionUpdate != null && !mSubscriptionUpdate.validate()) {
return false;
}
- return validateForCommonR1andR2(true);
+ return validateForCommonR1andR2();
}
/**
@@ -741,17 +741,17 @@
if (mSubscriptionUpdate == null || !mSubscriptionUpdate.validate()) {
return false;
}
- return validateForCommonR1andR2(false);
+ return validateForCommonR1andR2();
}
- private boolean validateForCommonR1andR2(boolean isR1) {
+ private boolean validateForCommonR1andR2() {
// Required: PerProviderSubscription/<X+>/HomeSP
if (mHomeSp == null || !mHomeSp.validate()) {
return false;
}
// Required: PerProviderSubscription/<X+>/Credential
- if (mCredential == null || !mCredential.validate(isR1)) {
+ if (mCredential == null || !mCredential.validate()) {
return false;
}
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
index 9c01d36..65e8b3d 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
@@ -1081,11 +1081,10 @@
/**
* Validate the configuration data.
*
- * @param isR1 {@code true} if the configuration is for R1
* @return true on success or false on failure
* @hide
*/
- public boolean validate(boolean isR1) {
+ public boolean validate() {
if (TextUtils.isEmpty(mRealm)) {
Log.d(TAG, "Missing realm");
return false;
@@ -1098,11 +1097,11 @@
// Verify the credential.
if (mUserCredential != null) {
- if (!verifyUserCredential(isR1)) {
+ if (!verifyUserCredential()) {
return false;
}
} else if (mCertCredential != null) {
- if (!verifyCertCredential(isR1)) {
+ if (!verifyCertCredential()) {
return false;
}
} else if (mSimCredential != null) {
@@ -1143,11 +1142,11 @@
/**
* Verify user credential.
+ * If no CA certificate is provided, then the system uses the CAs in the trust store.
*
- * @param isR1 {@code true} if credential is for R1
* @return true if user credential is valid, false otherwise.
*/
- private boolean verifyUserCredential(boolean isR1) {
+ private boolean verifyUserCredential() {
if (mUserCredential == null) {
Log.d(TAG, "Missing user credential");
return false;
@@ -1160,24 +1159,17 @@
return false;
}
- // CA certificate is required for R1 Passpoint profile.
- // For R2, it is downloaded using cert URL provided in PPS MO after validation completes.
- if (isR1 && mCaCertificates == null) {
- Log.d(TAG, "Missing CA Certificate for user credential");
- return false;
- }
-
return true;
}
/**
* Verify certificate credential, which is used for EAP-TLS. This will verify
* that the necessary client key and certificates are provided.
+ * If no CA certificate is provided, then the system uses the CAs in the trust store.
*
- * @param isR1 {@code true} if credential is for R1
* @return true if certificate credential is valid, false otherwise.
*/
- private boolean verifyCertCredential(boolean isR1) {
+ private boolean verifyCertCredential() {
if (mCertCredential == null) {
Log.d(TAG, "Missing certificate credential");
return false;
@@ -1191,13 +1183,6 @@
return false;
}
- // Verify required key and certificates for certificate credential.
- // CA certificate is required for R1 Passpoint profile.
- // For R2, it is downloaded using cert URL provided in PPS MO after validation completes.
- if (isR1 && mCaCertificates == null) {
- Log.d(TAG, "Missing CA Certificate for certificate credential");
- return false;
- }
if (mClientPrivateKey == null) {
Log.d(TAG, "Missing client private key for certificate credential");
return false;
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 234d929..76ac837 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -1803,10 +1803,10 @@
* {@link WifiManager#setMacRandomizationSettingPasspointEnabled(String, boolean)}
*/
@Test
- public void testSetMeteredOverridePasspoint() throws Exception {
+ public void testSetPasspointMeteredOverride() throws Exception {
final String fqdn = "FullyQualifiedDomainName";
- mWifiManager.setMeteredOverridePasspoint(fqdn, METERED_OVERRIDE_METERED);
- verify(mWifiService).setMeteredOverridePasspoint(fqdn, METERED_OVERRIDE_METERED);
+ mWifiManager.setPasspointMeteredOverride(fqdn, METERED_OVERRIDE_METERED);
+ verify(mWifiService).setPasspointMeteredOverride(fqdn, METERED_OVERRIDE_METERED);
}
/**
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
index 0a3e989..c682582 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
@@ -158,7 +158,7 @@
}
/**
- * Verify parcel read/write for an user credential.
+ * Verify parcel read/write for a user credential.
*
* @throws Exception
*/
@@ -176,14 +176,14 @@
Credential cred = createCredentialWithUserCredential();
// For R1 validation
- assertTrue(cred.validate(true));
+ assertTrue(cred.validate());
// For R2 validation
- assertTrue(cred.validate(false));
+ assertTrue(cred.validate());
}
/**
- * Verify that an user credential without CA Certificate is invalid.
+ * Verify that a user credential without CA Certificate is valid.
*
* @throws Exception
*/
@@ -192,15 +192,12 @@
Credential cred = createCredentialWithUserCredential();
cred.setCaCertificate(null);
- // For R1 validation
- assertFalse(cred.validate(true));
-
- // For R2 validation
- assertTrue(cred.validate(false));
+ // Accept a configuration with no CA certificate, the system will use the default cert store
+ assertTrue(cred.validate());
}
/**
- * Verify that an user credential with EAP type other than EAP-TTLS is invalid.
+ * Verify that a user credential with EAP type other than EAP-TTLS is invalid.
*
* @throws Exception
*/
@@ -210,15 +207,15 @@
cred.getUserCredential().setEapType(EAPConstants.EAP_TLS);
// For R1 validation
- assertFalse(cred.validate(true));
+ assertFalse(cred.validate());
// For R2 validation
- assertFalse(cred.validate(false));
+ assertFalse(cred.validate());
}
/**
- * Verify that an user credential without realm is invalid.
+ * Verify that a user credential without realm is invalid.
*
* @throws Exception
*/
@@ -228,14 +225,14 @@
cred.setRealm(null);
// For R1 validation
- assertFalse(cred.validate(true));
+ assertFalse(cred.validate());
// For R2 validation
- assertFalse(cred.validate(false));
+ assertFalse(cred.validate());
}
/**
- * Verify that an user credential without username is invalid.
+ * Verify that a user credential without username is invalid.
*
* @throws Exception
*/
@@ -245,14 +242,14 @@
cred.getUserCredential().setUsername(null);
// For R1 validation
- assertFalse(cred.validate(true));
+ assertFalse(cred.validate());
// For R2 validation
- assertFalse(cred.validate(false));
+ assertFalse(cred.validate());
}
/**
- * Verify that an user credential without password is invalid.
+ * Verify that a user credential without password is invalid.
*
* @throws Exception
*/
@@ -262,14 +259,14 @@
cred.getUserCredential().setPassword(null);
// For R1 validation
- assertFalse(cred.validate(true));
+ assertFalse(cred.validate());
// For R2 validation
- assertFalse(cred.validate(false));
+ assertFalse(cred.validate());
}
/**
- * Verify that an user credential without auth methoh (non-EAP inner method) is invalid.
+ * Verify that a user credential without auth methoh (non-EAP inner method) is invalid.
*
* @throws Exception
*/
@@ -279,10 +276,10 @@
cred.getUserCredential().setNonEapInnerMethod(null);
// For R1 validation
- assertFalse(cred.validate(true));
+ assertFalse(cred.validate());
// For R2 validation
- assertFalse(cred.validate(false));
+ assertFalse(cred.validate());
}
/**
@@ -297,10 +294,10 @@
Credential cred = createCredentialWithCertificateCredential();
// For R1 validation
- assertTrue(cred.validate(true));
+ assertTrue(cred.validate());
// For R2 validation
- assertTrue(cred.validate(true));
+ assertTrue(cred.validate());
}
/**
@@ -313,11 +310,8 @@
Credential cred = createCredentialWithCertificateCredential();
cred.setCaCertificate(null);
- // For R1 validation
- assertFalse(cred.validate(true));
-
- // For R2 validation
- assertTrue(cred.validate(false));
+ // Accept a configuration with no CA certificate, the system will use the default cert store
+ assertTrue(cred.validate());
}
/**
@@ -331,10 +325,10 @@
cred.setClientCertificateChain(null);
// For R1 validation
- assertFalse(cred.validate(true));
+ assertFalse(cred.validate());
// For R2 validation
- assertFalse(cred.validate(false));
+ assertFalse(cred.validate());
}
/**
@@ -348,10 +342,10 @@
cred.setClientPrivateKey(null);
// For R1 validation
- assertFalse(cred.validate(true));
+ assertFalse(cred.validate());
// For R2 validation
- assertFalse(cred.validate(false));
+ assertFalse(cred.validate());
}
/**
@@ -366,10 +360,10 @@
cred.getCertCredential().setCertSha256Fingerprint(new byte[32]);
// For R1 validation
- assertFalse(cred.validate(true));
+ assertFalse(cred.validate());
// For R2 validation
- assertFalse(cred.validate(false));
+ assertFalse(cred.validate());
}
/**
@@ -382,10 +376,10 @@
Credential cred = createCredentialWithSimCredential();
// For R1 validation
- assertTrue(cred.validate(true));
+ assertTrue(cred.validate());
// For R2 validation
- assertTrue(cred.validate(false));
+ assertTrue(cred.validate());
}
/**
@@ -399,10 +393,10 @@
cred.getSimCredential().setEapType(EAPConstants.EAP_AKA);
// For R1 validation
- assertTrue(cred.validate(true));
+ assertTrue(cred.validate());
// For R2 validation
- assertTrue(cred.validate(false));
+ assertTrue(cred.validate());
}
/**
@@ -416,10 +410,10 @@
cred.getSimCredential().setEapType(EAPConstants.EAP_AKA_PRIME);
// For R1 validation
- assertTrue(cred.validate(true));
+ assertTrue(cred.validate());
// For R2 validation
- assertTrue(cred.validate(false));
+ assertTrue(cred.validate());
}
/**
@@ -433,10 +427,10 @@
cred.getSimCredential().setImsi(null);
// For R1 validation
- assertFalse(cred.validate(true));
+ assertFalse(cred.validate());
// For R2 validation
- assertFalse(cred.validate(false));
+ assertFalse(cred.validate());
}
/**
@@ -450,10 +444,10 @@
cred.getSimCredential().setImsi("dummy");
// For R1 validation
- assertFalse(cred.validate(true));
+ assertFalse(cred.validate());
// For R2 validation
- assertFalse(cred.validate(false));
+ assertFalse(cred.validate());
}
/**
@@ -467,14 +461,14 @@
cred.getSimCredential().setEapType(EAPConstants.EAP_TLS);
// For R1 validation
- assertFalse(cred.validate(true));
+ assertFalse(cred.validate());
// For R2 validation
- assertFalse(cred.validate(false));
+ assertFalse(cred.validate());
}
/**
- * Verify that a credential contained both an user and a SIM credential is invalid.
+ * Verify that a credential contained both a user and a SIM credential is invalid.
*
* @throws Exception
*/
@@ -488,10 +482,10 @@
cred.setSimCredential(simCredential);
// For R1 validation
- assertFalse(cred.validate(true));
+ assertFalse(cred.validate());
// For R2 validation
- assertFalse(cred.validate(false));
+ assertFalse(cred.validate());
}
/**