Merge "Close the GZIPOutputStream to release the deflater" into sc-dev
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreIdleJobService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreIdleJobService.java
index 999860f..e65abcf 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreIdleJobService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreIdleJobService.java
@@ -48,7 +48,8 @@
@Override
public boolean onStopJob(final JobParameters params) {
Slog.d(TAG, "Idle maintenance job is stopped; id=" + params.getJobId()
- + ", reason=" + JobParameters.getReasonCodeDescription(params.getStopReason()));
+ + ", reason="
+ + JobParameters.getLegacyReasonCodeDescription(params.getLegacyStopReason()));
return false;
}
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
index 0d3e001..60f6475 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
@@ -16,10 +16,14 @@
package android.app.job;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.usage.UsageStatsManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ClipData;
+import android.content.pm.PackageManager;
import android.net.Network;
import android.net.NetworkRequest;
import android.net.Uri;
@@ -30,6 +34,9 @@
import android.os.PersistableBundle;
import android.os.RemoteException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Contains the parameters used to configure/identify your job. You do not create this object
* yourself, instead it is handed in to your application by the System.
@@ -82,7 +89,7 @@
*/
// TODO(142420609): make it @SystemApi for mainline
@NonNull
- public static String getReasonCodeDescription(int reasonCode) {
+ public static String getLegacyReasonCodeDescription(int reasonCode) {
switch (reasonCode) {
case REASON_CANCELED: return "canceled";
case REASON_CONSTRAINTS_NOT_SATISFIED: return "constraints";
@@ -96,12 +103,119 @@
}
/** @hide */
- // @SystemApi TODO make it a system api for mainline
+ // TODO: move current users of legacy reasons to new public reasons
@NonNull
public static int[] getJobStopReasonCodes() {
return JOB_STOP_REASON_CODES;
}
+ /**
+ * There is no reason the job is stopped. This is the value returned from the JobParameters
+ * object passed to {@link JobService#onStartJob(JobParameters)}.
+ */
+ public static final int STOP_REASON_UNDEFINED = 0;
+ /**
+ * The job was cancelled directly by the app, either by calling
+ * {@link JobScheduler#cancel(int)}, {@link JobScheduler#cancelAll()}, or by scheduling a
+ * new job with the same job ID.
+ */
+ public static final int STOP_REASON_CANCELLED_BY_APP = 1;
+ /** The job was stopped to run a higher priority job of the app. */
+ public static final int STOP_REASON_PREEMPT = 2;
+ /**
+ * The job used up its maximum execution time and timed out. Each individual job has a maximum
+ * execution time limit, regardless of how much total quota the app has. See the note on
+ * {@link JobScheduler} for the execution time limits.
+ */
+ public static final int STOP_REASON_TIMEOUT = 3;
+ /**
+ * The device state (eg. Doze, battery saver, memory usage, etc) requires JobScheduler stop this
+ * job.
+ */
+ public static final int STOP_REASON_DEVICE_STATE = 4;
+ /**
+ * The requested battery-not-low constraint is no longer satisfied.
+ *
+ * @see JobInfo.Builder#setRequiresBatteryNotLow(boolean)
+ */
+ public static final int STOP_REASON_CONSTRAINT_BATTERY_NOT_LOW = 5;
+ /**
+ * The requested charging constraint is no longer satisfied.
+ *
+ * @see JobInfo.Builder#setRequiresCharging(boolean)
+ */
+ public static final int STOP_REASON_CONSTRAINT_CHARGING = 6;
+ /**
+ * The requested connectivity constraint is no longer satisfied.
+ *
+ * @see JobInfo.Builder#setRequiredNetwork(NetworkRequest)
+ * @see JobInfo.Builder#setRequiredNetworkType(int)
+ */
+ public static final int STOP_REASON_CONSTRAINT_CONNECTIVITY = 7;
+ /**
+ * The requested idle constraint is no longer satisfied.
+ *
+ * @see JobInfo.Builder#setRequiresDeviceIdle(boolean)
+ */
+ public static final int STOP_REASON_CONSTRAINT_DEVICE_IDLE = 8;
+ /**
+ * The requested storage-not-low constraint is no longer satisfied.
+ *
+ * @see JobInfo.Builder#setRequiresStorageNotLow(boolean)
+ */
+ public static final int STOP_REASON_CONSTRAINT_STORAGE_NOT_LOW = 9;
+ /**
+ * The app has consumed all of its current quota. Each app is assigned a quota of how much
+ * it can run jobs within a certain time frame. The quota is informed, in part, by app standby
+ * buckets. Once an app has used up all of its quota, it won't be able to start jobs until
+ * quota is replenished, is changed, or is temporarily not applied.
+ *
+ * @see UsageStatsManager#getAppStandbyBucket()
+ */
+ public static final int STOP_REASON_QUOTA = 10;
+ /**
+ * The app is restricted from running in the background.
+ *
+ * @see ActivityManager#isBackgroundRestricted()
+ * @see PackageManager#isInstantApp()
+ */
+ public static final int STOP_REASON_BACKGROUND_RESTRICTION = 11;
+ /**
+ * The current standby bucket requires that the job stop now.
+ *
+ * @see UsageStatsManager#STANDBY_BUCKET_RESTRICTED
+ */
+ public static final int STOP_REASON_APP_STANDBY = 12;
+ /**
+ * The user stopped the job. This can happen either through force-stop, or via adb shell
+ * commands.
+ */
+ public static final int STOP_REASON_USER = 13;
+ /** The system is doing some processing that requires stopping this job. */
+ public static final int STOP_REASON_SYSTEM_PROCESSING = 14;
+
+ /** @hide */
+ @IntDef(prefix = {"STOP_REASON_"}, value = {
+ STOP_REASON_UNDEFINED,
+ STOP_REASON_CANCELLED_BY_APP,
+ STOP_REASON_PREEMPT,
+ STOP_REASON_TIMEOUT,
+ STOP_REASON_DEVICE_STATE,
+ STOP_REASON_CONSTRAINT_BATTERY_NOT_LOW,
+ STOP_REASON_CONSTRAINT_CHARGING,
+ STOP_REASON_CONSTRAINT_CONNECTIVITY,
+ STOP_REASON_CONSTRAINT_DEVICE_IDLE,
+ STOP_REASON_CONSTRAINT_STORAGE_NOT_LOW,
+ STOP_REASON_QUOTA,
+ STOP_REASON_BACKGROUND_RESTRICTION,
+ STOP_REASON_APP_STANDBY,
+ STOP_REASON_USER,
+ STOP_REASON_SYSTEM_PROCESSING,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface StopReason {
+ }
+
@UnsupportedAppUsage
private final int jobId;
private final PersistableBundle extras;
@@ -116,7 +230,8 @@
private final String[] mTriggeredContentAuthorities;
private final Network network;
- private int stopReason; // Default value of stopReason is REASON_CANCELED
+ private int mStopReason = STOP_REASON_UNDEFINED;
+ private int mLegacyStopReason; // Default value of stopReason is REASON_CANCELED
private String debugStopReason; // Human readable stop reason for debugging.
/** @hide */
@@ -145,15 +260,23 @@
}
/**
- * Reason onStopJob() was called on this job.
- * @hide
+ * @return The reason {@link JobService#onStopJob(JobParameters)} was called on this job. Will
+ * be {@link #STOP_REASON_UNDEFINED} if {@link JobService#onStopJob(JobParameters)} has not
+ * yet been called.
*/
+ @StopReason
public int getStopReason() {
- return stopReason;
+ return mStopReason;
+ }
+
+ /** @hide */
+ public int getLegacyStopReason() {
+ return mLegacyStopReason;
}
/**
* Reason onStopJob() was called on this job.
+ *
* @hide
*/
public String getDebugStopReason() {
@@ -368,13 +491,16 @@
} else {
network = null;
}
- stopReason = in.readInt();
+ mStopReason = in.readInt();
+ mLegacyStopReason = in.readInt();
debugStopReason = in.readString();
}
/** @hide */
- public void setStopReason(int reason, String debugStopReason) {
- stopReason = reason;
+ public void setStopReason(@StopReason int reason, int legacyStopReason,
+ String debugStopReason) {
+ mStopReason = reason;
+ mLegacyStopReason = legacyStopReason;
this.debugStopReason = debugStopReason;
}
@@ -406,7 +532,8 @@
} else {
dest.writeInt(0);
}
- dest.writeInt(stopReason);
+ dest.writeInt(mStopReason);
+ dest.writeInt(mLegacyStopReason);
dest.writeString(debugStopReason);
}
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobService.java b/apex/jobscheduler/framework/java/android/app/job/JobService.java
index 0f3d299..fa7a2d3 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobService.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobService.java
@@ -139,19 +139,23 @@
* Once this method is called, you no longer need to call
* {@link #jobFinished(JobParameters, boolean)}.
*
- * <p>This will happen if the requirements specified at schedule time are no longer met. For
+ * <p>This may happen if the requirements specified at schedule time are no longer met. For
* example you may have requested WiFi with
* {@link android.app.job.JobInfo.Builder#setRequiredNetworkType(int)}, yet while your
* job was executing the user toggled WiFi. Another example is if you had specified
- * {@link android.app.job.JobInfo.Builder#setRequiresDeviceIdle(boolean)}, and the phone left its
- * idle maintenance window. You are solely responsible for the behavior of your application
- * upon receipt of this message; your app will likely start to misbehave if you ignore it.
+ * {@link android.app.job.JobInfo.Builder#setRequiresDeviceIdle(boolean)}, and the phone left
+ * its idle maintenance window. There are many other reasons a job can be stopped early besides
+ * constraints no longer being satisfied. {@link JobParameters#getStopReason()} will return the
+ * reason this method was called. You are solely responsible for the behavior of your
+ * application upon receipt of this message; your app will likely start to misbehave if you
+ * ignore it.
* <p>
* Once this method returns (or times out), the system releases the wakelock that it is holding
* on behalf of the job.</p>
*
- * @param params The parameters identifying this job, as supplied to
- * the job in the {@link #onStartJob(JobParameters)} callback.
+ * @param params The parameters identifying this job, similar to what was supplied to the job in
+ * the {@link #onStartJob(JobParameters)} callback, but with the stop reason
+ * included.
* @return {@code true} to indicate to the JobManager whether you'd like to reschedule
* this job based on the retry criteria provided at job creation-time; or {@code false}
* to end the job entirely. Regardless of the value returned, your job must stop executing.
diff --git a/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java b/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java
index 7833a03..6ae91a0 100644
--- a/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.app.job.JobInfo;
+import android.app.job.JobParameters;
import android.util.proto.ProtoOutputStream;
import java.util.List;
@@ -36,7 +37,7 @@
/**
* Cancel the jobs for a given uid (e.g. when app data is cleared)
*/
- void cancelJobsForUid(int uid, String reason);
+ void cancelJobsForUid(int uid, @JobParameters.StopReason int reason, String debugReason);
/**
* These are for activity manager to communicate to use what is currently performing backups.
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index b958c3f..325be1b 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -266,6 +266,8 @@
String[] mRecycledPreemptReasonForContext = new String[MAX_JOB_CONTEXTS_COUNT];
+ int[] mRecycledPreemptReasonCodeForContext = new int[MAX_JOB_CONTEXTS_COUNT];
+
String[] mRecycledShouldStopJobReason = new String[MAX_JOB_CONTEXTS_COUNT];
private final ArraySet<JobStatus> mRunningJobs = new ArraySet<>();
@@ -505,6 +507,7 @@
int[] preferredUidForContext = mRecycledPreferredUidForContext;
int[] workTypeForContext = mRecycledWorkTypeForContext;
String[] preemptReasonForContext = mRecycledPreemptReasonForContext;
+ int[] preemptReasonCodeForContext = mRecycledPreemptReasonCodeForContext;
String[] shouldStopJobReason = mRecycledShouldStopJobReason;
updateCounterConfigLocked();
@@ -528,6 +531,7 @@
slotChanged[i] = false;
preferredUidForContext[i] = js.getPreferredUid();
preemptReasonForContext[i] = null;
+ preemptReasonCodeForContext[i] = JobParameters.STOP_REASON_UNDEFINED;
shouldStopJobReason[i] = shouldStopRunningJobLocked(js);
}
if (DEBUG) {
@@ -551,6 +555,7 @@
int allWorkTypes = getJobWorkTypes(nextPending);
int workType = mWorkCountTracker.canJobStart(allWorkTypes);
boolean startingJob = false;
+ int preemptReasonCode = JobParameters.STOP_REASON_UNDEFINED;
String preemptReason = null;
// TODO(141645789): rewrite this to look at empty contexts first so we don't
// unnecessarily preempt
@@ -582,6 +587,7 @@
// assign the new job to this context since we'll reassign when the
// preempted job finally stops.
preemptReason = reason;
+ preemptReasonCode = JobParameters.STOP_REASON_DEVICE_STATE;
}
continue;
}
@@ -597,6 +603,7 @@
minPriorityForPreemption = jobPriority;
selectedContextId = j;
preemptReason = "higher priority job found";
+ preemptReasonCode = JobParameters.STOP_REASON_PREEMPT;
// In this case, we're just going to preempt a low priority job, we're not
// actually starting a job, so don't set startingJob.
}
@@ -604,6 +611,7 @@
if (selectedContextId != -1) {
contextIdToJobMap[selectedContextId] = nextPending;
slotChanged[selectedContextId] = true;
+ preemptReasonCodeForContext[selectedContextId] = preemptReasonCode;
preemptReasonForContext[selectedContextId] = preemptReason;
}
if (startingJob) {
@@ -631,8 +639,13 @@
}
// preferredUid will be set to uid of currently running job.
activeServices.get(i).cancelExecutingJobLocked(
+ preemptReasonCodeForContext[i],
JobParameters.REASON_PREEMPT, preemptReasonForContext[i]);
- preservePreferredUid = true;
+ // Only preserve the UID if we're preempting for the same UID. If we're stopping
+ // the job because something is pending (eg. EJs), then we shouldn't preserve
+ // the UID.
+ preservePreferredUid =
+ preemptReasonCodeForContext[i] == JobParameters.STOP_REASON_PREEMPT;
} else {
final JobStatus pendingJob = contextIdToJobMap[i];
if (DEBUG) {
@@ -657,7 +670,8 @@
final JobStatus jobStatus = jsc.getRunningJobLocked();
if (jobStatus != null && !jsc.isWithinExecutionGuaranteeTime()) {
- jsc.cancelExecutingJobLocked(JobParameters.REASON_TIMEOUT, debugReason);
+ jsc.cancelExecutingJobLocked(JobParameters.STOP_REASON_DEVICE_STATE,
+ JobParameters.REASON_TIMEOUT, debugReason);
}
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java b/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java
index 6ffac91..02f9129 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java
@@ -374,7 +374,7 @@
pw.print(pe.stopReasons.valueAt(k));
pw.print("x ");
pw.print(JobParameters
- .getReasonCodeDescription(pe.stopReasons.keyAt(k)));
+ .getLegacyReasonCodeDescription(pe.stopReasons.keyAt(k)));
}
pw.println();
}
@@ -621,7 +621,7 @@
if (reason != null) {
pw.print(mEventReasons[index]);
} else {
- pw.print(JobParameters.getReasonCodeDescription(
+ pw.print(JobParameters.getLegacyReasonCodeDescription(
(mEventCmds[index] & EVENT_STOP_REASON_MASK)
>> EVENT_STOP_REASON_SHIFT));
}
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 2b08ba5..a041f8c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -746,8 +746,11 @@
Slog.d(TAG, "Removing jobs for package " + pkgName
+ " in user " + userId);
}
+ // By the time we get here, the process should have already
+ // been stopped, so the app wouldn't get the stop reason,
+ // so just put USER instead of UNINSTALL or DISABLED.
cancelJobsForPackageAndUid(pkgName, pkgUid,
- "app disabled");
+ JobParameters.STOP_REASON_USER, "app disabled");
}
} catch (RemoteException|IllegalArgumentException e) {
/*
@@ -785,7 +788,11 @@
if (DEBUG) {
Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
}
- cancelJobsForPackageAndUid(pkgName, uidRemoved, "app uninstalled");
+ // By the time we get here, the process should have already
+ // been stopped, so the app wouldn't get the stop reason,
+ // so just put USER instead of UNINSTALL or DISABLED.
+ cancelJobsForPackageAndUid(pkgName, uidRemoved,
+ JobParameters.STOP_REASON_USER, "app uninstalled");
synchronized (mLock) {
for (int c = 0; c < mControllers.size(); ++c) {
mControllers.get(c).onAppRemovedLocked(pkgName, pkgUid);
@@ -837,7 +844,8 @@
if (DEBUG) {
Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid);
}
- cancelJobsForPackageAndUid(pkgName, pkgUid, "app force stopped");
+ cancelJobsForPackageAndUid(pkgName, pkgUid,
+ JobParameters.STOP_REASON_USER, "app force stopped");
}
}
}
@@ -924,8 +932,7 @@
final String servicePkg = job.getService().getPackageName();
if (job.isPersisted() && (packageName == null || packageName.equals(servicePkg))) {
// Only limit schedule calls for persisted jobs scheduled by the app itself.
- final String pkg =
- packageName == null ? job.getService().getPackageName() : packageName;
+ final String pkg = packageName == null ? servicePkg : packageName;
if (!mQuotaTracker.isWithinQuota(userId, pkg, QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG)) {
if (mQuotaTracker.isWithinQuota(userId, pkg, QUOTA_TRACKER_SCHEDULE_LOGGED)) {
// Don't log too frequently
@@ -972,14 +979,10 @@
mQuotaTracker.noteEvent(userId, pkg, QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG);
}
- try {
- if (ActivityManager.getService().isAppStartModeDisabled(uId,
- job.getService().getPackageName())) {
- Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
- + " -- package not allowed to start");
- return JobScheduler.RESULT_FAILURE;
- }
- } catch (RemoteException e) {
+ if (mActivityManagerInternal.isAppStartModeDisabled(uId, servicePkg)) {
+ Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
+ + " -- package not allowed to start");
+ return JobScheduler.RESULT_FAILURE;
}
synchronized (mLock) {
@@ -1029,7 +1032,8 @@
if (toCancel != null) {
// Implicitly replaces the existing job record with the new instance
- cancelJobImplLocked(toCancel, jobStatus, "job rescheduled by app");
+ cancelJobImplLocked(toCancel, jobStatus, JobParameters.STOP_REASON_CANCELLED_BY_APP,
+ "job rescheduled by app");
} else {
startTrackingJobLocked(jobStatus, null);
}
@@ -1105,7 +1109,10 @@
final List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle);
for (int i=0; i<jobsForUser.size(); i++) {
JobStatus toRemove = jobsForUser.get(i);
- cancelJobImplLocked(toRemove, null, "user removed");
+ // By the time we get here, the process should have already been stopped, so the
+ // app wouldn't get the stop reason, so just put USER instead of UNINSTALL.
+ cancelJobImplLocked(toRemove, null, JobParameters.STOP_REASON_USER,
+ "user removed");
}
}
}
@@ -1117,7 +1124,8 @@
}
}
- void cancelJobsForPackageAndUid(String pkgName, int uid, String reason) {
+ void cancelJobsForPackageAndUid(String pkgName, int uid, @JobParameters.StopReason int reason,
+ String debugReason) {
if ("android".equals(pkgName)) {
Slog.wtfStack(TAG, "Can't cancel all jobs for system package");
return;
@@ -1127,7 +1135,7 @@
for (int i = jobsForUid.size() - 1; i >= 0; i--) {
final JobStatus job = jobsForUid.get(i);
if (job.getSourcePackageName().equals(pkgName)) {
- cancelJobImplLocked(job, null, reason);
+ cancelJobImplLocked(job, null, reason, debugReason);
}
}
}
@@ -1137,10 +1145,11 @@
* Entry point from client to cancel all jobs originating from their uid.
* This will remove the job from the master list, and cancel the job if it was staged for
* execution or being executed.
- * @param uid Uid to check against for removal of a job.
*
+ * @param uid Uid to check against for removal of a job.
*/
- public boolean cancelJobsForUid(int uid, String reason) {
+ public boolean cancelJobsForUid(int uid, @JobParameters.StopReason int reason,
+ String debugReason) {
if (uid == Process.SYSTEM_UID) {
Slog.wtfStack(TAG, "Can't cancel all jobs for system uid");
return false;
@@ -1151,7 +1160,7 @@
final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
for (int i=0; i<jobsForUid.size(); i++) {
JobStatus toRemove = jobsForUid.get(i);
- cancelJobImplLocked(toRemove, null, reason);
+ cancelJobImplLocked(toRemove, null, reason, debugReason);
jobsCanceled = true;
}
}
@@ -1162,15 +1171,17 @@
* Entry point from client to cancel the job corresponding to the jobId provided.
* This will remove the job from the master list, and cancel the job if it was staged for
* execution or being executed.
- * @param uid Uid of the calling client.
+ *
+ * @param uid Uid of the calling client.
* @param jobId Id of the job, provided at schedule-time.
*/
- public boolean cancelJob(int uid, int jobId, int callingUid) {
+ private boolean cancelJob(int uid, int jobId, int callingUid,
+ @JobParameters.StopReason int reason) {
JobStatus toCancel;
synchronized (mLock) {
toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
if (toCancel != null) {
- cancelJobImplLocked(toCancel, null,
+ cancelJobImplLocked(toCancel, null, reason,
"cancel() called by app, callingUid=" + callingUid
+ " uid=" + uid + " jobId=" + jobId);
}
@@ -1184,7 +1195,8 @@
* {@code incomingJob} is non-null, it replaces {@code cancelled} in the store of
* currently scheduled jobs.
*/
- private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob, String reason) {
+ private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob,
+ @JobParameters.StopReason int reason, String debugReason) {
if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
cancelled.unprepareLocked();
stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */);
@@ -1193,7 +1205,8 @@
mJobPackageTracker.noteNonpending(cancelled);
}
// Cancel if running.
- stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED, reason);
+ stopJobOnServiceContextLocked(cancelled, reason, JobParameters.REASON_CANCELED,
+ debugReason);
// If this is a replacement, bring in the new version of the job
if (incomingJob != null) {
if (DEBUG) Slog.i(TAG, "Tracking replacement job " + incomingJob.toShortString());
@@ -1232,7 +1245,8 @@
JobServiceContext jsc = mActiveServices.get(i);
final JobStatus executing = jsc.getRunningJobLocked();
if (executing != null && !executing.canRunInDoze()) {
- jsc.cancelExecutingJobLocked(JobParameters.REASON_DEVICE_IDLE,
+ jsc.cancelExecutingJobLocked(JobParameters.STOP_REASON_DEVICE_STATE,
+ JobParameters.REASON_DEVICE_IDLE,
"cancelled due to doze");
}
}
@@ -1430,7 +1444,8 @@
if (DEBUG) {
Slog.v(TAG, " replacing " + oldJob + " with " + newJob);
}
- cancelJobImplLocked(oldJob, newJob, "deferred rtc calculation");
+ cancelJobImplLocked(oldJob, newJob, JobParameters.STOP_REASON_SYSTEM_PROCESSING,
+ "deferred rtc calculation");
}
}
};
@@ -1550,12 +1565,13 @@
return removed;
}
- private boolean stopJobOnServiceContextLocked(JobStatus job, int reason, String debugReason) {
+ private boolean stopJobOnServiceContextLocked(JobStatus job,
+ @JobParameters.StopReason int reason, int legacyReason, String debugReason) {
for (int i=0; i<mActiveServices.size(); i++) {
JobServiceContext jsc = mActiveServices.get(i);
final JobStatus executing = jsc.getRunningJobLocked();
if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
- jsc.cancelExecutingJobLocked(reason, debugReason);
+ jsc.cancelExecutingJobLocked(reason, legacyReason, debugReason);
return true;
}
}
@@ -1880,7 +1896,7 @@
queueReadyJobsForExecutionLocked();
break;
case MSG_STOP_JOB:
- cancelJobImplLocked((JobStatus) message.obj, null,
+ cancelJobImplLocked((JobStatus) message.obj, null, message.arg1,
"app no longer allowed to run");
break;
@@ -1895,7 +1911,9 @@
final boolean disabled = message.arg2 != 0;
updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
if (disabled) {
- cancelJobsForUid(uid, "uid gone");
+ cancelJobsForUid(uid,
+ JobParameters.STOP_REASON_BACKGROUND_RESTRICTION,
+ "uid gone");
}
synchronized (mLock) {
mDeviceIdleJobsController.setUidActiveLocked(uid, false);
@@ -1913,7 +1931,9 @@
final int uid = message.arg1;
final boolean disabled = message.arg2 != 0;
if (disabled) {
- cancelJobsForUid(uid, "app uid idle");
+ cancelJobsForUid(uid,
+ JobParameters.STOP_REASON_BACKGROUND_RESTRICTION,
+ "app uid idle");
}
synchronized (mLock) {
mDeviceIdleJobsController.setUidActiveLocked(uid, false);
@@ -1965,10 +1985,12 @@
if (running.getEffectiveStandbyBucket() == RESTRICTED_INDEX
&& !running.areDynamicConstraintsSatisfied()) {
serviceContext.cancelExecutingJobLocked(
+ running.getStopReason(),
JobParameters.REASON_RESTRICTED_BUCKET,
"cancelled due to restricted bucket");
} else {
serviceContext.cancelExecutingJobLocked(
+ running.getStopReason(),
JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED,
"cancelled due to unsatisfied constraints");
}
@@ -1977,7 +1999,9 @@
if (restriction != null) {
final int reason = restriction.getReason();
serviceContext.cancelExecutingJobLocked(reason,
- "restricted due to " + JobParameters.getReasonCodeDescription(reason));
+ restriction.getLegacyReason(),
+ "restricted due to " + JobParameters.getLegacyReasonCodeDescription(
+ reason));
}
}
}
@@ -2058,15 +2082,14 @@
@Override
public void accept(JobStatus job) {
if (isReadyToBeExecutedLocked(job)) {
- try {
- if (ActivityManager.getService().isAppStartModeDisabled(job.getUid(),
- job.getJob().getService().getPackageName())) {
- Slog.w(TAG, "Aborting job " + job.getUid() + ":"
- + job.getJob().toString() + " -- package not allowed to start");
- mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget();
- return;
- }
- } catch (RemoteException e) {
+ if (mActivityManagerInternal.isAppStartModeDisabled(job.getUid(),
+ job.getJob().getService().getPackageName())) {
+ Slog.w(TAG, "Aborting job " + job.getUid() + ":"
+ + job.getJob().toString() + " -- package not allowed to start");
+ mHandler.obtainMessage(MSG_STOP_JOB,
+ JobParameters.STOP_REASON_BACKGROUND_RESTRICTION, 0, job)
+ .sendToTarget();
+ return;
}
final boolean shouldForceBatchJob;
@@ -2276,7 +2299,7 @@
if (restriction != null) {
if (DEBUG) {
Slog.v(TAG, "areComponentsInPlaceLocked: " + job.toShortString()
- + " restricted due to " + restriction.getReason());
+ + " restricted due to " + restriction.getLegacyReason());
}
return false;
}
@@ -2367,8 +2390,9 @@
}
@Override
- public void cancelJobsForUid(int uid, String reason) {
- JobSchedulerService.this.cancelJobsForUid(uid, reason);
+ public void cancelJobsForUid(int uid, @JobParameters.StopReason int reason,
+ String debugReason) {
+ JobSchedulerService.this.cancelJobsForUid(uid, reason, debugReason);
}
@Override
@@ -2706,6 +2730,7 @@
final long ident = Binder.clearCallingIdentity();
try {
JobSchedulerService.this.cancelJobsForUid(uid,
+ JobParameters.STOP_REASON_CANCELLED_BY_APP,
"cancelAll() called by app, callingUid=" + uid);
} finally {
Binder.restoreCallingIdentity(ident);
@@ -2718,7 +2743,8 @@
final long ident = Binder.clearCallingIdentity();
try {
- JobSchedulerService.this.cancelJob(uid, jobId, uid);
+ JobSchedulerService.this.cancelJob(uid, jobId, uid,
+ JobParameters.STOP_REASON_CANCELLED_BY_APP);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -2924,12 +2950,13 @@
if (!hasJobId) {
pw.println("Canceling all jobs for " + pkgName + " in user " + userId);
- if (!cancelJobsForUid(pkgUid, "cancel shell command for package")) {
+ if (!cancelJobsForUid(pkgUid, JobParameters.STOP_REASON_USER,
+ "cancel shell command for package")) {
pw.println("No matching jobs found.");
}
} else {
pw.println("Canceling job " + pkgName + "/#" + jobId + " in user " + userId);
- if (!cancelJob(pkgUid, jobId, Process.SHELL_UID)) {
+ if (!cancelJob(pkgUid, jobId, Process.SHELL_UID, JobParameters.STOP_REASON_USER)) {
pw.println("No matching job found.");
}
}
@@ -3164,8 +3191,9 @@
for (int i = mJobRestrictions.size() - 1; i >= 0; i--) {
final JobRestriction restriction = mJobRestrictions.get(i);
if (restriction.isJobRestricted(job)) {
- final int reason = restriction.getReason();
- pw.print(" " + JobParameters.getReasonCodeDescription(reason));
+ final int reason = restriction.getLegacyReason();
+ pw.print(" ");
+ pw.print(JobParameters.getLegacyReasonCodeDescription(reason));
}
}
} else {
@@ -3430,7 +3458,7 @@
final long restrictionsToken = proto.start(
JobSchedulerServiceDumpProto.RegisteredJob.RESTRICTIONS);
proto.write(JobSchedulerServiceDumpProto.JobRestriction.REASON,
- restriction.getReason());
+ restriction.getLegacyReason());
proto.write(JobSchedulerServiceDumpProto.JobRestriction.IS_RESTRICTING,
restriction.isJobRestricted(job));
proto.end(restrictionsToken);
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 9ef46df..e8bcbfb 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -354,8 +354,9 @@
/** Called externally when a job that was scheduled for execution should be cancelled. */
@GuardedBy("mLock")
- void cancelExecutingJobLocked(int reason, @NonNull String debugReason) {
- doCancelLocked(reason, debugReason);
+ void cancelExecutingJobLocked(@JobParameters.StopReason int reason,
+ int legacyStopReason, @NonNull String debugReason) {
+ doCancelLocked(reason, legacyStopReason, debugReason);
}
int getPreferredUid() {
@@ -387,7 +388,8 @@
&& (pkgName == null || pkgName.equals(executing.getSourcePackageName()))
&& (!matchJobId || jobId == executing.getJobId())) {
if (mVerb == VERB_EXECUTING) {
- mParams.setStopReason(JobParameters.REASON_TIMEOUT, reason);
+ mParams.setStopReason(JobParameters.STOP_REASON_TIMEOUT,
+ JobParameters.REASON_TIMEOUT, reason);
sendStopMessageLocked("force timeout from shell");
return true;
}
@@ -614,7 +616,8 @@
}
@GuardedBy("mLock")
- private void doCancelLocked(int stopReasonCode, @Nullable String debugReason) {
+ private void doCancelLocked(@JobParameters.StopReason int stopReasonCode, int legacyStopReason,
+ @Nullable String debugReason) {
if (mVerb == VERB_FINISHED) {
if (DEBUG) {
Slog.d(TAG,
@@ -622,8 +625,8 @@
}
return;
}
- mParams.setStopReason(stopReasonCode, debugReason);
- if (stopReasonCode == JobParameters.REASON_PREEMPT) {
+ mParams.setStopReason(stopReasonCode, legacyStopReason, debugReason);
+ if (legacyStopReason == JobParameters.REASON_PREEMPT) {
mPreferredUid = mRunningJob != null ? mRunningJob.getUid() :
NO_PREFERRED_UID;
}
@@ -781,7 +784,8 @@
// Not an error - client ran out of time.
Slog.i(TAG, "Client timed out while executing (no jobFinished received)."
+ " Sending onStop: " + getRunningJobNameLocked());
- mParams.setStopReason(JobParameters.REASON_TIMEOUT, "client timed out");
+ mParams.setStopReason(JobParameters.STOP_REASON_TIMEOUT,
+ JobParameters.REASON_TIMEOUT, "client timed out");
sendStopMessageLocked("timeout while executing");
} else {
// We've given the app the minimum execution time. See if we should stop it or
@@ -790,7 +794,11 @@
if (reason != null) {
Slog.i(TAG, "Stopping client after min execution time: "
+ getRunningJobNameLocked() + " because " + reason);
- mParams.setStopReason(JobParameters.REASON_TIMEOUT, reason);
+ // Tell the developer we're stopping the job due to device state instead
+ // of timeout since all of the reasons could equate to "the system needs
+ // the resources the app is currently using."
+ mParams.setStopReason(JobParameters.STOP_REASON_DEVICE_STATE,
+ JobParameters.REASON_TIMEOUT, reason);
sendStopMessageLocked(reason);
} else {
Slog.i(TAG, "Letting " + getRunningJobNameLocked()
@@ -844,12 +852,12 @@
}
applyStoppedReasonLocked(reason);
completedJob = mRunningJob;
- final int stopReason = mParams.getStopReason();
- mJobPackageTracker.noteInactive(completedJob, stopReason, reason);
+ final int legacyStopReason = mParams.getLegacyStopReason();
+ mJobPackageTracker.noteInactive(completedJob, legacyStopReason, reason);
FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
completedJob.getSourceUid(), null, completedJob.getBatteryName(),
FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__FINISHED,
- stopReason, completedJob.getStandbyBucket(), completedJob.getJobId(),
+ legacyStopReason, completedJob.getStandbyBucket(), completedJob.getJobId(),
completedJob.hasChargingConstraint(),
completedJob.hasBatteryNotLowConstraint(),
completedJob.hasStorageNotLowConstraint(),
@@ -860,7 +868,7 @@
completedJob.hasContentTriggerConstraint());
try {
mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(), mRunningJob.getSourceUid(),
- stopReason);
+ legacyStopReason);
} catch (RemoteException e) {
// Whatever.
}
@@ -879,7 +887,7 @@
service = null;
mAvailable = true;
removeOpTimeOutLocked();
- mCompletedListener.onJobCompletedLocked(completedJob, stopReason, reschedule);
+ mCompletedListener.onJobCompletedLocked(completedJob, legacyStopReason, reschedule);
mJobConcurrencyManager.onJobCompletedLocked(this, completedJob, workType);
}
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 a230b23..548a1ac 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
@@ -210,7 +210,8 @@
jobStatus.maybeLogBucketMismatch();
}
boolean didChange =
- jobStatus.setBackgroundNotRestrictedConstraintSatisfied(nowElapsed, canRun);
+ jobStatus.setBackgroundNotRestrictedConstraintSatisfied(nowElapsed, canRun,
+ !mAppStateTracker.isRunAnyInBackgroundAppOpsAllowed(uid, packageName));
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 bad8dc1..659cfa7 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
@@ -24,6 +24,7 @@
import android.app.AppGlobals;
import android.app.job.JobInfo;
+import android.app.job.JobParameters;
import android.app.job.JobWorkItem;
import android.content.ClipData;
import android.content.ComponentName;
@@ -353,6 +354,9 @@
*/
private long mLastFailedRunTime;
+ /** Whether or not the app is background restricted by the user (FAS). */
+ private boolean mIsUserBgRestricted;
+
/**
* Transient: when a job is inflated from disk before we have a reliable RTC clock time,
* we retain the canonical (delay, deadline) scheduling tuple read out of the persistent
@@ -409,6 +413,9 @@
/** The job's dynamic requirements have been satisfied. */
private boolean mReadyDynamicSatisfied;
+ /** The reason a job most recently went from ready to not ready. */
+ private int mReasonReadyToUnready = JobParameters.STOP_REASON_UNDEFINED;
+
/** Provide a handle to the service that this job will be run on. */
public int getServiceToken() {
return callingUid;
@@ -1042,6 +1049,11 @@
mOriginalLatestRunTimeElapsedMillis = latestRunTimeElapsed;
}
+ @JobParameters.StopReason
+ public int getStopReason() {
+ return mReasonReadyToUnready;
+ }
+
/**
* Return the fractional position of "now" within the "run time" window of
* this job.
@@ -1172,7 +1184,9 @@
}
/** @return true if the constraint was changed, false otherwise. */
- boolean setBackgroundNotRestrictedConstraintSatisfied(final long nowElapsed, boolean state) {
+ boolean setBackgroundNotRestrictedConstraintSatisfied(final long nowElapsed, boolean state,
+ boolean isUserBgRestricted) {
+ mIsUserBgRestricted = isUserBgRestricted;
if (setConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED, nowElapsed, state)) {
// The constraint was changed. Update the ready flag.
mReadyNotRestrictedInBg = state;
@@ -1226,6 +1240,7 @@
"Constraint " + constraint + " is " + (!state ? "NOT " : "") + "satisfied for "
+ toShortString());
}
+ final boolean wasReady = !state && isReady();
satisfiedConstraints = (satisfiedConstraints&~constraint) | (state ? constraint : 0);
mSatisfiedConstraintsOfInterest = satisfiedConstraints & CONSTRAINTS_OF_INTEREST;
mReadyDynamicSatisfied = mDynamicConstraints != 0
@@ -1244,9 +1259,81 @@
mConstraintChangeHistoryIndex =
(mConstraintChangeHistoryIndex + 1) % NUM_CONSTRAINT_CHANGE_HISTORY;
+ // Can't use isReady() directly since "cache booleans" haven't updated yet.
+ final boolean isReady = readinessStatusWithConstraint(constraint, state);
+ if (wasReady && !isReady) {
+ mReasonReadyToUnready = constraintToStopReason(constraint);
+ } else if (!wasReady && isReady) {
+ mReasonReadyToUnready = JobParameters.STOP_REASON_UNDEFINED;
+ }
+
return true;
}
+ @JobParameters.StopReason
+ private int constraintToStopReason(int constraint) {
+ switch (constraint) {
+ case CONSTRAINT_BATTERY_NOT_LOW:
+ if ((requiredConstraints & constraint) != 0) {
+ // The developer requested this constraint, so it makes sense to return the
+ // explicit constraint reason.
+ return JobParameters.STOP_REASON_CONSTRAINT_BATTERY_NOT_LOW;
+ }
+ // Hard-coding right now since the current dynamic constraint sets don't overlap
+ // TODO: return based on active dynamic constraint sets when they start overlapping
+ return JobParameters.STOP_REASON_APP_STANDBY;
+ case CONSTRAINT_CHARGING:
+ if ((requiredConstraints & constraint) != 0) {
+ // The developer requested this constraint, so it makes sense to return the
+ // explicit constraint reason.
+ return JobParameters.STOP_REASON_CONSTRAINT_CHARGING;
+ }
+ // Hard-coding right now since the current dynamic constraint sets don't overlap
+ // TODO: return based on active dynamic constraint sets when they start overlapping
+ return JobParameters.STOP_REASON_APP_STANDBY;
+ case CONSTRAINT_CONNECTIVITY:
+ return JobParameters.STOP_REASON_CONSTRAINT_CONNECTIVITY;
+ case CONSTRAINT_IDLE:
+ if ((requiredConstraints & constraint) != 0) {
+ // The developer requested this constraint, so it makes sense to return the
+ // explicit constraint reason.
+ return JobParameters.STOP_REASON_CONSTRAINT_DEVICE_IDLE;
+ }
+ // Hard-coding right now since the current dynamic constraint sets don't overlap
+ // TODO: return based on active dynamic constraint sets when they start overlapping
+ return JobParameters.STOP_REASON_APP_STANDBY;
+ case CONSTRAINT_STORAGE_NOT_LOW:
+ return JobParameters.STOP_REASON_CONSTRAINT_STORAGE_NOT_LOW;
+
+ case CONSTRAINT_BACKGROUND_NOT_RESTRICTED:
+ // The BACKGROUND_NOT_RESTRICTED constraint could be dissatisfied either because
+ // the app is background restricted, or because we're restricting background work
+ // in battery saver. Assume that background restriction is the reason apps that
+ // are background restricted have their jobs stopped, and battery saver otherwise.
+ // This has the benefit of being consistent for background restricted apps
+ // (they'll always get BACKGROUND_RESTRICTION) as the reason, regardless of
+ // battery saver state.
+ if (mIsUserBgRestricted) {
+ return JobParameters.STOP_REASON_BACKGROUND_RESTRICTION;
+ }
+ return JobParameters.STOP_REASON_DEVICE_STATE;
+ case CONSTRAINT_DEVICE_NOT_DOZING:
+ return JobParameters.STOP_REASON_DEVICE_STATE;
+
+ case CONSTRAINT_WITHIN_QUOTA:
+ case CONSTRAINT_WITHIN_EXPEDITED_QUOTA:
+ return JobParameters.STOP_REASON_QUOTA;
+
+ // These should never be stop reasons since they can never go from true to false.
+ case CONSTRAINT_CONTENT_TRIGGER:
+ case CONSTRAINT_DEADLINE:
+ case CONSTRAINT_TIMING_DELAY:
+ default:
+ Slog.wtf(TAG, "Unsupported constraint (" + constraint + ") --stop reason mapping");
+ return JobParameters.STOP_REASON_UNDEFINED;
+ }
+ }
+
boolean isConstraintSatisfied(int constraint) {
return (satisfiedConstraints&constraint) != 0;
}
@@ -1330,33 +1417,42 @@
* granted, based on its requirements.
*/
boolean wouldBeReadyWithConstraint(int constraint) {
+ return readinessStatusWithConstraint(constraint, true);
+ }
+
+ private boolean readinessStatusWithConstraint(int constraint, boolean value) {
boolean oldValue = false;
int satisfied = mSatisfiedConstraintsOfInterest;
switch (constraint) {
case CONSTRAINT_BACKGROUND_NOT_RESTRICTED:
oldValue = mReadyNotRestrictedInBg;
- mReadyNotRestrictedInBg = true;
+ mReadyNotRestrictedInBg = value;
break;
case CONSTRAINT_DEADLINE:
oldValue = mReadyDeadlineSatisfied;
- mReadyDeadlineSatisfied = true;
+ mReadyDeadlineSatisfied = value;
break;
case CONSTRAINT_DEVICE_NOT_DOZING:
oldValue = mReadyNotDozing;
- mReadyNotDozing = true;
+ mReadyNotDozing = value;
break;
case CONSTRAINT_WITHIN_QUOTA:
oldValue = mReadyWithinQuota;
- mReadyWithinQuota = true;
+ mReadyWithinQuota = value;
break;
case CONSTRAINT_WITHIN_EXPEDITED_QUOTA:
oldValue = mReadyWithinExpeditedQuota;
- mReadyWithinExpeditedQuota = true;
+ mReadyWithinExpeditedQuota = value;
break;
default:
- satisfied |= constraint;
+ if (value) {
+ satisfied |= constraint;
+ } else {
+ satisfied &= ~constraint;
+ }
mReadyDynamicSatisfied = mDynamicConstraints != 0
&& mDynamicConstraints == (satisfied & mDynamicConstraints);
+
break;
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/restrictions/JobRestriction.java b/apex/jobscheduler/service/java/com/android/server/job/restrictions/JobRestriction.java
index ac59f95..2962b10 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/restrictions/JobRestriction.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/restrictions/JobRestriction.java
@@ -17,6 +17,7 @@
package com.android.server.job.restrictions;
import android.app.job.JobInfo;
+import android.app.job.JobParameters;
import android.util.IndentingPrintWriter;
import android.util.proto.ProtoOutputStream;
@@ -26,9 +27,8 @@
/**
* Used by {@link JobSchedulerService} to impose additional restrictions regarding whether jobs
* should be scheduled or not based on the state of the system/device.
- * Every restriction is associated with exactly one reason (from {@link
- * android.app.job.JobParameters#JOB_STOP_REASON_CODES}), which could be retrieved using {@link
- * #getReason()}.
+ * Every restriction is associated with exactly one stop reason, which could be retrieved using
+ * {@link #getReason()} (and the legacy reason via {@link #getLegacyReason()}).
* Note, that this is not taken into account for the jobs that have priority
* {@link JobInfo#PRIORITY_FOREGROUND_APP} or higher.
*/
@@ -36,10 +36,13 @@
final JobSchedulerService mService;
private final int mReason;
+ private final int mLegacyReason;
- JobRestriction(JobSchedulerService service, int reason) {
+ JobRestriction(JobSchedulerService service, @JobParameters.StopReason int reason,
+ int legacyReason) {
mService = service;
mReason = reason;
+ mLegacyReason = legacyReason;
}
/**
@@ -66,7 +69,12 @@
public abstract void dumpConstants(ProtoOutputStream proto);
/** @return reason code for the Restriction. */
+ @JobParameters.StopReason
public final int getReason() {
return mReason;
}
+
+ public final int getLegacyReason() {
+ return mLegacyReason;
+ }
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java b/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
index 954a5b8..8b699e9 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
@@ -34,7 +34,7 @@
private PowerManager mPowerManager;
public ThermalStatusRestriction(JobSchedulerService service) {
- super(service, JobParameters.REASON_DEVICE_THERMAL);
+ super(service, JobParameters.STOP_REASON_DEVICE_STATE, JobParameters.REASON_DEVICE_THERMAL);
}
@Override
diff --git a/core/api/current.txt b/core/api/current.txt
index dad2d7d..c18e122 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -5830,8 +5830,8 @@
method @NonNull public android.app.Notification.BigPictureStyle bigLargeIcon(@Nullable android.graphics.drawable.Icon);
method @NonNull public android.app.Notification.BigPictureStyle bigPicture(@Nullable android.graphics.Bitmap);
method @NonNull public android.app.Notification.BigPictureStyle bigPicture(@Nullable android.graphics.drawable.Icon);
- method @NonNull public android.app.Notification.BigPictureStyle bigPictureContentDescription(@Nullable CharSequence);
method @NonNull public android.app.Notification.BigPictureStyle setBigContentTitle(@Nullable CharSequence);
+ method @NonNull public android.app.Notification.BigPictureStyle setContentDescription(@Nullable CharSequence);
method @NonNull public android.app.Notification.BigPictureStyle setSummaryText(@Nullable CharSequence);
method @NonNull public android.app.Notification.BigPictureStyle showBigPictureWhenCollapsed(boolean);
}
@@ -7970,6 +7970,7 @@
method @NonNull public android.os.PersistableBundle getExtras();
method public int getJobId();
method @Nullable public android.net.Network getNetwork();
+ method public int getStopReason();
method @NonNull public android.os.Bundle getTransientExtras();
method @Nullable public String[] getTriggeredContentAuthorities();
method @Nullable public android.net.Uri[] getTriggeredContentUris();
@@ -7978,6 +7979,21 @@
method public boolean isOverrideDeadlineExpired();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.job.JobParameters> CREATOR;
+ field public static final int STOP_REASON_APP_STANDBY = 12; // 0xc
+ field public static final int STOP_REASON_BACKGROUND_RESTRICTION = 11; // 0xb
+ field public static final int STOP_REASON_CANCELLED_BY_APP = 1; // 0x1
+ field public static final int STOP_REASON_CONSTRAINT_BATTERY_NOT_LOW = 5; // 0x5
+ field public static final int STOP_REASON_CONSTRAINT_CHARGING = 6; // 0x6
+ field public static final int STOP_REASON_CONSTRAINT_CONNECTIVITY = 7; // 0x7
+ field public static final int STOP_REASON_CONSTRAINT_DEVICE_IDLE = 8; // 0x8
+ field public static final int STOP_REASON_CONSTRAINT_STORAGE_NOT_LOW = 9; // 0x9
+ field public static final int STOP_REASON_DEVICE_STATE = 4; // 0x4
+ field public static final int STOP_REASON_PREEMPT = 2; // 0x2
+ field public static final int STOP_REASON_QUOTA = 10; // 0xa
+ field public static final int STOP_REASON_SYSTEM_PROCESSING = 14; // 0xe
+ field public static final int STOP_REASON_TIMEOUT = 3; // 0x3
+ field public static final int STOP_REASON_UNDEFINED = 0; // 0x0
+ field public static final int STOP_REASON_USER = 13; // 0xd
}
public abstract class JobScheduler {
@@ -42128,7 +42144,7 @@
method public static int getDefaultSmsSubscriptionId();
method public static int getDefaultSubscriptionId();
method public static int getDefaultVoiceSubscriptionId();
- method public int getDeviceToDeviceStatusSharing(int);
+ method public int getDeviceToDeviceStatusSharingPreference(int);
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telephony.SubscriptionInfo> getOpportunisticSubscriptions();
method public static int getSlotIndex(int);
method @Nullable public int[] getSubscriptionIds(int);
@@ -42141,7 +42157,7 @@
method public void removeOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void removeSubscriptionsFromGroup(@NonNull java.util.List<java.lang.Integer>, @NonNull android.os.ParcelUuid);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDeviceToDeviceStatusSharing(int, int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDeviceToDeviceStatusSharingPreference(int, int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunistic(boolean, int);
method public void setSubscriptionOverrideCongested(int, boolean, long);
method public void setSubscriptionOverrideCongested(int, boolean, @NonNull int[], long);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 7bbabb6..1b3edcf 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1192,12 +1192,14 @@
public class RestoreSet implements android.os.Parcelable {
ctor public RestoreSet();
- ctor public RestoreSet(String, String, long);
+ ctor public RestoreSet(@Nullable String, @Nullable String, long);
+ ctor public RestoreSet(@Nullable String, @Nullable String, long, int);
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.backup.RestoreSet> CREATOR;
- field public String device;
- field public String name;
+ field public final int backupTransportFlags;
+ field @Nullable public String device;
+ field @Nullable public String name;
field public long token;
}
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 3bfddf7..e5a969a 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -244,6 +244,8 @@
boolean requestAutofillData(in IAssistDataReceiver receiver, in Bundle receiverExtras,
in IBinder activityToken, int flags);
boolean isAssistDataAllowedOnCurrentActivity();
+ boolean requestAssistDataForTask(in IAssistDataReceiver receiver, int taskId,
+ in String callingPackageName);
/**
* Notify the system that the keyguard is going away.
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 3a533c9..4eda6fe 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -20,8 +20,6 @@
import static android.graphics.drawable.Icon.TYPE_URI;
import static android.graphics.drawable.Icon.TYPE_URI_ADAPTIVE_BITMAP;
-import static com.android.internal.util.ContrastColorUtil.satisfiesTextContrast;
-
import static java.util.Objects.requireNonNull;
import android.annotation.AttrRes;
@@ -1216,7 +1214,7 @@
/**
* {@link #extras} key: this is a content description of the big picture supplied from
* {@link BigPictureStyle#bigPicture(Bitmap)}, supplied to
- * {@link BigPictureStyle#bigPictureContentDescription(CharSequence)}.
+ * {@link BigPictureStyle#setContentDescription(CharSequence)}.
*/
public static final String EXTRA_PICTURE_CONTENT_DESCRIPTION =
"android.pictureContentDescription";
@@ -3701,8 +3699,6 @@
private int mTextColorsAreForBackground = COLOR_INVALID;
private int mPrimaryTextColor = COLOR_INVALID;
private int mSecondaryTextColor = COLOR_INVALID;
- private int mBackgroundColor = COLOR_INVALID;
- private int mForegroundColor = COLOR_INVALID;
private boolean mRebuildStyledRemoteViews;
private boolean mTintActionButtons;
@@ -5041,7 +5037,8 @@
private RemoteViews applyStandardTemplate(int resId, StandardTemplateParams p,
TemplateBindResult result) {
p.headerless(resId == getBaseLayoutResource()
- || resId == getHeadsUpBaseLayoutResource());
+ || resId == getHeadsUpBaseLayoutResource()
+ || resId == R.layout.notification_template_material_media);
RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
resetStandardTemplate(contentView);
@@ -5092,7 +5089,7 @@
}
private CharSequence processTextSpans(CharSequence text) {
- if (hasForegroundColor() || mInNightMode) {
+ if (mInNightMode) {
return ContrastColorUtil.clearColorSpans(text);
}
return text;
@@ -5103,10 +5100,6 @@
contentView.setTextColor(id, getPrimaryTextColor(p));
}
- private boolean hasForegroundColor() {
- return mForegroundColor != COLOR_INVALID;
- }
-
/**
* @param p the template params to inflate this with
* @return the primary text color
@@ -5140,75 +5133,15 @@
|| mSecondaryTextColor == COLOR_INVALID
|| mTextColorsAreForBackground != backgroundColor) {
mTextColorsAreForBackground = backgroundColor;
- if (!hasForegroundColor() || !isColorized(p)) {
- mPrimaryTextColor = ContrastColorUtil.resolvePrimaryColor(mContext,
- backgroundColor, mInNightMode);
- mSecondaryTextColor = ContrastColorUtil.resolveSecondaryColor(mContext,
- backgroundColor, mInNightMode);
- if (backgroundColor != COLOR_DEFAULT && isColorized(p)) {
- mPrimaryTextColor = ContrastColorUtil.findAlphaToMeetContrast(
- mPrimaryTextColor, backgroundColor, 4.5);
- mSecondaryTextColor = ContrastColorUtil.findAlphaToMeetContrast(
- mSecondaryTextColor, backgroundColor, 4.5);
- }
- } else {
- double backLum = ContrastColorUtil.calculateLuminance(backgroundColor);
- double textLum = ContrastColorUtil.calculateLuminance(mForegroundColor);
- double contrast = ContrastColorUtil.calculateContrast(mForegroundColor,
- backgroundColor);
- // We only respect the given colors if worst case Black or White still has
- // contrast
- boolean backgroundLight = backLum > textLum
- && satisfiesTextContrast(backgroundColor, Color.BLACK)
- || backLum <= textLum
- && !satisfiesTextContrast(backgroundColor, Color.WHITE);
- if (contrast < 4.5f) {
- if (backgroundLight) {
- mSecondaryTextColor = ContrastColorUtil.findContrastColor(
- mForegroundColor,
- backgroundColor,
- true /* findFG */,
- 4.5f);
- mPrimaryTextColor = ContrastColorUtil.changeColorLightness(
- mSecondaryTextColor, -LIGHTNESS_TEXT_DIFFERENCE_LIGHT);
- } else {
- mSecondaryTextColor =
- ContrastColorUtil.findContrastColorAgainstDark(
- mForegroundColor,
- backgroundColor,
- true /* findFG */,
- 4.5f);
- mPrimaryTextColor = ContrastColorUtil.changeColorLightness(
- mSecondaryTextColor, -LIGHTNESS_TEXT_DIFFERENCE_DARK);
- }
- } else {
- mPrimaryTextColor = mForegroundColor;
- mSecondaryTextColor = ContrastColorUtil.changeColorLightness(
- mPrimaryTextColor, backgroundLight ? LIGHTNESS_TEXT_DIFFERENCE_LIGHT
- : LIGHTNESS_TEXT_DIFFERENCE_DARK);
- if (ContrastColorUtil.calculateContrast(mSecondaryTextColor,
- backgroundColor) < 4.5f) {
- // oh well the secondary is not good enough
- if (backgroundLight) {
- mSecondaryTextColor = ContrastColorUtil.findContrastColor(
- mSecondaryTextColor,
- backgroundColor,
- true /* findFG */,
- 4.5f);
- } else {
- mSecondaryTextColor
- = ContrastColorUtil.findContrastColorAgainstDark(
- mSecondaryTextColor,
- backgroundColor,
- true /* findFG */,
- 4.5f);
- }
- mPrimaryTextColor = ContrastColorUtil.changeColorLightness(
- mSecondaryTextColor, backgroundLight
- ? -LIGHTNESS_TEXT_DIFFERENCE_LIGHT
- : -LIGHTNESS_TEXT_DIFFERENCE_DARK);
- }
- }
+ mPrimaryTextColor = ContrastColorUtil.resolvePrimaryColor(mContext,
+ backgroundColor, mInNightMode);
+ mSecondaryTextColor = ContrastColorUtil.resolveSecondaryColor(mContext,
+ backgroundColor, mInNightMode);
+ if (backgroundColor != COLOR_DEFAULT && isColorized(p)) {
+ mPrimaryTextColor = ContrastColorUtil.findAlphaToMeetContrast(
+ mPrimaryTextColor, backgroundColor, 4.5);
+ mSecondaryTextColor = ContrastColorUtil.findAlphaToMeetContrast(
+ mSecondaryTextColor, backgroundColor, 4.5);
}
}
}
@@ -5254,11 +5187,7 @@
result = new TemplateBindResult();
}
bindLargeIcon(contentView, p, result);
- if (p.mHeaderless) {
- // views in the headerless (collapsed) state
- result.mHeadingExtraMarginSet.applyToView(contentView,
- R.id.notification_headerless_view_column);
- } else {
+ if (!p.mHeaderless) {
// views in states with a header (big states)
result.mHeadingExtraMarginSet.applyToView(contentView, R.id.notification_header);
result.mTitleMarginSet.applyToView(contentView, R.id.title);
@@ -5278,6 +5207,8 @@
@NonNull TemplateBindResult result) {
final Resources resources = mContext.getResources();
final float density = resources.getDisplayMetrics().density;
+ final float iconMarginDp = resources.getDimension(
+ R.dimen.notification_right_icon_content_margin) / density;
final float contentMarginDp = resources.getDimension(
R.dimen.notification_content_margin_end) / density;
final float expanderSizeDp = resources.getDimension(
@@ -5299,7 +5230,7 @@
}
}
}
- final float extraMarginEndDpIfVisible = viewWidthDp + contentMarginDp;
+ final float extraMarginEndDpIfVisible = viewWidthDp + iconMarginDp;
result.setRightIconState(largeIconShown, viewWidthDp,
extraMarginEndDpIfVisible, expanderSizeDp);
}
@@ -5363,7 +5294,7 @@
private void bindHeaderChronometerAndTime(RemoteViews contentView,
StandardTemplateParams p, boolean hasTextToLeft) {
- if (showsTimeOrChronometer()) {
+ if (!p.mHideTime && showsTimeOrChronometer()) {
if (hasTextToLeft) {
contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
setTextViewColorSecondary(contentView, R.id.time_divider, p);
@@ -5394,6 +5325,9 @@
*/
private boolean bindHeaderText(RemoteViews contentView, StandardTemplateParams p,
boolean hasTextToLeft) {
+ if (p.mHideSubText) {
+ return false;
+ }
CharSequence summaryText = p.summaryText;
if (summaryText == null && mStyle != null && mStyle.mSummaryTextSet
&& mStyle.hasSummaryInHeader()) {
@@ -5424,6 +5358,9 @@
*/
private boolean bindHeaderTextSecondary(RemoteViews contentView, StandardTemplateParams p,
boolean hasTextToLeft) {
+ if (p.mHideSubText) {
+ return false;
+ }
if (!TextUtils.isEmpty(p.headerTextSecondary)) {
contentView.setTextViewText(R.id.header_text_secondary, processTextSpans(
processLegacyText(p.headerTextSecondary)));
@@ -6654,7 +6591,7 @@
*/
private @ColorInt int getUnresolvedBackgroundColor(StandardTemplateParams p) {
if (isColorized(p)) {
- return mBackgroundColor != COLOR_INVALID ? mBackgroundColor : getRawColor(p);
+ return getRawColor(p);
} else {
return COLOR_DEFAULT;
}
@@ -6682,21 +6619,6 @@
}
/**
- * Set a color palette to be used as the background and textColors
- *
- * @param backgroundColor the color to be used as the background
- * @param foregroundColor the color to be used as the foreground
- *
- * @hide
- */
- public void setColorPalette(@ColorInt int backgroundColor, @ColorInt int foregroundColor) {
- mBackgroundColor = backgroundColor;
- mForegroundColor = foregroundColor;
- mTextColorsAreForBackground = COLOR_INVALID;
- ensureColors(mParams.reset().fillTextsFrom(this));
- }
-
- /**
* Forces all styled remoteViews to be built from scratch and not use any cached
* RemoteViews.
* This is needed for legacy apps that are baking in their remoteviews into the
@@ -6752,24 +6674,14 @@
if (mLargeIcon != null || largeIcon != null) {
Resources resources = context.getResources();
Class<? extends Style> style = getNotificationStyle();
- int maxWidth = resources.getDimensionPixelSize(isLowRam
+ int maxSize = resources.getDimensionPixelSize(isLowRam
? R.dimen.notification_right_icon_size_low_ram
: R.dimen.notification_right_icon_size);
- int maxHeight = maxWidth;
- if (MediaStyle.class.equals(style)
- || DecoratedMediaCustomViewStyle.class.equals(style)) {
- maxHeight = resources.getDimensionPixelSize(isLowRam
- ? R.dimen.notification_media_image_max_height_low_ram
- : R.dimen.notification_media_image_max_height);
- maxWidth = resources.getDimensionPixelSize(isLowRam
- ? R.dimen.notification_media_image_max_width_low_ram
- : R.dimen.notification_media_image_max_width);
- }
if (mLargeIcon != null) {
- mLargeIcon.scaleDownIfNecessary(maxWidth, maxHeight);
+ mLargeIcon.scaleDownIfNecessary(maxSize, maxSize);
}
if (largeIcon != null) {
- largeIcon = Icon.scaleDownIfNecessary(largeIcon, maxWidth, maxHeight);
+ largeIcon = Icon.scaleDownIfNecessary(largeIcon, maxSize, maxSize);
}
}
reduceImageSizesForRemoteView(contentView, context, isLowRam);
@@ -6856,9 +6768,6 @@
* @hide
*/
public boolean isColorized() {
- if (isColorizedMedia()) {
- return true;
- }
return extras.getBoolean(EXTRA_COLORIZED)
&& (hasColorizedPermission() || isForegroundService());
}
@@ -6872,27 +6781,6 @@
}
/**
- * @return true if this notification is colorized and it is a media notification
- *
- * @hide
- */
- public boolean isColorizedMedia() {
- Class<? extends Style> style = getNotificationStyle();
- if (MediaStyle.class.equals(style)) {
- Boolean colorized = (Boolean) extras.get(EXTRA_COLORIZED);
- if ((colorized == null || colorized) && hasMediaSession()) {
- return true;
- }
- } else if (DecoratedMediaCustomViewStyle.class.equals(style)) {
- if (extras.getBoolean(EXTRA_COLORIZED) && hasMediaSession()) {
- return true;
- }
- }
- return false;
- }
-
-
- /**
* @return true if this is a media notification
*
* @hide
@@ -7180,15 +7068,6 @@
/**
* @hide
- * @return true if the style positions the progress bar on the second line; false if the
- * style hides the progress bar
- */
- protected boolean hasProgress() {
- return true;
- }
-
- /**
- * @hide
* @return Whether we should put the summary be put into the notification header
*/
public boolean hasSummaryInHeader() {
@@ -7292,7 +7171,7 @@
* Set the content description of the big picture.
*/
@NonNull
- public BigPictureStyle bigPictureContentDescription(
+ public BigPictureStyle setContentDescription(
@Nullable CharSequence contentDescription) {
mPictureContentDescription = contentDescription;
return this;
@@ -9041,7 +8920,7 @@
*/
@Override
public RemoteViews makeContentView(boolean increasedHeight) {
- return makeMediaContentView();
+ return makeMediaContentView(null /* customContent */);
}
/**
@@ -9049,7 +8928,7 @@
*/
@Override
public RemoteViews makeBigContentView() {
- return makeMediaBigContentView();
+ return makeMediaBigContentView(null /* customContent */);
}
/**
@@ -9057,7 +8936,7 @@
*/
@Override
public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
- return makeMediaContentView();
+ return makeMediaContentView(null /* customContent */);
}
/** @hide */
@@ -9127,88 +9006,72 @@
container.setContentDescription(buttonId, action.title);
}
- private RemoteViews makeMediaContentView() {
- StandardTemplateParams p = mBuilder.mParams.reset()
- .viewType(StandardTemplateParams.VIEW_TYPE_NORMAL)
- .hideProgress(true)
- .fillTextsFrom(mBuilder);
- RemoteViews view = mBuilder.applyStandardTemplate(
- R.layout.notification_template_material_media, p,
- null /* result */);
-
+ /** @hide */
+ protected RemoteViews makeMediaContentView(@Nullable RemoteViews customContent) {
final int numActions = mBuilder.mActions.size();
- final int numActionsToShow = mActionsToShowInCompact == null
- ? 0
- : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
+ final int numActionsToShow = Math.min(mActionsToShowInCompact == null
+ ? 0 : mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
if (numActionsToShow > numActions) {
throw new IllegalArgumentException(String.format(
"setShowActionsInCompactView: action %d out of bounds (max %d)",
numActions, numActions - 1));
}
+
+ StandardTemplateParams p = mBuilder.mParams.reset()
+ .viewType(StandardTemplateParams.VIEW_TYPE_NORMAL)
+ .hideTime(numActionsToShow > 1) // hide if actions wider than a large icon
+ .hideSubText(numActionsToShow > 1) // hide if actions wider than a large icon
+ .hideLargeIcon(numActionsToShow > 0) // large icon or actions; not both
+ .hideProgress(true)
+ .fillTextsFrom(mBuilder);
+ TemplateBindResult result = new TemplateBindResult();
+ RemoteViews template = mBuilder.applyStandardTemplate(
+ R.layout.notification_template_material_media, p,
+ null /* result */);
+
for (int i = 0; i < MAX_MEDIA_BUTTONS_IN_COMPACT; i++) {
if (i < numActionsToShow) {
final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
- bindMediaActionButton(view, MEDIA_BUTTON_IDS[i], action, p);
+ bindMediaActionButton(template, MEDIA_BUTTON_IDS[i], action, p);
} else {
- view.setViewVisibility(MEDIA_BUTTON_IDS[i], View.GONE);
+ template.setViewVisibility(MEDIA_BUTTON_IDS[i], View.GONE);
}
}
- handleImage(view);
- // handle the content margin
- int endMargin = R.dimen.notification_content_margin_end;
- if (mBuilder.mN.hasLargeIcon()) {
- endMargin = R.dimen.notification_media_image_margin_end;
- }
- view.setViewLayoutMarginDimen(R.id.notification_main_column,
- RemoteViews.MARGIN_END, endMargin);
- return view;
+ // Prevent a swooping expand animation when there are no actions
+ boolean hasActions = numActionsToShow != 0;
+ template.setViewVisibility(R.id.media_actions, hasActions ? View.VISIBLE : View.GONE);
+
+ // Add custom view if provided by subclass.
+ buildCustomContentIntoTemplate(mBuilder.mContext, template, customContent, p, result,
+ DevFlags.DECORATION_PARTIAL);
+ return template;
}
- private RemoteViews makeMediaBigContentView() {
+ /** @hide */
+ protected RemoteViews makeMediaBigContentView(@Nullable RemoteViews customContent) {
final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
- // Dont add an expanded view if there is no more content to be revealed
- int actionsInCompact = mActionsToShowInCompact == null
- ? 0
- : Math.min(mActionsToShowInCompact.length, MAX_MEDIA_BUTTONS_IN_COMPACT);
- if (!mBuilder.mN.hasLargeIcon() && actionCount <= actionsInCompact) {
- return null;
- }
StandardTemplateParams p = mBuilder.mParams.reset()
.viewType(StandardTemplateParams.VIEW_TYPE_BIG)
.hideProgress(true)
.fillTextsFrom(mBuilder);
- RemoteViews big = mBuilder.applyStandardTemplate(
- R.layout.notification_template_material_big_media, p , null /* result */);
+ TemplateBindResult result = new TemplateBindResult();
+ RemoteViews template = mBuilder.applyStandardTemplate(
+ R.layout.notification_template_material_big_media, p , result);
for (int i = 0; i < MAX_MEDIA_BUTTONS; i++) {
if (i < actionCount) {
- bindMediaActionButton(big, MEDIA_BUTTON_IDS[i], mBuilder.mActions.get(i), p);
+ bindMediaActionButton(template,
+ MEDIA_BUTTON_IDS[i], mBuilder.mActions.get(i), p);
} else {
- big.setViewVisibility(MEDIA_BUTTON_IDS[i], View.GONE);
+ template.setViewVisibility(MEDIA_BUTTON_IDS[i], View.GONE);
}
}
- handleImage(big);
- return big;
- }
-
- private void handleImage(RemoteViews contentView) {
- if (mBuilder.mN.hasLargeIcon()) {
- contentView.setViewLayoutMarginDimen(R.id.title, RemoteViews.MARGIN_END, 0);
- contentView.setViewLayoutMarginDimen(R.id.text, RemoteViews.MARGIN_END, 0);
- }
- }
-
- /**
- * @hide
- */
- @Override
- protected boolean hasProgress() {
- return false;
+ buildCustomContentIntoTemplate(mBuilder.mContext, template, customContent, p, result,
+ DevFlags.DECORATION_PARTIAL);
+ return template;
}
}
-
-
/**
* Helper class for generating large-format notifications that include a large image attachment.
*
@@ -9896,9 +9759,7 @@
*/
@Override
public RemoteViews makeContentView(boolean increasedHeight) {
- RemoteViews remoteViews = super.makeContentView(false /* increasedHeight */);
- return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
- mBuilder.mN.contentView);
+ return makeMediaContentView(mBuilder.mN.contentView);
}
/**
@@ -9906,24 +9767,10 @@
*/
@Override
public RemoteViews makeBigContentView() {
- RemoteViews customRemoteView = mBuilder.mN.bigContentView != null
+ RemoteViews customContent = mBuilder.mN.bigContentView != null
? mBuilder.mN.bigContentView
: mBuilder.mN.contentView;
- return makeBigContentViewWithCustomContent(customRemoteView);
- }
-
- private RemoteViews makeBigContentViewWithCustomContent(RemoteViews customRemoteView) {
- RemoteViews remoteViews = super.makeBigContentView();
- if (remoteViews != null) {
- return buildIntoRemoteView(remoteViews, R.id.notification_main_column,
- customRemoteView);
- } else if (customRemoteView != mBuilder.mN.contentView){
- remoteViews = super.makeContentView(false /* increasedHeight */);
- return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
- customRemoteView);
- } else {
- return null;
- }
+ return makeMediaBigContentView(customContent);
}
/**
@@ -9931,10 +9778,10 @@
*/
@Override
public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
- RemoteViews customRemoteView = mBuilder.mN.headsUpContentView != null
+ RemoteViews customContent = mBuilder.mN.headsUpContentView != null
? mBuilder.mN.headsUpContentView
: mBuilder.mN.contentView;
- return makeBigContentViewWithCustomContent(customRemoteView);
+ return makeMediaBigContentView(customContent);
}
/**
@@ -9949,18 +9796,21 @@
return false;
}
- private RemoteViews buildIntoRemoteView(RemoteViews remoteViews, int id,
- RemoteViews customContent) {
+ private RemoteViews buildIntoRemoteView(RemoteViews template, RemoteViews customContent,
+ boolean headerless) {
if (customContent != null) {
// Need to clone customContent before adding, because otherwise it can no longer be
// parceled independently of remoteViews.
customContent = customContent.clone();
customContent.overrideTextColors(mBuilder.getPrimaryTextColor(mBuilder.mParams));
- remoteViews.removeAllViews(id);
- remoteViews.addView(id, customContent);
- remoteViews.addFlags(RemoteViews.FLAG_REAPPLY_DISALLOWED);
+ if (headerless) {
+ template.removeFromParent(R.id.notification_top_line);
+ }
+ template.removeAllViews(R.id.notification_main_column);
+ template.addView(R.id.notification_main_column, customContent);
+ template.addFlags(RemoteViews.FLAG_REAPPLY_DISALLOWED);
}
- return remoteViews;
+ return template;
}
}
@@ -12253,6 +12103,8 @@
boolean mHeaderless;
boolean mHideAppName;
boolean mHideTitle;
+ boolean mHideSubText;
+ boolean mHideTime;
boolean mHideActions;
boolean mHideProgress;
boolean mHideSnoozeButton;
@@ -12275,6 +12127,8 @@
mHeaderless = false;
mHideAppName = false;
mHideTitle = false;
+ mHideSubText = false;
+ mHideTime = false;
mHideActions = false;
mHideProgress = false;
mHideSnoozeButton = false;
@@ -12288,6 +12142,7 @@
summaryText = null;
headerTextSecondary = null;
maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES;
+ hideLargeIcon = false;
allowColorization = true;
mReduceHighlights = false;
return this;
@@ -12312,6 +12167,16 @@
return this;
}
+ public StandardTemplateParams hideSubText(boolean hideSubText) {
+ mHideSubText = hideSubText;
+ return this;
+ }
+
+ public StandardTemplateParams hideTime(boolean hideTime) {
+ mHideTime = hideTime;
+ return this;
+ }
+
final StandardTemplateParams hideActions(boolean hideActions) {
this.mHideActions = hideActions;
return this;
diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java
index 6b2e649..d640a6f 100644
--- a/core/java/android/app/WallpaperColors.java
+++ b/core/java/android/app/WallpaperColors.java
@@ -189,7 +189,7 @@
} else {
palette = Palette
.from(bitmap, new CelebiQuantizer())
- .maximumColorCount(256)
+ .maximumColorCount(5)
.resizeBitmapArea(MAX_WALLPAPER_EXTRACTION_AREA)
.generate();
}
diff --git a/core/java/android/app/backup/RestoreSet.java b/core/java/android/app/backup/RestoreSet.java
index 6759346..51430c0 100644
--- a/core/java/android/app/backup/RestoreSet.java
+++ b/core/java/android/app/backup/RestoreSet.java
@@ -16,6 +16,7 @@
package android.app.backup;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -32,12 +33,14 @@
* Name of this restore set. May be user generated, may simply be the name
* of the handset model, e.g. "T-Mobile G1".
*/
+ @Nullable
public String name;
/**
* Identifier of the device whose data this is. This will be as unique as
* is practically possible; for example, it might be an IMEI.
*/
+ @Nullable
public String device;
/**
@@ -47,15 +50,48 @@
*/
public long token;
+ /**
+ * Properties of the {@link BackupTransport} transport that was used to obtain the data in
+ * this restore set.
+ */
+ public final int backupTransportFlags;
+ /**
+ * Constructs a RestoreSet object that identifies a set of data that can be restored.
+ */
public RestoreSet() {
// Leave everything zero / null
+ backupTransportFlags = 0;
}
- public RestoreSet(String _name, String _dev, long _token) {
- name = _name;
- device = _dev;
- token = _token;
+ /**
+ * Constructs a RestoreSet object that identifies a set of data that can be restored.
+ *
+ * @param name The name of the restore set.
+ * @param device The name of the device where the restore data is coming from.
+ * @param token The unique identifier for the current restore set.
+ */
+ public RestoreSet(@Nullable String name, @Nullable String device, long token) {
+ this(name, device, token, /* backupTransportFlags */ 0);
+ }
+
+ /**
+ * Constructs a RestoreSet object that identifies a set of data that can be restored.
+ *
+ * @param name The name of the restore set.
+ * @param device The name of the device where the restore data is coming from.
+ * @param token The unique identifier for the current restore set.
+ * @param backupTransportFlags Flags returned by {@link BackupTransport#getTransportFlags()}
+ * implementation of the backup transport used by the source device
+ * to create this restore set. See {@link BackupAgent} for possible
+ * flag values.
+ */
+ public RestoreSet(@Nullable String name, @Nullable String device, long token,
+ int backupTransportFlags) {
+ this.name = name;
+ this.device = device;
+ this.token = token;
+ this.backupTransportFlags = backupTransportFlags;
}
// Parcelable implementation
@@ -67,6 +103,7 @@
out.writeString(name);
out.writeString(device);
out.writeLong(token);
+ out.writeInt(backupTransportFlags);
}
public static final @android.annotation.NonNull Parcelable.Creator<RestoreSet> CREATOR
@@ -84,5 +121,6 @@
name = in.readString();
device = in.readString();
token = in.readLong();
+ backupTransportFlags = in.readInt();
}
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index fa6472e..4674aa2 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -6030,7 +6030,7 @@
pw.print(":");
for (int it=0; it<types.size(); it++) {
pw.print(" ");
- pw.print(JobParameters.getReasonCodeDescription(types.keyAt(it)));
+ pw.print(JobParameters.getLegacyReasonCodeDescription(types.keyAt(it)));
pw.print("(");
pw.print(types.valueAt(it));
pw.print("x)");
diff --git a/core/java/android/util/SparseDoubleArray.java b/core/java/android/util/SparseDoubleArray.java
new file mode 100644
index 0000000..dc93a47
--- /dev/null
+++ b/core/java/android/util/SparseDoubleArray.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+/**
+ * SparseDoubleArrays map integers to doubles. Unlike a normal array of doubles,
+ * there can be gaps in the indices. It is intended to be more memory efficient
+ * than using a HashMap to map Integers to Doubles, both because it avoids
+ * auto-boxing keys and values and its data structure doesn't rely on an extra entry object
+ * for each mapping.
+ *
+ * <p>Note that this container keeps its mappings in an array data structure,
+ * using a binary search to find keys. The implementation is not intended to be appropriate for
+ * data structures
+ * that may contain large numbers of items. It is generally slower than a traditional
+ * HashMap, since lookups require a binary search and adds and removes require inserting
+ * and deleting entries in the array. For containers holding up to hundreds of items,
+ * the performance difference is not significant, less than 50%.</p>
+ *
+ * <p>It is possible to iterate over the items in this container using
+ * {@link #keyAt(int)} and {@link #valueAt(int)}. Iterating over the keys using
+ * <code>keyAt(int)</code> with ascending values of the index will return the
+ * keys in ascending order, or the values corresponding to the keys in ascending
+ * order in the case of <code>valueAt(int)</code>.</p>
+ *
+ * @see SparseLongArray
+ *
+ * @hide
+ */
+public class SparseDoubleArray implements Cloneable {
+ /**
+ * The int->double map, but storing the doubles as longs using
+ * {@link Double#doubleToRawLongBits(double)}.
+ */
+ private SparseLongArray mValues;
+
+ /** Creates a new SparseDoubleArray containing no mappings. */
+ public SparseDoubleArray() {
+ this(10);
+ }
+
+ /**
+ * Creates a new SparseDoubleArray, containing no mappings, that will not
+ * require any additional memory allocation to store the specified
+ * number of mappings. If you supply an initial capacity of 0, the
+ * sparse array will be initialized with a light-weight representation
+ * not requiring any additional array allocations.
+ */
+ public SparseDoubleArray(int initialCapacity) {
+ mValues = new SparseLongArray(initialCapacity);
+ }
+
+ @Override
+ public SparseDoubleArray clone() {
+ SparseDoubleArray clone = null;
+ try {
+ clone = (SparseDoubleArray) super.clone();
+ clone.mValues = mValues.clone();
+ } catch (CloneNotSupportedException cnse) {
+ /* ignore */
+ }
+ return clone;
+ }
+
+ /**
+ * Gets the double mapped from the specified key, or <code>0</code>
+ * if no such mapping has been made.
+ */
+ public double get(int key) {
+ final int index = mValues.indexOfKey(key);
+ if (index < 0) {
+ return 0.0d;
+ }
+ return valueAt(index);
+ }
+
+ /**
+ * Adds a mapping from the specified key to the specified value,
+ * replacing the previous mapping from the specified key if there
+ * was one.
+ */
+ public void put(int key, double value) {
+ mValues.put(key, Double.doubleToRawLongBits(value));
+ }
+
+ /**
+ * Adds a mapping from the specified key to the specified value,
+ * <b>adding</b> its value to the previous mapping from the specified key if there
+ * was one.
+ *
+ * <p>This differs from {@link #put} because instead of replacing any previous value, it adds
+ * (in the numerical sense) to it.
+ */
+ public void add(int key, double summand) {
+ final double oldValue = get(key);
+ put(key, oldValue + summand);
+ }
+
+ /** Returns the number of key-value mappings that this SparseDoubleArray currently stores. */
+ public int size() {
+ return mValues.size();
+ }
+
+ /**
+ * Given an index in the range <code>0...size()-1</code>, returns
+ * the key from the <code>index</code>th key-value mapping that this
+ * SparseDoubleArray stores.
+ *
+ * @see SparseLongArray#keyAt(int)
+ */
+ public int keyAt(int index) {
+ return mValues.keyAt(index);
+ }
+
+ /**
+ * Given an index in the range <code>0...size()-1</code>, returns
+ * the value from the <code>index</code>th key-value mapping that this
+ * SparseDoubleArray stores.
+ *
+ * @see SparseLongArray#valueAt(int)
+ */
+ public double valueAt(int index) {
+ return Double.longBitsToDouble(mValues.valueAt(index));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>This implementation composes a string by iterating over its mappings.
+ */
+ @Override
+ public String toString() {
+ if (size() <= 0) {
+ return "{}";
+ }
+
+ StringBuilder buffer = new StringBuilder(size() * 34);
+ buffer.append('{');
+ for (int i = 0; i < size(); i++) {
+ if (i > 0) {
+ buffer.append(", ");
+ }
+ int key = keyAt(i);
+ buffer.append(key);
+ buffer.append('=');
+ double value = valueAt(i);
+ buffer.append(value);
+ }
+ buffer.append('}');
+ return buffer.toString();
+ }
+}
diff --git a/core/java/android/view/NotificationTopLineView.java b/core/java/android/view/NotificationTopLineView.java
index 05636de..bd20f5b 100644
--- a/core/java/android/view/NotificationTopLineView.java
+++ b/core/java/android/view/NotificationTopLineView.java
@@ -26,6 +26,9 @@
import com.android.internal.R;
+import java.util.HashSet;
+import java.util.Set;
+
/**
* The top line of content in a notification view.
* This includes the text views and badges but excludes the icon and the expander.
@@ -34,17 +37,23 @@
*/
@RemoteViews.RemoteView
public class NotificationTopLineView extends ViewGroup {
+ private final OverflowAdjuster mOverflowAdjuster = new OverflowAdjuster();
private final int mGravityY;
private final int mChildMinWidth;
+ private final int mChildHideWidth;
@Nullable private View mAppName;
@Nullable private View mTitle;
private View mHeaderText;
+ private View mHeaderTextDivider;
private View mSecondaryHeaderText;
+ private View mSecondaryHeaderTextDivider;
private OnClickListener mFeedbackListener;
private HeaderTouchListener mTouchListener = new HeaderTouchListener();
private View mFeedbackIcon;
private int mHeaderTextMarginEnd;
+ private Set<View> mViewsToDisappear = new HashSet<>();
+
private int mMaxAscent;
private int mMaxDescent;
@@ -66,6 +75,7 @@
super(context, attrs, defStyleAttr, defStyleRes);
Resources res = getResources();
mChildMinWidth = res.getDimensionPixelSize(R.dimen.notification_header_shrink_min_width);
+ mChildHideWidth = res.getDimensionPixelSize(R.dimen.notification_header_shrink_hide_width);
// NOTE: Implementation only supports TOP, BOTTOM, and CENTER_VERTICAL gravities,
// with CENTER_VERTICAL being the default.
@@ -88,7 +98,9 @@
mAppName = findViewById(R.id.app_name_text);
mTitle = findViewById(R.id.title);
mHeaderText = findViewById(R.id.header_text);
+ mHeaderTextDivider = findViewById(R.id.header_text_divider);
mSecondaryHeaderText = findViewById(R.id.header_text_secondary);
+ mSecondaryHeaderTextDivider = findViewById(R.id.header_text_secondary_divider);
mFeedbackIcon = findViewById(R.id.feedback);
}
@@ -125,48 +137,37 @@
maxChildHeight = Math.max(maxChildHeight, childHeight);
}
+ mViewsToDisappear.clear();
// Ensure that there is at least enough space for the icons
int endMargin = Math.max(mHeaderTextMarginEnd, getPaddingEnd());
if (totalWidth > givenWidth - endMargin) {
int overFlow = totalWidth - givenWidth + endMargin;
- // First shrink the app name, down to a minimum size
- overFlow = shrinkViewForOverflow(heightSpec, overFlow, mAppName, mChildMinWidth);
-
- // Next, shrink the header text (this usually has subText)
- // This shrinks the subtext first, but not all the way (yet!)
- overFlow = shrinkViewForOverflow(heightSpec, overFlow, mHeaderText, mChildMinWidth);
-
- // Next, shrink the secondary header text (this rarely has conversationTitle)
- overFlow = shrinkViewForOverflow(heightSpec, overFlow, mSecondaryHeaderText, 0);
-
- // Next, shrink the title text (this has contentTitle; only in headerless views)
- overFlow = shrinkViewForOverflow(heightSpec, overFlow, mTitle, mChildMinWidth);
-
- // Finally, if there is still overflow, shrink the header down to 0 if still necessary.
- shrinkViewForOverflow(heightSpec, overFlow, mHeaderText, 0);
+ mOverflowAdjuster.resetForOverflow(overFlow, heightSpec)
+ // First shrink the app name, down to a minimum size
+ .adjust(mAppName, null, mChildMinWidth)
+ // Next, shrink the header text (this usually has subText)
+ // This shrinks the subtext first, but not all the way (yet!)
+ .adjust(mHeaderText, mHeaderTextDivider, mChildMinWidth)
+ // Next, shrink the secondary header text (this rarely has conversationTitle)
+ .adjust(mSecondaryHeaderText, mSecondaryHeaderTextDivider, 0)
+ // Next, shrink the title text (this has contentTitle; only in headerless views)
+ .adjust(mTitle, null, mChildMinWidth)
+ // Next, shrink the header down to 0 if still necessary.
+ .adjust(mHeaderText, mHeaderTextDivider, 0)
+ // Finally, shrink the title to 0 if necessary (media is super cramped)
+ .adjust(mTitle, null, 0)
+ // Clean up
+ .finish();
}
setMeasuredDimension(givenWidth, wrapHeight ? maxChildHeight : givenHeight);
}
- private int shrinkViewForOverflow(int heightSpec, int overFlow, View targetView,
- int minimumWidth) {
- if (targetView != null) {
- final int oldWidth = targetView.getMeasuredWidth();
- if (overFlow > 0 && targetView.getVisibility() != GONE && oldWidth > minimumWidth) {
- // we're still too big
- int newSize = Math.max(minimumWidth, oldWidth - overFlow);
- int childWidthSpec = MeasureSpec.makeMeasureSpec(newSize, MeasureSpec.AT_MOST);
- targetView.measure(childWidthSpec, heightSpec);
- overFlow -= oldWidth - newSize;
- }
- }
- return overFlow;
- }
-
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
- int left = getPaddingStart();
+ final boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+ final int width = getWidth();
+ int start = getPaddingStart();
int childCount = getChildCount();
int ownHeight = b - t;
int childSpace = ownHeight - mPaddingTop - mPaddingBottom;
@@ -182,8 +183,6 @@
}
int childHeight = child.getMeasuredHeight();
MarginLayoutParams params = (MarginLayoutParams) child.getLayoutParams();
- int layoutLeft;
- int layoutRight;
// Calculate vertical alignment of the views, accounting for the view baselines
int childTop;
@@ -219,19 +218,16 @@
default:
childTop = mPaddingTop;
}
-
- left += params.getMarginStart();
- int right = left + child.getMeasuredWidth();
- layoutLeft = left;
- layoutRight = right;
- left = right + params.getMarginEnd();
-
- if (getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
- int ltrLeft = layoutLeft;
- layoutLeft = getWidth() - layoutRight;
- layoutRight = getWidth() - ltrLeft;
+ if (mViewsToDisappear.contains(child)) {
+ child.layout(start, childTop, start, childTop + childHeight);
+ } else {
+ start += params.getMarginStart();
+ int end = start + child.getMeasuredWidth();
+ int layoutLeft = isRtl ? width - end : start;
+ int layoutRight = isRtl ? width - start : end;
+ start = end + params.getMarginEnd();
+ child.layout(layoutLeft, childTop, layoutRight, childTop + childHeight);
}
- child.layout(layoutLeft, childTop, layoutRight, childTop + childHeight);
}
updateTouchListener();
}
@@ -400,4 +396,83 @@
}
return mTouchListener.onTouchUp(upX, upY, downX, downY);
}
+
+ private final class OverflowAdjuster {
+ private int mOverflow;
+ private int mHeightSpec;
+ private View mRegrowView;
+
+ OverflowAdjuster resetForOverflow(int overflow, int heightSpec) {
+ mOverflow = overflow;
+ mHeightSpec = heightSpec;
+ mRegrowView = null;
+ return this;
+ }
+
+ /**
+ * Shrink the targetView's width by up to overFlow, down to minimumWidth.
+ * @param targetView the view to shrink the width of
+ * @param targetDivider a divider view which should be set to 0 width if the targetView is
+ * @param minimumWidth the minimum width allowed for the targetView
+ * @return this object
+ */
+ OverflowAdjuster adjust(View targetView, View targetDivider, int minimumWidth) {
+ if (mOverflow <= 0 || targetView == null || targetView.getVisibility() == View.GONE) {
+ return this;
+ }
+ final int oldWidth = targetView.getMeasuredWidth();
+ if (oldWidth <= minimumWidth) {
+ return this;
+ }
+ // we're too big
+ int newSize = Math.max(minimumWidth, oldWidth - mOverflow);
+ if (minimumWidth == 0 && newSize < mChildHideWidth
+ && mRegrowView != null && mRegrowView != targetView) {
+ // View is so small it's better to hide it entirely (and its divider and margins)
+ // so we can give that space back to another previously shrunken view.
+ newSize = 0;
+ }
+
+ int childWidthSpec = MeasureSpec.makeMeasureSpec(newSize, MeasureSpec.AT_MOST);
+ targetView.measure(childWidthSpec, mHeightSpec);
+ mOverflow -= oldWidth - newSize;
+
+ if (newSize == 0) {
+ mViewsToDisappear.add(targetView);
+ mOverflow -= getHorizontalMargins(targetView);
+ if (targetDivider != null && targetDivider.getVisibility() != GONE) {
+ mViewsToDisappear.add(targetDivider);
+ int oldDividerWidth = targetDivider.getMeasuredWidth();
+ int dividerWidthSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.AT_MOST);
+ targetDivider.measure(dividerWidthSpec, mHeightSpec);
+ mOverflow -= (oldDividerWidth + getHorizontalMargins(targetDivider));
+ }
+ }
+ if (mOverflow < 0 && mRegrowView != null) {
+ // We're now under-flowing, so regrow the last view.
+ final int regrowCurrentSize = mRegrowView.getMeasuredWidth();
+ final int maxSize = regrowCurrentSize - mOverflow;
+ int regrowWidthSpec = MeasureSpec.makeMeasureSpec(maxSize, MeasureSpec.AT_MOST);
+ mRegrowView.measure(regrowWidthSpec, mHeightSpec);
+ finish();
+ return this;
+ }
+
+ if (newSize != 0) {
+ // if we shrunk this view (but did not completely hide it) store it for potential
+ // re-growth if we proactively shorten a future view.
+ mRegrowView = targetView;
+ }
+ return this;
+ }
+
+ void finish() {
+ resetForOverflow(0, 0);
+ }
+
+ private int getHorizontalMargins(View view) {
+ MarginLayoutParams params = (MarginLayoutParams) view.getLayoutParams();
+ return params.getMarginStart() + params.getMarginEnd();
+ }
+ }
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 66ee23a1..dbccf10 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1370,17 +1370,10 @@
// can be used by code on the system process to escape that and enable
// HW accelerated drawing. (This is basically for the lock screen.)
- final boolean fakeHwAccelerated = (attrs.privateFlags &
- WindowManager.LayoutParams.PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED) != 0;
final boolean forceHwAccelerated = (attrs.privateFlags &
WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED) != 0;
- if (fakeHwAccelerated) {
- // This is exclusively for the preview windows the window manager
- // shows for launching applications, so they will look more like
- // the app being launched.
- mAttachInfo.mHardwareAccelerationRequested = true;
- } else if (ThreadedRenderer.sRendererEnabled || forceHwAccelerated) {
+ if (ThreadedRenderer.sRendererEnabled || forceHwAccelerated) {
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mThreadedRenderer.destroy();
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 04512c9..9df87dc 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -2218,26 +2218,6 @@
public int flags;
/**
- * If the window has requested hardware acceleration, but this is not
- * allowed in the process it is in, then still render it as if it is
- * hardware accelerated. This is used for the starting preview windows
- * in the system process, which don't need to have the overhead of
- * hardware acceleration (they are just a static rendering), but should
- * be rendered as such to match the actual window of the app even if it
- * is hardware accelerated.
- * Even if the window isn't hardware accelerated, still do its rendering
- * as if it was.
- * Like {@link #FLAG_HARDWARE_ACCELERATED} except for trusted system windows
- * that need hardware acceleration (e.g. LockScreen), where hardware acceleration
- * is generally disabled. This flag must be specified in addition to
- * {@link #FLAG_HARDWARE_ACCELERATED} to enable hardware acceleration for system
- * windows.
- *
- * @hide
- */
- public static final int PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED = 0x00000001;
-
- /**
* In the system process, we globally do not use hardware acceleration
* because there are many threads doing UI there and they conflict.
* If certain parts of the UI that really do want to use hardware
@@ -2463,7 +2443,6 @@
* @hide
*/
@IntDef(flag = true, prefix="PRIVATE_FLAG_", value = {
- PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED,
PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED,
PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS,
SYSTEM_FLAG_SHOW_FOR_ALL_USERS,
@@ -2499,10 +2478,6 @@
@UnsupportedAppUsage
@ViewDebug.ExportedProperty(flagMapping = {
@ViewDebug.FlagToString(
- mask = PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED,
- equals = PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED,
- name = "FAKE_HARDWARE_ACCELERATED"),
- @ViewDebug.FlagToString(
mask = PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED,
equals = PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED,
name = "FORCE_HARDWARE_ACCELERATED"),
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index 37220fe..c5bce28 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -608,7 +608,12 @@
Preconditions.checkArgumentNonnegative(afterLength);
final Editable content = getEditable();
- if (content == null) return null;
+ // If {@link #getEditable()} is null or {@code mEditable} is equal to {@link #getEditable()}
+ // (a.k.a, a fake editable), it means we cannot get valid content from the editable, so
+ // fallback to retrieve surrounding text from other APIs.
+ if (content == null || mEditable == content) {
+ return InputConnection.super.getSurroundingText(beforeLength, afterLength, flags);
+ }
int selStart = Selection.getSelectionStart(content);
int selEnd = Selection.getSelectionEnd(content);
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 34a60bb..38019c9 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -325,16 +325,16 @@
CharSequence textBeforeCursor = getTextBeforeCursor(beforeLength, flags);
if (textBeforeCursor == null) {
- textBeforeCursor = "";
+ return null;
+ }
+ CharSequence textAfterCursor = getTextAfterCursor(afterLength, flags);
+ if (textAfterCursor == null) {
+ return null;
}
CharSequence selectedText = getSelectedText(flags);
if (selectedText == null) {
selectedText = "";
}
- CharSequence textAfterCursor = getTextAfterCursor(afterLength, flags);
- if (textAfterCursor == null) {
- textAfterCursor = "";
- }
CharSequence surroundingText =
TextUtils.concat(textBeforeCursor, selectedText, textAfterCursor);
return new SurroundingText(surroundingText, textBeforeCursor.length(),
diff --git a/core/java/android/window/SplashScreenView.java b/core/java/android/window/SplashScreenView.java
index 3709aa1..616a0d0 100644
--- a/core/java/android/window/SplashScreenView.java
+++ b/core/java/android/window/SplashScreenView.java
@@ -15,6 +15,7 @@
*/
package android.window;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import android.annotation.ColorInt;
@@ -32,6 +33,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
+import android.os.Trace;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
@@ -183,6 +185,7 @@
* Create SplashScreenWindowView object from materials.
*/
public SplashScreenView build() {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "SplashScreenView#build");
final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
final SplashScreenView view = (SplashScreenView)
layoutInflater.inflate(R.layout.splash_screen_view, null, false);
@@ -226,6 +229,7 @@
+ view.mBrandingImageView + " drawable: " + mBrandingDrawable
+ " size w: " + mBrandingImageWidth + " h: " + mBrandingImageHeight);
}
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
return view;
}
}
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 02cbccc..a5b894d 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -130,12 +130,6 @@
// Flags related to media notifications
/**
- * (boolean) If {@code true}, enables the seekbar in compact media notifications.
- */
- public static final String COMPACT_MEDIA_SEEKBAR_ENABLED =
- "compact_media_notification_seekbar_enabled";
-
- /**
* (int) Maximum number of days to retain the salt for hashing direct share targets in logging
*/
public static final String HASH_SALT_MAX_DAYS = "hash_salt_max_days";
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 7f87885..a043756 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -89,6 +89,7 @@
import android.util.Printer;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseDoubleArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
import android.util.TimeUtils;
@@ -12548,81 +12549,6 @@
}
/**
- * SparseDoubleArray map integers to doubles.
- * Its implementation is the same as that of {@link SparseLongArray}; see there for details.
- *
- * @see SparseLongArray
- */
- private static class SparseDoubleArray {
- /**
- * The int->double map, but storing the doubles as longs using
- * {@link Double.doubleToRawLongBits(double)}.
- */
- private final SparseLongArray mValues = new SparseLongArray();
-
- /**
- * Gets the double mapped from the specified key, or <code>0</code>
- * if no such mapping has been made.
- */
- public double get(int key) {
- if (mValues.indexOfKey(key) >= 0) {
- return Double.longBitsToDouble(mValues.get(key));
- }
- return 0;
- }
-
- /**
- * Adds a mapping from the specified key to the specified value,
- * replacing the previous mapping from the specified key if there
- * was one.
- */
- public void put(int key, double value) {
- mValues.put(key, Double.doubleToRawLongBits(value));
- }
-
- /**
- * Adds a mapping from the specified key to the specified value,
- * <b>adding</b> to the previous mapping from the specified key if there
- * was one.
- */
- public void add(int key, double summand) {
- final double oldValue = get(key);
- put(key, oldValue + summand);
- }
-
- /**
- * Returns the number of key-value mappings that this SparseDoubleArray
- * currently stores.
- */
- public int size() {
- return mValues.size();
- }
-
- /**
- * Given an index in the range <code>0...size()-1</code>, returns
- * the key from the <code>index</code>th key-value mapping that this
- * SparseDoubleArray stores.
- *
- * @see SparseLongArray#keyAt(int)
- */
- public int keyAt(int index) {
- return mValues.keyAt(index);
- }
-
- /**
- * Given an index in the range <code>0...size()-1</code>, returns
- * the value from the <code>index</code>th key-value mapping that this
- * SparseDoubleArray stores.
- *
- * @see SparseLongArray#valueAt(int)
- */
- public double valueAt(int index) {
- return Double.longBitsToDouble(mValues.valueAt(index));
- }
-
- }
-
- /**
* Read and record Rail Energy data.
*/
public void updateRailStatsLocked() {
diff --git a/core/java/com/android/internal/widget/MediaNotificationView.java b/core/java/com/android/internal/widget/MediaNotificationView.java
index f42d5da..8ff3c10 100644
--- a/core/java/com/android/internal/widget/MediaNotificationView.java
+++ b/core/java/com/android/internal/widget/MediaNotificationView.java
@@ -19,31 +19,19 @@
import android.annotation.Nullable;
import android.content.Context;
import android.util.AttributeSet;
-import android.view.NotificationHeaderView;
-import android.view.View;
-import android.view.ViewGroup;
import android.widget.FrameLayout;
-import android.widget.ImageView;
import android.widget.RemoteViews;
import java.util.ArrayList;
/**
- * A TextView that can float around an image on the end.
+ * The Layout class which handles template details for the Notification.MediaStyle
*
* @hide
*/
@RemoteViews.RemoteView
public class MediaNotificationView extends FrameLayout {
- private final int mNotificationContentMarginEnd;
- private final int mNotificationContentImageMarginEnd;
- private ImageView mRightIcon;
- private View mActions;
- private NotificationHeaderView mHeader;
- private View mMainColumn;
- private View mMediaContent;
- private int mImagePushIn;
private ArrayList<VisibilityChangeListener> mListeners;
public MediaNotificationView(Context context) {
@@ -58,120 +46,14 @@
this(context, attrs, defStyleAttr, 0);
}
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- boolean hasIcon = mRightIcon.getVisibility() != GONE;
- if (!hasIcon) {
- resetHeaderIndention();
- }
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- int mode = MeasureSpec.getMode(widthMeasureSpec);
- boolean reMeasure = false;
- mImagePushIn = 0;
- if (hasIcon && mode != MeasureSpec.UNSPECIFIED) {
- int size = MeasureSpec.getSize(widthMeasureSpec);
- size = size - mActions.getMeasuredWidth();
- ViewGroup.MarginLayoutParams layoutParams =
- (MarginLayoutParams) mRightIcon.getLayoutParams();
- int imageEndMargin = layoutParams.getMarginEnd();
- size -= imageEndMargin;
- int fullHeight = mMediaContent.getMeasuredHeight();
- if (size > fullHeight) {
- size = fullHeight;
- } else if (size < fullHeight) {
- size = Math.max(0, size);
- mImagePushIn = fullHeight - size;
- }
- if (layoutParams.width != fullHeight || layoutParams.height != fullHeight) {
- layoutParams.width = fullHeight;
- layoutParams.height = fullHeight;
- mRightIcon.setLayoutParams(layoutParams);
- reMeasure = true;
- }
-
- // lets ensure that the main column doesn't run into the image
- ViewGroup.MarginLayoutParams params
- = (MarginLayoutParams) mMainColumn.getLayoutParams();
- int marginEnd = size + imageEndMargin + mNotificationContentMarginEnd;
- if (marginEnd != params.getMarginEnd()) {
- params.setMarginEnd(marginEnd);
- mMainColumn.setLayoutParams(params);
- reMeasure = true;
- }
- // TODO(b/172652345): validate all this logic (especially positioning of expand button)
- // margin for the entire header line
- int headerMarginEnd = imageEndMargin;
- // margin for the header text (not including the expand button and other icons)
- int headerExtraMarginEnd = Math.max(0,
- size + imageEndMargin - mHeader.getTopLineBaseMarginEnd());
- if (headerExtraMarginEnd != mHeader.getTopLineExtraMarginEnd()) {
- mHeader.setTopLineExtraMarginEnd(headerExtraMarginEnd);
- reMeasure = true;
- }
- params = (MarginLayoutParams) mHeader.getLayoutParams();
- if (params.getMarginEnd() != headerMarginEnd) {
- params.setMarginEnd(headerMarginEnd);
- mHeader.setLayoutParams(params);
- reMeasure = true;
- }
- if (mHeader.getPaddingEnd() != mNotificationContentImageMarginEnd) {
- mHeader.setPaddingRelative(mHeader.getPaddingStart(),
- mHeader.getPaddingTop(),
- mNotificationContentImageMarginEnd,
- mHeader.getPaddingBottom());
- reMeasure = true;
- }
- }
- if (reMeasure) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- if (mImagePushIn > 0) {
- if (this.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
- mImagePushIn *= -1;
- }
- mRightIcon.layout(mRightIcon.getLeft() + mImagePushIn, mRightIcon.getTop(),
- mRightIcon.getRight() + mImagePushIn, mRightIcon.getBottom());
- }
- }
-
- private void resetHeaderIndention() {
- if (mHeader.getPaddingEnd() != mNotificationContentMarginEnd) {
- mHeader.setPaddingRelative(mHeader.getPaddingStart(),
- mHeader.getPaddingTop(),
- mNotificationContentMarginEnd,
- mHeader.getPaddingBottom());
- }
- ViewGroup.MarginLayoutParams headerParams =
- (MarginLayoutParams) mHeader.getLayoutParams();
- headerParams.setMarginEnd(0);
- if (headerParams.getMarginEnd() != 0) {
- headerParams.setMarginEnd(0);
- mHeader.setLayoutParams(headerParams);
- }
- }
-
public MediaNotificationView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- mNotificationContentMarginEnd = context.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.notification_content_margin_end);
- mNotificationContentImageMarginEnd = context.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.notification_content_image_margin_end);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mRightIcon = findViewById(com.android.internal.R.id.right_icon);
- mActions = findViewById(com.android.internal.R.id.media_actions);
- mHeader = findViewById(com.android.internal.R.id.notification_header);
- mMainColumn = findViewById(com.android.internal.R.id.notification_main_column);
- mMediaContent = findViewById(com.android.internal.R.id.notification_media_content);
}
@Override
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 29fa70d..8d7f542 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3554,7 +3554,7 @@
@hide
-->
<permission android:name="android.permission.GET_TOP_ACTIVITY_INFO"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|recents" />
<!-- Allows an application to retrieve the current state of keys and
switches.
diff --git a/core/res/res/layout/notification_material_media_action.xml b/core/res/res/layout/notification_material_media_action.xml
index dd79a0b..5f1b60e 100644
--- a/core/res/res/layout/notification_material_media_action.xml
+++ b/core/res/res/layout/notification_material_media_action.xml
@@ -24,7 +24,6 @@
android:paddingTop="8dp"
android:paddingStart="8dp"
android:paddingEnd="8dp"
- android:layout_marginEnd="2dp"
android:gravity="center"
android:background="@drawable/notification_material_media_action_background"
android:visibility="gone"
diff --git a/core/res/res/layout/notification_material_media_seekbar.xml b/core/res/res/layout/notification_material_media_seekbar.xml
deleted file mode 100644
index 4aa8acc..0000000
--- a/core/res/res/layout/notification_material_media_seekbar.xml
+++ /dev/null
@@ -1,65 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/notification_media_progress"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:layout_alignParentBottom="true"
- >
- <SeekBar android:id="@+id/notification_media_progress_bar"
- style="@style/Widget.ProgressBar.Horizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:maxHeight="3dp"
- android:paddingTop="24dp"
- android:paddingBottom="24dp"
- android:clickable="true"
- android:layout_marginBottom="-24dp"
- android:layout_marginTop="-12dp"
- android:splitTrack="false"
- />
- <FrameLayout
- android:id="@+id/notification_media_progress_time"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_marginBottom="11dp"
- >
-
- <!-- width is set to "match_parent" to avoid extra layout calls -->
- <TextView android:id="@+id/notification_media_elapsed_time"
- style="@style/Widget.DeviceDefault.Notification.Text"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_marginStart="@dimen/notification_content_margin_start"
- android:gravity="start"
- />
-
- <TextView android:id="@+id/notification_media_total_time"
- style="@style/Widget.DeviceDefault.Notification.Text"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
- android:gravity="end"
- />
- </FrameLayout>
-</LinearLayout>
\ No newline at end of file
diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml
index bad9a6b..e644cd5 100644
--- a/core/res/res/layout/notification_template_material_base.xml
+++ b/core/res/res/layout/notification_template_material_base.xml
@@ -46,20 +46,6 @@
android:padding="@dimen/notification_icon_circle_padding"
/>
- <ImageView
- android:id="@+id/right_icon"
- android:layout_width="@dimen/notification_right_icon_size"
- android:layout_height="@dimen/notification_right_icon_size"
- android:layout_gravity="center_vertical|end"
- android:layout_marginTop="@dimen/notification_right_icon_headerless_margin"
- android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin"
- android:layout_marginEnd="@dimen/notification_header_expand_icon_size"
- android:background="@drawable/notification_large_icon_outline"
- android:clipToOutline="true"
- android:importantForAccessibility="no"
- android:scaleType="centerCrop"
- />
-
<FrameLayout
android:id="@+id/alternate_expand_target"
android:layout_width="@dimen/notification_content_margin_start"
@@ -68,95 +54,116 @@
android:importantForAccessibility="no"
/>
- <FrameLayout
- android:id="@+id/expand_button_touch_container"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_gravity="end">
-
- <include layout="@layout/notification_expand_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical|end"
- />
-
- </FrameLayout>
-
<LinearLayout
- android:id="@+id/notification_headerless_view_column"
+ android:id="@+id/notification_headerless_view_row"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginBottom="@dimen/notification_headerless_margin_twoline"
- android:layout_marginTop="@dimen/notification_headerless_margin_twoline"
- android:orientation="vertical"
+ android:layout_height="match_parent"
+ android:layout_marginStart="@dimen/notification_content_margin_start"
+ android:orientation="horizontal"
>
- <!-- extends ViewGroup -->
- <NotificationTopLineView
- android:id="@+id/notification_top_line"
- android:layout_width="wrap_content"
- android:layout_height="@dimen/notification_headerless_line_height"
- android:layout_marginEnd="@dimen/notification_heading_margin_end"
- android:layout_marginStart="@dimen/notification_content_margin_start"
- android:clipChildren="false"
- android:theme="@style/Theme.DeviceDefault.Notification"
- >
-
- <!--
- NOTE: The notification_top_line_views layout contains the app_name_text.
- In order to include the title view at the beginning, the Notification.Builder
- has logic to hide that view whenever this title view is to be visible.
- -->
-
- <TextView
- android:id="@+id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/notification_header_separating_margin"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal"
- android:singleLine="true"
- android:textAlignment="viewStart"
- android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
- />
-
- <include layout="@layout/notification_top_line_views" />
-
- </NotificationTopLineView>
-
<LinearLayout
- android:id="@+id/notification_main_column"
- android:layout_width="match_parent"
+ android:id="@+id/notification_headerless_view_column"
+ android:layout_width="0px"
android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/notification_heading_margin_end"
- android:layout_marginStart="@dimen/notification_content_margin_start"
+ android:layout_gravity="center_vertical"
+ android:layout_weight="1"
+ android:layout_marginBottom="@dimen/notification_headerless_margin_twoline"
+ android:layout_marginTop="@dimen/notification_headerless_margin_twoline"
android:orientation="vertical"
>
- <com.android.internal.widget.NotificationVanishingFrameLayout
- android:layout_width="match_parent"
+ <NotificationTopLineView
+ android:id="@+id/notification_top_line"
+ android:layout_width="wrap_content"
android:layout_height="@dimen/notification_headerless_line_height"
+ android:clipChildren="false"
+ android:theme="@style/Theme.DeviceDefault.Notification"
>
- <!-- This is the simplest way to keep this text vertically centered without using
- gravity="center_vertical" which causes jumpiness in expansion animations. -->
- <include
- layout="@layout/notification_template_text"
- android:layout_width="match_parent"
- android:layout_height="@dimen/notification_text_height"
- android:layout_gravity="center_vertical"
- android:layout_marginTop="0dp"
- />
- </com.android.internal.widget.NotificationVanishingFrameLayout>
- <include
- layout="@layout/notification_template_progress"
+ <!--
+ NOTE: The notification_top_line_views layout contains the app_name_text.
+ In order to include the title view at the beginning, the Notification.Builder
+ has logic to hide that view whenever this title view is to be visible.
+ -->
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/notification_header_separating_margin"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ android:singleLine="true"
+ android:textAlignment="viewStart"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
+ />
+
+ <include layout="@layout/notification_top_line_views" />
+
+ </NotificationTopLineView>
+
+ <LinearLayout
+ android:id="@+id/notification_main_column"
android:layout_width="match_parent"
- android:layout_height="@dimen/notification_headerless_line_height"
- />
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ >
+
+ <com.android.internal.widget.NotificationVanishingFrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_headerless_line_height"
+ >
+ <!-- This is the simplest way to keep this text vertically centered without
+ gravity="center_vertical" which causes jumpiness in expansion animations. -->
+ <include
+ layout="@layout/notification_template_text"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_text_height"
+ android:layout_gravity="center_vertical"
+ android:layout_marginTop="0dp"
+ />
+ </com.android.internal.widget.NotificationVanishingFrameLayout>
+
+ <include
+ layout="@layout/notification_template_progress"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_headerless_line_height"
+ />
+
+ </LinearLayout>
</LinearLayout>
+ <ImageView
+ android:id="@+id/right_icon"
+ android:layout_width="@dimen/notification_right_icon_size"
+ android:layout_height="@dimen/notification_right_icon_size"
+ android:layout_gravity="center_vertical|end"
+ android:layout_marginTop="@dimen/notification_right_icon_headerless_margin"
+ android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin"
+ android:layout_marginStart="@dimen/notification_right_icon_content_margin"
+ android:background="@drawable/notification_large_icon_outline"
+ android:clipToOutline="true"
+ android:importantForAccessibility="no"
+ android:scaleType="centerCrop"
+ />
+
+ <FrameLayout
+ android:id="@+id/expand_button_touch_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/notification_content_margin_end"
+ >
+
+ <include layout="@layout/notification_expand_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|end"
+ />
+
+ </FrameLayout>
+
</LinearLayout>
</com.android.internal.widget.NotificationMaxHeightFrameLayout>
diff --git a/core/res/res/layout/notification_template_material_big_media.xml b/core/res/res/layout/notification_template_material_big_media.xml
index aa20ad3..ff64315 100644
--- a/core/res/res/layout/notification_template_material_big_media.xml
+++ b/core/res/res/layout/notification_template_material_big_media.xml
@@ -20,17 +20,8 @@
android:id="@+id/status_bar_latest_event_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="#00000000"
android:tag="bigMediaNarrow"
>
- <!-- The size will actually be determined at runtime -->
- <ImageView
- android:id="@+id/right_icon"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_gravity="top|end"
- android:scaleType="centerCrop"
- />
<include
layout="@layout/notification_template_header"
@@ -49,46 +40,27 @@
android:id="@+id/notification_main_column"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="46dp"
+ android:layout_marginTop="@dimen/notification_content_margin_top"
android:layout_marginStart="@dimen/notification_content_margin_start"
android:layout_marginBottom="@dimen/notification_content_margin"
android:layout_marginEnd="@dimen/notification_content_margin_end"
android:orientation="vertical"
>
- <!-- TODO(b/172652345): fix the media style -->
- <!--<include layout="@layout/notification_template_part_line1"/>-->
- <!--<include layout="@layout/notification_template_text"/>-->
-
- <TextView android:id="@+id/title"
- android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal"
- android:textAlignment="viewStart"
- />
-
- <com.android.internal.widget.ImageFloatingTextView
- style="@style/Widget.DeviceDefault.Notification.Text"
- android:id="@+id/text"
- android:layout_width="match_parent"
- android:layout_height="@dimen/notification_text_height"
- android:layout_gravity="top"
- android:layout_marginTop="0.5dp"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal"
- android:gravity="top"
- android:singleLine="true"
- android:textAlignment="viewStart"
- />
+ <include layout="@layout/notification_template_part_line1"/>
+ <include layout="@layout/notification_template_text"/>
</LinearLayout>
<LinearLayout
android:id="@+id/media_actions"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="-21dp"
+ android:paddingStart="44dp"
+ android:paddingEnd="44dp"
+ android:paddingBottom="@dimen/media_notification_actions_padding_bottom"
+ android:gravity="top"
android:orientation="horizontal"
android:layoutDirection="ltr"
- style="@style/NotificationMediaActionContainer"
>
<include
@@ -117,10 +89,8 @@
/>
</LinearLayout>
- <ViewStub
- android:id="@+id/notification_media_seekbar_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- />
</LinearLayout>
+
+ <include layout="@layout/notification_template_right_icon" />
+
</com.android.internal.widget.MediaNotificationView>
diff --git a/core/res/res/layout/notification_template_material_media.xml b/core/res/res/layout/notification_template_material_media.xml
index 542e59d..2991b17 100644
--- a/core/res/res/layout/notification_template_material_media.xml
+++ b/core/res/res/layout/notification_template_material_media.xml
@@ -19,101 +19,173 @@
android:id="@+id/status_bar_latest_event_content"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="#00000000"
+ android:layout_height="@dimen/notification_min_height"
android:tag="media"
>
- <ImageView android:id="@+id/right_icon"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:adjustViewBounds="true"
- android:layout_gravity="top|end"
+
+
+ <ImageView
+ android:id="@+id/left_icon"
+ android:layout_width="@dimen/notification_left_icon_size"
+ android:layout_height="@dimen/notification_left_icon_size"
+ android:layout_gravity="center_vertical|start"
+ android:layout_marginStart="@dimen/notification_left_icon_start"
+ android:background="@drawable/notification_large_icon_outline"
+ android:clipToOutline="true"
+ android:importantForAccessibility="no"
android:scaleType="centerCrop"
- />
- <include layout="@layout/notification_template_header"
- android:layout_width="match_parent"
- android:layout_height="@dimen/media_notification_header_height"
+ android:visibility="gone"
/>
+
+ <com.android.internal.widget.CachingIconView
+ android:id="@+id/icon"
+ android:layout_width="@dimen/notification_icon_circle_size"
+ android:layout_height="@dimen/notification_icon_circle_size"
+ android:layout_gravity="center_vertical|start"
+ android:layout_marginStart="@dimen/notification_icon_circle_start"
+ android:background="@drawable/notification_icon_circle"
+ android:padding="@dimen/notification_icon_circle_padding"
+ />
+
+ <FrameLayout
+ android:id="@+id/alternate_expand_target"
+ android:layout_width="@dimen/notification_content_margin_start"
+ android:layout_height="match_parent"
+ android:layout_gravity="start"
+ android:importantForAccessibility="no"
+ />
+
<LinearLayout
+ android:id="@+id/notification_headerless_view_row"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:id="@+id/notification_media_content"
+ android:layout_height="match_parent"
+ android:layout_marginStart="@dimen/notification_content_margin_start"
+ android:orientation="horizontal"
>
+
<LinearLayout
- android:id="@+id/notification_main_column"
- android:layout_width="match_parent"
+ android:id="@+id/notification_headerless_view_column"
+ android:layout_width="0px"
android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:layout_marginStart="@dimen/notification_content_margin_start"
- android:layout_marginTop="46dp"
- android:layout_alignParentTop="true"
- android:tag="media"
+ android:layout_gravity="center_vertical"
+ android:layout_weight="1"
+ android:layout_marginBottom="@dimen/notification_headerless_margin_twoline"
+ android:layout_marginTop="@dimen/notification_headerless_margin_twoline"
+ android:orientation="vertical"
>
+
+ <NotificationTopLineView
+ android:id="@+id/notification_top_line"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/notification_headerless_line_height"
+ android:clipChildren="false"
+ android:theme="@style/Theme.DeviceDefault.Notification"
+ >
+
+ <!--
+ NOTE: The notification_top_line_views layout contains the app_name_text.
+ In order to include the title view at the beginning, the Notification.Builder
+ has logic to hide that view whenever this title view is to be visible.
+ -->
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/notification_header_separating_margin"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ android:singleLine="true"
+ android:textAlignment="viewStart"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
+ />
+
+ <include layout="@layout/notification_top_line_views" />
+
+ </NotificationTopLineView>
+
<LinearLayout
- android:id="@+id/notification_content_container"
+ android:id="@+id/notification_main_column"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_gravity="fill_vertical"
- android:layout_weight="1"
- android:paddingBottom="@dimen/notification_content_margin"
android:orientation="vertical"
>
- <!-- TODO(b/172652345): fix the media style -->
- <!--<include layout="@layout/notification_template_part_line1"/>-->
- <!--<include layout="@layout/notification_template_text"/>-->
- <TextView android:id="@+id/title"
- android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
+ <com.android.internal.widget.NotificationVanishingFrameLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal"
- android:textAlignment="viewStart"
+ android:layout_height="@dimen/notification_headerless_line_height"
+ >
+ <!-- This is the simplest way to keep this text vertically centered without
+ gravity="center_vertical" which causes jumpiness in expansion animations. -->
+ <include
+ layout="@layout/notification_template_text"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_text_height"
+ android:layout_gravity="center_vertical"
+ android:layout_marginTop="0dp"
+ />
+ </com.android.internal.widget.NotificationVanishingFrameLayout>
+
+ <include
+ layout="@layout/notification_template_progress"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_headerless_line_height"
/>
- <com.android.internal.widget.ImageFloatingTextView
- style="@style/Widget.DeviceDefault.Notification.Text"
- android:id="@+id/text"
- android:layout_width="match_parent"
- android:layout_height="@dimen/notification_text_height"
- android:layout_gravity="top"
- android:layout_marginTop="0.5dp"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal"
- android:gravity="top"
- android:singleLine="true"
- android:textAlignment="viewStart"
- />
</LinearLayout>
- <LinearLayout
- android:id="@+id/media_actions"
+
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/right_icon"
+ android:layout_width="@dimen/notification_right_icon_size"
+ android:layout_height="@dimen/notification_right_icon_size"
+ android:layout_gravity="center_vertical|end"
+ android:layout_marginTop="@dimen/notification_right_icon_headerless_margin"
+ android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin"
+ android:layout_marginStart="@dimen/notification_right_icon_content_margin"
+ android:background="@drawable/notification_large_icon_outline"
+ android:clipToOutline="true"
+ android:importantForAccessibility="no"
+ android:scaleType="centerCrop"
+ />
+
+ <LinearLayout
+ android:id="@+id/media_actions"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layoutDirection="ltr"
+ android:orientation="horizontal"
+ >
+ <include
+ layout="@layout/notification_material_media_action"
+ android:id="@+id/action0"
+ />
+ <include
+ layout="@layout/notification_material_media_action"
+ android:id="@+id/action1"
+ />
+ <include
+ layout="@layout/notification_material_media_action"
+ android:id="@+id/action2"
+ />
+ </LinearLayout>
+
+ <FrameLayout
+ android:id="@+id/expand_button_touch_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/notification_content_margin_end"
+ >
+
+ <include layout="@layout/notification_expand_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="top|end"
- android:layout_marginStart="10dp"
- android:layoutDirection="ltr"
- android:orientation="horizontal"
- >
- <include
- layout="@layout/notification_material_media_action"
- android:id="@+id/action0"
+ android:layout_gravity="center_vertical|end"
/>
- <include
- layout="@layout/notification_material_media_action"
- android:id="@+id/action1"
- />
- <include
- layout="@layout/notification_material_media_action"
- android:id="@+id/action2"
- />
- </LinearLayout>
- </LinearLayout>
- <ViewStub android:id="@+id/notification_media_seekbar_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- />
+
+ </FrameLayout>
+
</LinearLayout>
</com.android.internal.widget.MediaNotificationView>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 43dbd38..afbbe46 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -226,9 +226,6 @@
<!-- The margin on the end of the top-line content views (accommodates the expander) -->
<dimen name="notification_heading_margin_end">56dp</dimen>
- <!-- The margin for text at the end of the image view for media notifications -->
- <dimen name="notification_media_image_margin_end">72dp</dimen>
-
<!-- The height of the notification action list -->
<dimen name="notification_action_list_height">60dp</dimen>
@@ -345,6 +342,9 @@
<!-- The minimum width of the app name in the header if it shrinks -->
<dimen name="notification_header_shrink_min_width">72dp</dimen>
+ <!-- The minimum width of optional header fields below which the view is simply hidden -->
+ <dimen name="notification_header_shrink_hide_width">24sp</dimen>
+
<!-- The size of the media actions in the media notification. -->
<dimen name="media_notification_action_button_size">48dp</dimen>
@@ -360,9 +360,6 @@
<!-- The absolute height for the header in a media notification. -->
<dimen name="media_notification_header_height">@dimen/notification_header_height</dimen>
- <!-- The margin of the content to an image-->
- <dimen name="notification_content_image_margin_end">8dp</dimen>
-
<!-- The padding at the end of actions when the snooze and bubble buttons are gone-->
<dimen name="snooze_and_bubble_gone_padding_end">12dp</dimen>
@@ -483,9 +480,6 @@
<!-- Top padding for notification when text is large and narrow (i.e. it has 3 lines -->
<dimen name="notification_top_pad_large_text_narrow">-4dp</dimen>
- <!-- Padding for notification icon when drawn with circle around it -->
- <dimen name="notification_large_icon_circle_padding">11dp</dimen>
-
<!-- The margin on top of the text of the notification -->
<dimen name="notification_text_margin_top">6dp</dimen>
@@ -736,12 +730,10 @@
<dimen name="notification_big_picture_max_height">284dp</dimen>
<!-- The maximum width of a big picture in a notification. The images will be reduced to that width in case they are bigger. This value is determined by the standard panel size -->
<dimen name="notification_big_picture_max_width">416dp</dimen>
- <!-- The maximum height of a image in a media notification. The images will be reduced to that height in case they are bigger. This value is determined by the expanded media template-->
- <dimen name="notification_media_image_max_height">140dp</dimen>
- <!-- The maximum width of a image in a media notification. The images will be reduced to that width in case they are bigger.-->
- <dimen name="notification_media_image_max_width">280dp</dimen>
<!-- The size of the right icon -->
<dimen name="notification_right_icon_size">48dp</dimen>
+ <!-- The margin between the right icon and the content. -->
+ <dimen name="notification_right_icon_content_margin">12dp</dimen>
<!-- The top and bottom margin of the right icon in the normal notification states -->
<dimen name="notification_right_icon_headerless_margin">20dp</dimen>
<!-- The top margin of the right icon in the "big" notification states -->
@@ -762,10 +754,6 @@
<dimen name="notification_big_picture_max_height_low_ram">208dp</dimen>
<!-- The maximum width of a big picture in a notification. The images will be reduced to that width in case they are bigger. -->
<dimen name="notification_big_picture_max_width_low_ram">294dp</dimen>
- <!-- The maximum height of a image in a media notification. The images will be reduced to that height in case they are bigger. -->
- <dimen name="notification_media_image_max_height_low_ram">100dp</dimen>
- <!-- The maximum width of a image in a media notification. The images will be reduced to that width in case they are bigger.-->
- <dimen name="notification_media_image_max_width_low_ram">100dp</dimen>
<!-- The size of the right icon image when on low ram -->
<dimen name="notification_right_icon_size_low_ram">@dimen/notification_right_icon_size</dimen>
<!-- The maximum size of the grayscale icon -->
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index c7ded0c..fbf67e0 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1481,17 +1481,6 @@
<item name="android:windowExitAnimation">@anim/slide_out_down</item>
</style>
- <!-- The style for the container of media actions in a notification. -->
- <!-- @hide -->
- <style name="NotificationMediaActionContainer">
- <item name="layout_width">wrap_content</item>
- <item name="layout_height">wrap_content</item>
- <item name="layout_marginTop">-21dp</item>
- <item name="paddingStart">8dp</item>
- <item name="paddingBottom">@dimen/media_notification_actions_padding_bottom</item>
- <item name="gravity">top</item>
- </style>
-
<!-- The style for normal action button on notification -->
<style name="NotificationAction" parent="Widget.Material.Light.Button.Borderless.Small">
<item name="textColor">@color/notification_action_button_text_color</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 1d74d85..d6a6f4d 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -200,12 +200,7 @@
<java-symbol type="id" name="action2" />
<java-symbol type="id" name="action3" />
<java-symbol type="id" name="action4" />
- <java-symbol type="id" name="notification_media_seekbar_container" />
<java-symbol type="id" name="notification_media_content" />
- <java-symbol type="id" name="notification_media_progress" />
- <java-symbol type="id" name="notification_media_progress_bar" />
- <java-symbol type="id" name="notification_media_elapsed_time" />
- <java-symbol type="id" name="notification_media_total_time" />
<java-symbol type="id" name="big_picture" />
<java-symbol type="id" name="big_text" />
<java-symbol type="id" name="chronometer" />
@@ -525,7 +520,6 @@
<java-symbol type="dimen" name="notification_top_pad_narrow" />
<java-symbol type="dimen" name="notification_top_pad_large_text" />
<java-symbol type="dimen" name="notification_top_pad_large_text_narrow" />
- <java-symbol type="dimen" name="notification_large_icon_circle_padding" />
<java-symbol type="dimen" name="notification_badge_size" />
<java-symbol type="dimen" name="immersive_mode_cling_width" />
<java-symbol type="dimen" name="accessibility_magnification_indicator_width" />
@@ -1564,7 +1558,6 @@
<java-symbol type="layout" name="immersive_mode_cling" />
<java-symbol type="layout" name="user_switching_dialog" />
<java-symbol type="layout" name="common_tab_settings" />
- <java-symbol type="layout" name="notification_material_media_seekbar" />
<java-symbol type="layout" name="resolver_list_per_profile" />
<java-symbol type="layout" name="chooser_list_per_profile" />
<java-symbol type="layout" name="resolver_empty_states" />
@@ -2921,6 +2914,7 @@
<java-symbol type="drawable" name="ic_expand_bundle" />
<java-symbol type="drawable" name="ic_collapse_bundle" />
<java-symbol type="dimen" name="notification_header_shrink_min_width" />
+ <java-symbol type="dimen" name="notification_header_shrink_hide_width" />
<java-symbol type="dimen" name="notification_content_margin_start" />
<java-symbol type="dimen" name="notification_content_margin_end" />
<java-symbol type="dimen" name="notification_heading_margin_end" />
@@ -3010,7 +3004,6 @@
<java-symbol type="string" name="new_sms_notification_content" />
<java-symbol type="dimen" name="media_notification_expanded_image_margin_bottom" />
- <java-symbol type="dimen" name="notification_content_image_margin_end" />
<java-symbol type="bool" name="config_strongAuthRequiredOnBoot" />
@@ -3019,8 +3012,6 @@
<java-symbol type="id" name="aerr_wait" />
- <java-symbol type="id" name="notification_content_container" />
-
<java-symbol type="plurals" name="duration_minutes_shortest" />
<java-symbol type="plurals" name="duration_hours_shortest" />
<java-symbol type="plurals" name="duration_days_shortest" />
@@ -3138,7 +3129,6 @@
<java-symbol type="bool" name="config_supportPreRebootSecurityLogs" />
- <java-symbol type="dimen" name="notification_media_image_margin_end" />
<java-symbol type="id" name="notification_action_list_margin_target" />
<java-symbol type="dimen" name="notification_action_disabled_alpha" />
<java-symbol type="id" name="tag_margin_end_when_icon_visible" />
@@ -3461,17 +3451,14 @@
<java-symbol type="dimen" name="notification_big_picture_max_height"/>
<java-symbol type="dimen" name="notification_big_picture_max_width"/>
- <java-symbol type="dimen" name="notification_media_image_max_width"/>
- <java-symbol type="dimen" name="notification_media_image_max_height"/>
<java-symbol type="dimen" name="notification_right_icon_size"/>
+ <java-symbol type="dimen" name="notification_right_icon_content_margin"/>
<java-symbol type="dimen" name="notification_actions_icon_drawable_size"/>
<java-symbol type="dimen" name="notification_custom_view_max_image_height"/>
<java-symbol type="dimen" name="notification_custom_view_max_image_width"/>
<java-symbol type="dimen" name="notification_big_picture_max_height_low_ram"/>
<java-symbol type="dimen" name="notification_big_picture_max_width_low_ram"/>
- <java-symbol type="dimen" name="notification_media_image_max_width_low_ram"/>
- <java-symbol type="dimen" name="notification_media_image_max_height_low_ram"/>
<java-symbol type="dimen" name="notification_right_icon_size_low_ram"/>
<java-symbol type="dimen" name="notification_grayscale_icon_max_size"/>
<java-symbol type="dimen" name="notification_custom_view_max_image_height_low_ram"/>
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index 252938a..0ea6364 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -16,8 +16,6 @@
package android.app;
-import static com.android.internal.util.ContrastColorUtil.satisfiesTextContrast;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotSame;
@@ -99,23 +97,6 @@
}
@Test
- public void testColorSatisfiedWhenBgDarkTextDarker() {
- Notification.Builder builder = getMediaNotification();
- Notification n = builder.build();
-
- assertTrue(n.isColorized());
-
- // An initial guess where the foreground color is actually darker than an already dark bg
- int backgroundColor = 0xff585868;
- int initialForegroundColor = 0xff505868;
- builder.setColorPalette(backgroundColor, initialForegroundColor);
- int primaryTextColor = builder.getPrimaryTextColor(builder.mParams);
- assertTrue(satisfiesTextContrast(primaryTextColor, backgroundColor));
- int secondaryTextColor = builder.getSecondaryTextColor(builder.mParams);
- assertTrue(satisfiesTextContrast(secondaryTextColor, backgroundColor));
- }
-
- @Test
public void testHasCompletedProgress_noProgress() {
Notification n = new Notification.Builder(mContext).build();
diff --git a/core/tests/coretests/src/android/util/SparseDoubleArrayTest.java b/core/tests/coretests/src/android/util/SparseDoubleArrayTest.java
new file mode 100644
index 0000000..2dd3f69
--- /dev/null
+++ b/core/tests/coretests/src/android/util/SparseDoubleArrayTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+
+/**
+ * Internal tests for {@link SparseDoubleArray}.
+ *
+ * Run using:
+ * atest FrameworksCoreTests:android.util.SparseDoubleArrayTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SparseDoubleArrayTest {
+ private static final double EXACT_PRECISION = 0;
+ private static final double PRECISION = 0.000000001;
+
+ @Test
+ public void testPutGet() {
+ final SparseDoubleArray sda = new SparseDoubleArray();
+ assertEquals("Array should be empty", 0, sda.size());
+
+ final int[] keys = {1, 6, -14, 53251, 5, -13412, 12, 0, 2};
+ final double[] values = {7, -12.4, 7, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY,
+ Double.NaN,
+ 4311236312.0 / 3431470161413514334123.0,
+ 431123636434132151313412.0 / 34323.0,
+ 0};
+ for (int i = 0; i < keys.length; i++) {
+ sda.put(keys[i], values[i]);
+ }
+
+ assertEquals("Wrong size array", keys.length, sda.size());
+ // Due to the implementation, we actually expect EXACT double equality.
+ for (int i = 0; i < keys.length; i++) {
+ assertEquals("Wrong value at index " + i, values[i], sda.get(keys[i]), EXACT_PRECISION);
+ }
+
+ // Now check something that was never put in
+ assertEquals("Wrong value for absent index", 0, sda.get(100000), EXACT_PRECISION);
+ }
+
+ @Test
+ public void testAdd() {
+ final SparseDoubleArray sda = new SparseDoubleArray();
+
+ sda.put(4, 6.1);
+ sda.add(4, -1.2);
+ sda.add(2, -1.2);
+
+ assertEquals(6.1 - 1.2, sda.get(4), PRECISION);
+ assertEquals(-1.2, sda.get(2), PRECISION);
+ }
+
+ @Test
+ public void testKeyValueAt() {
+ final SparseDoubleArray sda = new SparseDoubleArray();
+
+ final int[] keys = {1, 6, -14, 53251, 5, -13412, 12, 0, 2};
+ final double[] values = {7, -12.4, 7, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY,
+ Double.NaN,
+ 4311236312.0 / 3431470161413514334123.0,
+ 431123636434132151313412.0 / 34323.0,
+ 0};
+ for (int i = 0; i < keys.length; i++) {
+ sda.put(keys[i], values[i]);
+ }
+
+ // Sort the sample data.
+ final ArrayMap<Integer, Double> map = new ArrayMap<>(keys.length);
+ for (int i = 0; i < keys.length; i++) {
+ map.put(keys[i], values[i]);
+ }
+ final int[] sortedKeys = Arrays.copyOf(keys, keys.length);
+ Arrays.sort(sortedKeys);
+
+ for (int i = 0; i < sortedKeys.length; i++) {
+ final int expectedKey = sortedKeys[i];
+ final double expectedValue = map.get(expectedKey);
+
+ assertEquals("Wrong key at index " + i, expectedKey, sda.keyAt(i), PRECISION);
+ assertEquals("Wrong value at index " + i, expectedValue, sda.valueAt(i), PRECISION);
+ }
+ }
+}
diff --git a/data/etc/car/com.android.car.carlauncher.xml b/data/etc/car/com.android.car.carlauncher.xml
index ac16af3..abde232 100644
--- a/data/etc/car/com.android.car.carlauncher.xml
+++ b/data/etc/car/com.android.car.carlauncher.xml
@@ -18,6 +18,7 @@
<privapp-permissions package="com.android.car.carlauncher">
<permission name="android.permission.ACTIVITY_EMBEDDING"/>
<permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
+ <permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.MANAGE_USERS"/>
<permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
<permission name="android.permission.PACKAGE_USAGE_STATS"/>
diff --git a/libs/WindowManager/Shell/AndroidManifest.xml b/libs/WindowManager/Shell/AndroidManifest.xml
index d2b3cf6..6bd0e0a 100644
--- a/libs/WindowManager/Shell/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/AndroidManifest.xml
@@ -17,6 +17,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.wm.shell">
+ <!-- System permission required by WM Shell Task Organizer. -->
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<uses-permission android:name="android.permission.ROTATE_SURFACE_FLINGER" />
<uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
</manifest>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
index 4768cf5..fa94ec5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
@@ -126,7 +126,7 @@
*/
public boolean getSettingsSwipeToNotificationEnabled(ContentResolver resolver) {
return Settings.Secure.getInt(resolver,
- Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 1) == 1;
+ Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 0 /* Default OFF */) == 1;
}
void dump(PrintWriter pw, String prefix, ContentResolver resolver) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
index 3f46fee..18c6b66 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -16,6 +16,8 @@
package com.android.wm.shell.startingsurface;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+
import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.app.ActivityThread;
@@ -31,6 +33,7 @@
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.os.Build;
+import android.os.Trace;
import android.util.Slog;
import android.view.SurfaceControl;
import android.window.SplashScreenView;
@@ -70,6 +73,7 @@
private int mIconNormalExitDistance;
private int mIconEarlyExitDistance;
private final TransactionPool mTransactionPool;
+ private final SplashScreenWindowAttrs mTmpAttrs = new SplashScreenWindowAttrs();
SplashscreenContentDrawer(Context context, int maxAnimatableIconDuration,
int iconExitAnimDuration, int appRevealAnimDuration, TransactionPool pool) {
@@ -109,49 +113,82 @@
return new ColorDrawable(getSystemBGColor());
}
- SplashScreenView makeSplashScreenContentView(Context context, int iconRes,
- int splashscreenContentResId) {
- updateDensity();
- // splash screen content will be deprecated after S.
- final SplashScreenView ssc =
- makeSplashscreenContentDrawable(context, splashscreenContentResId);
-
- if (ssc != null) {
- return ssc;
- }
-
- final SplashScreenWindowAttrs attrs =
- SplashScreenWindowAttrs.createWindowAttrs(context);
- final StartingWindowViewBuilder builder = new StartingWindowViewBuilder();
+ private @ColorInt int peekWindowBGColor(Context context) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "peekWindowBGColor");
final Drawable themeBGDrawable;
-
- if (attrs.mWindowBgColor != 0) {
- themeBGDrawable = new ColorDrawable(attrs.mWindowBgColor);
- } else if (attrs.mWindowBgResId != 0) {
- themeBGDrawable = context.getDrawable(attrs.mWindowBgResId);
+ if (mTmpAttrs.mWindowBgColor != 0) {
+ themeBGDrawable = new ColorDrawable(mTmpAttrs.mWindowBgColor);
+ } else if (mTmpAttrs.mWindowBgResId != 0) {
+ themeBGDrawable = context.getDrawable(mTmpAttrs.mWindowBgResId);
} else {
Slog.w(TAG, "Window background not exist!");
themeBGDrawable = createDefaultBackgroundDrawable();
}
+ final int estimatedWindowBGColor = estimateWindowBGColor(themeBGDrawable);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ return estimatedWindowBGColor;
+ }
+
+ private int estimateWindowBGColor(Drawable themeBGDrawable) {
+ final DrawableColorTester themeBGTester =
+ new DrawableColorTester(themeBGDrawable, true /* filterTransparent */);
+ if (themeBGTester.nonTransparentRatio() == 0) {
+ // the window background is transparent, unable to draw
+ Slog.w(TAG, "Window background is transparent, fill background with black color");
+ return getSystemBGColor();
+ } else {
+ return themeBGTester.getDominateColor();
+ }
+ }
+
+ SplashScreenView makeSplashScreenContentView(Context context, int iconRes) {
+ updateDensity();
+
+ getWindowAttrs(context, mTmpAttrs);
+ final StartingWindowViewBuilder builder = new StartingWindowViewBuilder();
final int animationDuration;
final Drawable iconDrawable;
- if (attrs.mReplaceIcon != null) {
- iconDrawable = attrs.mReplaceIcon;
+ if (mTmpAttrs.mReplaceIcon != null) {
+ iconDrawable = mTmpAttrs.mReplaceIcon;
animationDuration = Math.max(0,
- Math.min(attrs.mAnimationDuration, mMaxAnimatableIconDuration));
+ Math.min(mTmpAttrs.mAnimationDuration, mMaxAnimatableIconDuration));
} else {
iconDrawable = iconRes != 0 ? context.getDrawable(iconRes)
: context.getPackageManager().getDefaultActivityIcon();
animationDuration = 0;
}
+ final int themeBGColor = peekWindowBGColor(context);
// TODO (b/173975965) Tracking the performance on improved splash screen.
return builder
.setContext(context)
- .setThemeDrawable(themeBGDrawable)
+ .setWindowBGColor(themeBGColor)
.setIconDrawable(iconDrawable)
.setIconAnimationDuration(animationDuration)
- .setBrandingDrawable(attrs.mBrandingImage)
- .setIconBackground(attrs.mIconBgColor).build();
+ .setBrandingDrawable(mTmpAttrs.mBrandingImage)
+ .setIconBackground(mTmpAttrs.mIconBgColor).build();
+ }
+
+ private static void getWindowAttrs(Context context, SplashScreenWindowAttrs attrs) {
+ final TypedArray typedArray = context.obtainStyledAttributes(
+ com.android.internal.R.styleable.Window);
+ attrs.mWindowBgResId = typedArray.getResourceId(R.styleable.Window_windowBackground, 0);
+ attrs.mWindowBgColor = typedArray.getColor(
+ R.styleable.Window_windowSplashScreenBackground, Color.TRANSPARENT);
+ attrs.mReplaceIcon = typedArray.getDrawable(
+ R.styleable.Window_windowSplashScreenAnimatedIcon);
+ attrs.mAnimationDuration = typedArray.getInt(
+ R.styleable.Window_windowSplashScreenAnimationDuration, 0);
+ attrs.mBrandingImage = typedArray.getDrawable(
+ R.styleable.Window_windowSplashScreenBrandingImage);
+ attrs.mIconBgColor = typedArray.getColor(
+ R.styleable.Window_windowSplashScreenIconBackgroundColor, Color.TRANSPARENT);
+ typedArray.recycle();
+ if (DEBUG) {
+ Slog.d(TAG, "window attributes color: "
+ + Integer.toHexString(attrs.mWindowBgColor)
+ + " icon " + attrs.mReplaceIcon + " duration " + attrs.mAnimationDuration
+ + " brandImage " + attrs.mBrandingImage);
+ }
}
static class SplashScreenWindowAttrs {
@@ -161,35 +198,9 @@
private Drawable mBrandingImage = null;
private int mIconBgColor = Color.TRANSPARENT;
private int mAnimationDuration = 0;
-
- static SplashScreenWindowAttrs createWindowAttrs(Context context) {
- final SplashScreenWindowAttrs attrs = new SplashScreenWindowAttrs();
- final TypedArray typedArray = context.obtainStyledAttributes(
- com.android.internal.R.styleable.Window);
- attrs.mWindowBgResId = typedArray.getResourceId(R.styleable.Window_windowBackground, 0);
- attrs.mWindowBgColor = typedArray.getColor(
- R.styleable.Window_windowSplashScreenBackground, Color.TRANSPARENT);
- attrs.mReplaceIcon = typedArray.getDrawable(
- R.styleable.Window_windowSplashScreenAnimatedIcon);
- attrs.mAnimationDuration = typedArray.getInt(
- R.styleable.Window_windowSplashScreenAnimationDuration, 0);
- attrs.mBrandingImage = typedArray.getDrawable(
- R.styleable.Window_windowSplashScreenBrandingImage);
- attrs.mIconBgColor = typedArray.getColor(
- R.styleable.Window_windowSplashScreenIconBackgroundColor, Color.TRANSPARENT);
- typedArray.recycle();
- if (DEBUG) {
- Slog.d(TAG, "window attributes color: "
- + Integer.toHexString(attrs.mWindowBgColor)
- + " icon " + attrs.mReplaceIcon + " duration " + attrs.mAnimationDuration
- + " brandImage " + attrs.mBrandingImage);
- }
- return attrs;
- }
}
private class StartingWindowViewBuilder {
- private Drawable mThemeBGDrawable;
private Drawable mIconDrawable;
private int mIconAnimationDuration;
private Context mContext;
@@ -203,8 +214,8 @@
private Drawable mFinalIconDrawable;
private float mScale = 1f;
- StartingWindowViewBuilder setThemeDrawable(Drawable background) {
- mThemeBGDrawable = background;
+ StartingWindowViewBuilder setWindowBGColor(@ColorInt int background) {
+ mThemeColor = background;
mBuildComplete = false;
return this;
}
@@ -247,18 +258,14 @@
Slog.e(TAG, "Unable to create StartingWindowView, lack of materials!");
return null;
}
- if (mThemeBGDrawable == null) {
- Slog.w(TAG, "Theme Background Drawable is null, forget to set Theme Drawable?");
- mThemeBGDrawable = createDefaultBackgroundDrawable();
- }
- processThemeColor();
+
if (!processAdaptiveIcon() && mIconDrawable != null) {
if (DEBUG) {
Slog.d(TAG, "The icon is not an AdaptiveIconDrawable");
}
mFinalIconDrawable = SplashscreenIconDrawableFactory.makeIconDrawable(
mIconBackground != Color.TRANSPARENT
- ? mIconBackground : mThemeColor, mIconDrawable);
+ ? mIconBackground : mThemeColor, mIconDrawable, mIconSize);
}
final int iconSize = mFinalIconDrawable != null ? (int) (mIconSize * mScale) : 0;
mCachedResult = fillViewWithIcon(mContext, iconSize, mFinalIconDrawable);
@@ -266,22 +273,10 @@
return mCachedResult;
}
- private void createIconDrawable(Drawable iconDrawable) {
+ private void createIconDrawable(Drawable iconDrawable, int iconSize) {
mFinalIconDrawable = SplashscreenIconDrawableFactory.makeIconDrawable(
mIconBackground != Color.TRANSPARENT
- ? mIconBackground : mThemeColor, iconDrawable);
- }
-
- private void processThemeColor() {
- final DrawableColorTester themeBGTester =
- new DrawableColorTester(mThemeBGDrawable, true /* filterTransparent */);
- if (themeBGTester.nonTransparentRatio() == 0) {
- // the window background is transparent, unable to draw
- Slog.w(TAG, "Window background is transparent, fill background with black color");
- mThemeColor = getSystemBGColor();
- } else {
- mThemeColor = themeBGTester.getDominateColor();
- }
+ ? mIconBackground : mThemeColor, iconDrawable, iconSize);
}
private boolean processAdaptiveIcon() {
@@ -289,6 +284,7 @@
return false;
}
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "processAdaptiveIcon");
final AdaptiveIconDrawable adaptiveIconDrawable = (AdaptiveIconDrawable) mIconDrawable;
final DrawableColorTester backIconTester =
new DrawableColorTester(adaptiveIconDrawable.getBackground());
@@ -325,29 +321,28 @@
if (DEBUG) {
Slog.d(TAG, "makeSplashScreenContentView: choose fg icon");
}
- // Using AdaptiveIconDrawable here can help keep the shape consistent with the
- // current settings.
- createIconDrawable(iconForeground);
// Reference AdaptiveIcon description, outer is 108 and inner is 72, so we
// should enlarge the size 108/72 if we only draw adaptiveIcon's foreground.
if (foreIconTester.nonTransparentRatio() < ENLARGE_FOREGROUND_ICON_THRESHOLD) {
mScale = 1.5f;
}
+ // Using AdaptiveIconDrawable here can help keep the shape consistent with the
+ // current settings.
+ final int iconSize = (int) (0.5f + mIconSize * mScale);
+ createIconDrawable(iconForeground, iconSize);
} else {
if (DEBUG) {
Slog.d(TAG, "makeSplashScreenContentView: draw whole icon");
}
- if (mIconBackground != Color.TRANSPARENT) {
- createIconDrawable(adaptiveIconDrawable);
- } else {
- mFinalIconDrawable = adaptiveIconDrawable;
- }
+ createIconDrawable(adaptiveIconDrawable, mIconSize);
}
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
return true;
}
private SplashScreenView fillViewWithIcon(Context context,
int iconSize, Drawable iconDrawable) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "fillViewWithIcon");
final SplashScreenView.Builder builder = new SplashScreenView.Builder(context);
builder.setIconSize(iconSize).setBackgroundColor(mThemeColor)
.setIconBackground(mIconBackground);
@@ -364,6 +359,7 @@
Slog.d(TAG, "fillViewWithIcon surfaceWindowView " + splashScreenView);
}
splashScreenView.makeSystemUIColorsTransparent();
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
return splashScreenView;
}
}
@@ -395,7 +391,7 @@
return root < 0.1;
}
- private static SplashScreenView makeSplashscreenContentDrawable(Context ctx,
+ static SplashScreenView makeSplashscreenContent(Context ctx,
int splashscreenContentResId) {
// doesn't support windowSplashscreenContent after S
// TODO add an allowlist to skip some packages if needed
@@ -525,6 +521,7 @@
new TransparentFilterQuantizer();
ComplexDrawableTester(Drawable drawable, boolean filterTransparent) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "ComplexDrawableTester");
final Rect initialBounds = drawable.copyBounds();
int width = drawable.getIntrinsicWidth();
int height = drawable.getIntrinsicHeight();
@@ -559,6 +556,7 @@
}
mPalette = builder.generate();
bitmap.recycle();
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
index 8626dbc..a4a83eb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
@@ -16,11 +16,15 @@
package com.android.wm.shell.startingsurface;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Matrix;
@@ -28,11 +32,13 @@
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.graphics.Shader;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.SystemClock;
+import android.os.Trace;
import android.util.PathParser;
import android.window.SplashScreenView;
@@ -47,12 +53,57 @@
public class SplashscreenIconDrawableFactory {
static Drawable makeIconDrawable(@ColorInt int backgroundColor,
- @NonNull Drawable foregroundDrawable) {
+ @NonNull Drawable foregroundDrawable, int iconSize) {
if (foregroundDrawable instanceof Animatable) {
return new AnimatableIconDrawable(backgroundColor, foregroundDrawable);
+ } else if (foregroundDrawable instanceof AdaptiveIconDrawable) {
+ return new ImmobileIconDrawable((AdaptiveIconDrawable) foregroundDrawable, iconSize);
} else {
- // TODO make a light weight drawable instead of AdaptiveIconDrawable
- return new AdaptiveIconDrawable(new ColorDrawable(backgroundColor), foregroundDrawable);
+ return new ImmobileIconDrawable(new AdaptiveIconDrawable(
+ new ColorDrawable(backgroundColor), foregroundDrawable), iconSize);
+ }
+ }
+
+ private static class ImmobileIconDrawable extends Drawable {
+ private Shader mLayersShader;
+ private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG
+ | Paint.FILTER_BITMAP_FLAG);
+
+ ImmobileIconDrawable(AdaptiveIconDrawable drawable, int iconSize) {
+ cachePaint(drawable, iconSize, iconSize);
+ }
+
+ private void cachePaint(AdaptiveIconDrawable drawable, int width, int height) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "cachePaint");
+ final Bitmap layersBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ final Canvas canvas = new Canvas(layersBitmap);
+ drawable.setBounds(0, 0, width, height);
+ drawable.draw(canvas);
+ mLayersShader = new BitmapShader(layersBitmap, Shader.TileMode.CLAMP,
+ Shader.TileMode.CLAMP);
+ mPaint.setShader(mLayersShader);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ final Rect bounds = getBounds();
+ canvas.drawRect(bounds, mPaint);
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+
+ }
+
+ @Override
+ public int getOpacity() {
+ return 1;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index b592121..b500813 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -181,11 +181,7 @@
}
int windowFlags = 0;
- final boolean enableHardAccelerated =
- (activityInfo.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0;
- if (enableHardAccelerated) {
- windowFlags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
- }
+ windowFlags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
final boolean[] showWallpaper = new boolean[1];
final int[] splashscreenContentResId = new int[1];
@@ -246,8 +242,6 @@
params.packageName = activityInfo.packageName;
params.windowAnimations = win.getWindowStyle().getResourceId(
com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
- params.privateFlags |=
- WindowManager.LayoutParams.PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED;
params.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
// Setting as trusted overlay to let touches pass through. This is safe because this
// window is controlled by the system.
@@ -267,21 +261,31 @@
final int taskId = taskInfo.taskId;
SplashScreenView sView = null;
try {
- sView = mSplashscreenContentDrawer.makeSplashScreenContentView(context, iconRes,
- splashscreenContentResId[0]);
final View view = win.getDecorView();
final WindowManager wm = mContext.getSystemService(WindowManager.class);
- if (postAddWindow(taskId, appToken, view, wm, params)) {
+ // splash screen content will be deprecated after S.
+ sView = SplashscreenContentDrawer.makeSplashscreenContent(
+ context, splashscreenContentResId[0]);
+ final boolean splashscreenContentCompatible = sView != null;
+ if (splashscreenContentCompatible) {
+ win.setContentView(sView);
+ } else {
+ sView = mSplashscreenContentDrawer.makeSplashScreenContentView(context, iconRes);
win.setContentView(sView);
sView.cacheRootWindow(win);
}
+ postAddWindow(taskId, appToken, view, wm, params);
} catch (RuntimeException e) {
// don't crash if something else bad happens, for example a
// failure loading resources because we are loading from an app
// on external storage that has been unmounted.
- Slog.w(TAG, " failed creating starting window", e);
+ Slog.w(TAG, " failed creating starting window at taskId: " + taskId, e);
+ sView = null;
} finally {
- setSplashScreenRecord(taskId, sView);
+ final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
+ if (record != null) {
+ record.setSplashScreenView(sView);
+ }
}
}
@@ -328,7 +332,7 @@
ActivityTaskManager.getInstance().onSplashScreenViewCopyFinished(taskId, parcelable);
}
- protected boolean postAddWindow(int taskId, IBinder appToken, View view, WindowManager wm,
+ protected void postAddWindow(int taskId, IBinder appToken, View view, WindowManager wm,
WindowManager.LayoutParams params) {
boolean shouldSaveView = true;
try {
@@ -349,7 +353,6 @@
removeWindowNoAnimate(taskId);
saveSplashScreenRecord(taskId, view);
}
- return shouldSaveView;
}
private void saveSplashScreenRecord(int taskId, View view) {
@@ -358,13 +361,6 @@
mStartingWindowRecords.put(taskId, tView);
}
- private void setSplashScreenRecord(int taskId, SplashScreenView splashScreenView) {
- final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
- if (record != null) {
- record.setSplashScreenView(splashScreenView);
- }
- }
-
private void removeWindowNoAnimate(int taskId) {
removeWindowSynced(taskId, null, null, false);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
index 207db9e..8ae0a73 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
@@ -83,12 +83,11 @@
}
@Override
- protected boolean postAddWindow(int taskId, IBinder appToken,
+ protected void postAddWindow(int taskId, IBinder appToken,
View view, WindowManager wm, WindowManager.LayoutParams params) {
// listen for addView
mAddWindowForTask = taskId;
mViewThemeResId = view.getContext().getThemeResId();
- return true;
}
@Override
diff --git a/location/java/android/location/util/identity/CallerIdentity.java b/location/java/android/location/util/identity/CallerIdentity.java
index 85a083e..ade0ea4 100644
--- a/location/java/android/location/util/identity/CallerIdentity.java
+++ b/location/java/android/location/util/identity/CallerIdentity.java
@@ -55,6 +55,20 @@
}
/**
+ * Returns a CallerIdentity with PID and listener ID information stripped. This is mostly
+ * useful for aggregating information for debug purposes, and should not be used in any API with
+ * security requirements.
+ */
+ public static CallerIdentity forAggregation(CallerIdentity callerIdentity) {
+ if (callerIdentity.getPid() == 0 && callerIdentity.getListenerId() == null) {
+ return callerIdentity;
+ }
+
+ return new CallerIdentity(callerIdentity.getUid(), 0, callerIdentity.getPackageName(),
+ callerIdentity.getAttributionTag(), null);
+ }
+
+ /**
* Creates a CallerIdentity for the current process and context.
*/
public static CallerIdentity fromContext(Context context) {
@@ -180,17 +194,6 @@
}
}
- /**
- * Returns a CallerIdentity corrosponding to this CallerIdentity but with a null listener id.
- */
- public CallerIdentity stripListenerId() {
- if (mListenerId == null) {
- return this;
- } else {
- return new CallerIdentity(mUid, mPid, mPackageName, mAttributionTag, null);
- }
- }
-
@Override
public String toString() {
int length = 10 + mPackageName.length();
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 3de78bb..644afb7 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -99,9 +99,7 @@
/**
- * MediaPlayer class can be used to control playback
- * of audio/video files and streams. An example on how to use the methods in
- * this class can be found in {@link android.widget.VideoView}.
+ * MediaPlayer class can be used to control playback of audio/video files and streams.
*
* <p>MediaPlayer is not thread-safe. Creation of and all access to player instances
* should be on the same thread. If registering <a href="#Callbacks">callbacks</a>,
diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt
index 9ca6d8f..b179c6d 100644
--- a/packages/Connectivity/framework/api/module-lib-current.txt
+++ b/packages/Connectivity/framework/api/module-lib-current.txt
@@ -45,6 +45,7 @@
public final class NetworkCapabilities implements android.os.Parcelable {
ctor public NetworkCapabilities(@Nullable android.net.NetworkCapabilities, long);
method @Nullable public java.util.Set<android.util.Range<java.lang.Integer>> getUids();
+ method public boolean hasUnwantedCapability(int);
field public static final long REDACT_ALL = -1L; // 0xffffffffffffffffL
field public static final long REDACT_FOR_ACCESS_FINE_LOCATION = 1L; // 0x1L
field public static final long REDACT_FOR_LOCAL_MAC_ADDRESS = 2L; // 0x2L
@@ -57,7 +58,13 @@
method @NonNull public android.net.NetworkCapabilities.Builder setUids(@Nullable java.util.Set<android.util.Range<java.lang.Integer>>);
}
+ public class NetworkRequest implements android.os.Parcelable {
+ method public boolean hasUnwantedCapability(int);
+ }
+
public static class NetworkRequest.Builder {
+ method @NonNull public android.net.NetworkRequest.Builder addUnwantedCapability(int);
+ method @NonNull public android.net.NetworkRequest.Builder removeUnwantedCapability(int);
method @NonNull public android.net.NetworkRequest.Builder setUids(@Nullable java.util.Set<android.util.Range<java.lang.Integer>>);
}
diff --git a/packages/Connectivity/framework/api/system-current.txt b/packages/Connectivity/framework/api/system-current.txt
index 703fca4..c19fcdd 100644
--- a/packages/Connectivity/framework/api/system-current.txt
+++ b/packages/Connectivity/framework/api/system-current.txt
@@ -216,6 +216,7 @@
method public void markConnected();
method public void onAddKeepalivePacketFilter(int, @NonNull android.net.KeepalivePacketData);
method public void onAutomaticReconnectDisabled();
+ method public void onBandwidthUpdateRequested();
method public void onNetworkUnwanted();
method public void onQosCallbackRegistered(int, @NonNull android.net.QosFilter);
method public void onQosCallbackUnregistered(int);
@@ -233,6 +234,7 @@
method public final void sendQosSessionAvailable(int, int, @NonNull android.telephony.data.EpsBearerQosSessionAttributes);
method public final void sendQosSessionLost(int, int);
method public final void sendSocketKeepaliveEvent(int, int);
+ method @Deprecated public void setLegacySubtype(int, @NonNull String);
method public final void setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>);
method public void unregister();
field public static final int VALIDATION_STATUS_NOT_VALID = 2; // 0x2
@@ -253,7 +255,12 @@
public static final class NetworkAgentConfig.Builder {
ctor public NetworkAgentConfig.Builder();
method @NonNull public android.net.NetworkAgentConfig build();
+ method @NonNull public android.net.NetworkAgentConfig.Builder disableNat64Detection();
+ method @NonNull public android.net.NetworkAgentConfig.Builder disableProvisioningNotification();
method @NonNull public android.net.NetworkAgentConfig.Builder setExplicitlySelected(boolean);
+ method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyExtraInfo(@NonNull String);
+ method @NonNull public android.net.NetworkAgentConfig.Builder setLegacySubType(int);
+ method @NonNull public android.net.NetworkAgentConfig.Builder setLegacySubTypeName(@NonNull String);
method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyType(int);
method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyTypeName(@NonNull String);
method @NonNull public android.net.NetworkAgentConfig.Builder setPartialConnectivityAcceptable(boolean);
@@ -388,6 +395,7 @@
}
public abstract class SocketKeepalive implements java.lang.AutoCloseable {
+ field public static final int ERROR_NO_SUCH_SLOT = -33; // 0xffffffdf
field public static final int SUCCESS = 0; // 0x0
}
diff --git a/packages/Connectivity/framework/src/android/net/NetworkAgent.java b/packages/Connectivity/framework/src/android/net/NetworkAgent.java
index 3863ed1..1ff0140 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkAgent.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkAgent.java
@@ -362,9 +362,8 @@
public static final int CMD_UNREGISTER_QOS_CALLBACK = BASE + 21;
private static NetworkInfo getLegacyNetworkInfo(final NetworkAgentConfig config) {
- // The subtype can be changed with (TODO) setLegacySubtype, but it starts
- // with 0 (TelephonyManager.NETWORK_TYPE_UNKNOWN) and an empty description.
- final NetworkInfo ni = new NetworkInfo(config.legacyType, 0, config.legacyTypeName, "");
+ final NetworkInfo ni = new NetworkInfo(config.legacyType, config.legacySubType,
+ config.legacyTypeName, config.legacySubTypeName);
ni.setIsAvailable(true);
ni.setDetailedState(NetworkInfo.DetailedState.CONNECTING, null /* reason */,
config.getLegacyExtraInfo());
@@ -829,6 +828,7 @@
* @hide
*/
@Deprecated
+ @SystemApi
public void setLegacySubtype(final int legacySubtype, @NonNull final String legacySubtypeName) {
mNetworkInfo.setSubtype(legacySubtype, legacySubtypeName);
queueOrSendNetworkInfo(mNetworkInfo);
@@ -962,6 +962,7 @@
* shall try to overwrite this method and produce a bandwidth update if capable.
* @hide
*/
+ @SystemApi
public void onBandwidthUpdateRequested() {
pollLceData();
}
diff --git a/packages/Connectivity/framework/src/android/net/NetworkAgentConfig.java b/packages/Connectivity/framework/src/android/net/NetworkAgentConfig.java
index 5e50a64..fb6fcc1 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkAgentConfig.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkAgentConfig.java
@@ -165,6 +165,12 @@
}
/**
+ * The legacy Sub type of this network agent, or TYPE_NONE if unset.
+ * @hide
+ */
+ public int legacySubType = ConnectivityManager.TYPE_NONE;
+
+ /**
* Set to true if the PRIVATE_DNS_BROKEN notification has shown for this network.
* Reset this bit when private DNS mode is changed from strict mode to opportunistic/off mode.
*
@@ -190,6 +196,13 @@
}
/**
+ * The name of the legacy Sub network type. It's a free-form string.
+ * @hide
+ */
+ @NonNull
+ public String legacySubTypeName = "";
+
+ /**
* The legacy extra info of the agent. The extra info should only be :
* <ul>
* <li>For cellular agents, the APN name.</li>
@@ -225,6 +238,8 @@
skip464xlat = nac.skip464xlat;
legacyType = nac.legacyType;
legacyTypeName = nac.legacyTypeName;
+ legacySubType = nac.legacySubType;
+ legacySubTypeName = nac.legacySubTypeName;
mLegacyExtraInfo = nac.mLegacyExtraInfo;
}
}
@@ -290,7 +305,6 @@
* and reduce idle traffic on networks that are known to be IPv6-only without a NAT64.
*
* @return this builder, to facilitate chaining.
- * @hide
*/
@NonNull
public Builder disableNat64Detection() {
@@ -303,7 +317,6 @@
* perform its own carrier-specific provisioning procedure.
*
* @return this builder, to facilitate chaining.
- * @hide
*/
@NonNull
public Builder disableProvisioningNotification() {
@@ -324,6 +337,18 @@
}
/**
+ * Sets the legacy sub-type for this network.
+ *
+ * @param legacySubType the type
+ * @return this builder, to facilitate chaining.
+ */
+ @NonNull
+ public Builder setLegacySubType(final int legacySubType) {
+ mConfig.legacySubType = legacySubType;
+ return this;
+ }
+
+ /**
* Sets the name of the legacy type of the agent. It's a free-form string used in logging.
* @param legacyTypeName the name
* @return this builder, to facilitate chaining.
@@ -335,10 +360,20 @@
}
/**
+ * Sets the name of the legacy Sub-type of the agent. It's a free-form string.
+ * @param legacySubTypeName the name
+ * @return this builder, to facilitate chaining.
+ */
+ @NonNull
+ public Builder setLegacySubTypeName(@NonNull String legacySubTypeName) {
+ mConfig.legacySubTypeName = legacySubTypeName;
+ return this;
+ }
+
+ /**
* Sets the legacy extra info of the agent.
* @param legacyExtraInfo the legacy extra info.
* @return this builder, to facilitate chaining.
- * @hide
*/
@NonNull
public Builder setLegacyExtraInfo(@NonNull String legacyExtraInfo) {
@@ -412,6 +447,8 @@
out.writeInt(skip464xlat ? 1 : 0);
out.writeInt(legacyType);
out.writeString(legacyTypeName);
+ out.writeInt(legacySubType);
+ out.writeString(legacySubTypeName);
out.writeString(mLegacyExtraInfo);
}
@@ -429,6 +466,8 @@
networkAgentConfig.skip464xlat = in.readInt() != 0;
networkAgentConfig.legacyType = in.readInt();
networkAgentConfig.legacyTypeName = in.readString();
+ networkAgentConfig.legacySubType = in.readInt();
+ networkAgentConfig.legacySubTypeName = in.readString();
networkAgentConfig.mLegacyExtraInfo = in.readString();
return networkAgentConfig;
}
diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
index c9c0940..881fa8c 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
@@ -639,19 +639,31 @@
}
/**
- * Removes (if found) the given capability from this {@code NetworkCapability} instance.
+ * Removes (if found) the given capability from this {@code NetworkCapability}
+ * instance that were added via addCapability(int) or setCapabilities(int[], int[]).
*
* @param capability the capability to be removed.
* @return This NetworkCapabilities instance, to facilitate chaining.
* @hide
*/
public @NonNull NetworkCapabilities removeCapability(@NetCapability int capability) {
- // Note that this method removes capabilities that were added via addCapability(int),
- // addUnwantedCapability(int) or setCapabilities(int[], int[]).
checkValidCapability(capability);
final long mask = ~(1 << capability);
mNetworkCapabilities &= mask;
- mUnwantedNetworkCapabilities &= mask;
+ return this;
+ }
+
+ /**
+ * Removes (if found) the given unwanted capability from this {@code NetworkCapability}
+ * instance that were added via addUnwantedCapability(int) or setCapabilities(int[], int[]).
+ *
+ * @param capability the capability to be removed.
+ * @return This NetworkCapabilities instance, to facilitate chaining.
+ * @hide
+ */
+ public @NonNull NetworkCapabilities removeUnwantedCapability(@NetCapability int capability) {
+ checkValidCapability(capability);
+ mUnwantedNetworkCapabilities &= ~(1 << capability);
return this;
}
@@ -723,6 +735,7 @@
}
/** @hide */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public boolean hasUnwantedCapability(@NetCapability int capability) {
return isValidCapability(capability)
&& ((mUnwantedNetworkCapabilities & (1 << capability)) != 0);
@@ -736,10 +749,16 @@
return ((mNetworkCapabilities & CONNECTIVITY_MANAGED_CAPABILITIES) != 0);
}
- /** Note this method may result in having the same capability in wanted and unwanted lists. */
private void combineNetCapabilities(@NonNull NetworkCapabilities nc) {
- this.mNetworkCapabilities |= nc.mNetworkCapabilities;
- this.mUnwantedNetworkCapabilities |= nc.mUnwantedNetworkCapabilities;
+ final long wantedCaps = this.mNetworkCapabilities | nc.mNetworkCapabilities;
+ final long unwantedCaps =
+ this.mUnwantedNetworkCapabilities | nc.mUnwantedNetworkCapabilities;
+ if ((wantedCaps & unwantedCaps) != 0) {
+ throw new IllegalArgumentException(
+ "Cannot have the same capability in wanted and unwanted lists.");
+ }
+ this.mNetworkCapabilities = wantedCaps;
+ this.mUnwantedNetworkCapabilities = unwantedCaps;
}
/**
diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
index f9b3db1..bcbc04f7 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
@@ -313,12 +313,31 @@
*
* @hide
*/
+ @NonNull
+ @SuppressLint("MissingGetterMatchingBuilder")
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public Builder addUnwantedCapability(@NetworkCapabilities.NetCapability int capability) {
mNetworkCapabilities.addUnwantedCapability(capability);
return this;
}
/**
+ * Removes (if found) the given unwanted capability from this builder instance.
+ *
+ * @param capability The unwanted capability to remove.
+ * @return The builder to facilitate chaining.
+ *
+ * @hide
+ */
+ @NonNull
+ @SuppressLint("BuilderSetStyle")
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public Builder removeUnwantedCapability(@NetworkCapabilities.NetCapability int capability) {
+ mNetworkCapabilities.removeUnwantedCapability(capability);
+ return this;
+ }
+
+ /**
* Completely clears all the {@code NetworkCapabilities} from this builder instance,
* removing even the capabilities that are set by default when the object is constructed.
*
@@ -575,6 +594,7 @@
*
* @hide
*/
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public boolean hasUnwantedCapability(@NetCapability int capability) {
return networkCapabilities.hasUnwantedCapability(capability);
}
diff --git a/packages/Connectivity/framework/src/android/net/SocketKeepalive.java b/packages/Connectivity/framework/src/android/net/SocketKeepalive.java
index d007a95..f6cae72 100644
--- a/packages/Connectivity/framework/src/android/net/SocketKeepalive.java
+++ b/packages/Connectivity/framework/src/android/net/SocketKeepalive.java
@@ -55,36 +55,68 @@
static final String TAG = "SocketKeepalive";
/**
- * No errors.
+ * Success. It indicates there is no error.
* @hide
*/
@SystemApi
public static final int SUCCESS = 0;
- /** @hide */
+ /**
+ * No keepalive. This should only be internally as it indicates There is no keepalive.
+ * It should not propagate to applications.
+ * @hide
+ */
public static final int NO_KEEPALIVE = -1;
- /** @hide */
+ /**
+ * Data received.
+ * @hide
+ */
public static final int DATA_RECEIVED = -2;
- /** @hide */
+ /**
+ * The binder died.
+ * @hide
+ */
public static final int BINDER_DIED = -10;
- /** The specified {@code Network} is not connected. */
+ /**
+ * The invalid network. It indicates the specified {@code Network} is not connected.
+ */
public static final int ERROR_INVALID_NETWORK = -20;
- /** The specified IP addresses are invalid. For example, the specified source IP address is
- * not configured on the specified {@code Network}. */
+
+ /**
+ * The invalid IP addresses. Indicates the specified IP addresses are invalid.
+ * For example, the specified source IP address is not configured on the
+ * specified {@code Network}.
+ */
public static final int ERROR_INVALID_IP_ADDRESS = -21;
- /** The requested port is invalid. */
+
+ /**
+ * The port is invalid.
+ */
public static final int ERROR_INVALID_PORT = -22;
- /** The packet length is invalid (e.g., too long). */
+
+ /**
+ * The length is invalid (e.g. too long).
+ */
public static final int ERROR_INVALID_LENGTH = -23;
- /** The packet transmission interval is invalid (e.g., too short). */
+
+ /**
+ * The interval is invalid (e.g. too short).
+ */
public static final int ERROR_INVALID_INTERVAL = -24;
- /** The target socket is invalid. */
+
+ /**
+ * The socket is invalid.
+ */
public static final int ERROR_INVALID_SOCKET = -25;
- /** The target socket is not idle. */
+
+ /**
+ * The socket is not idle.
+ */
public static final int ERROR_SOCKET_NOT_IDLE = -26;
+
/**
* The stop reason is uninitialized. This should only be internally used as initial state
* of stop reason, instead of propagating to application.
@@ -92,15 +124,29 @@
*/
public static final int ERROR_STOP_REASON_UNINITIALIZED = -27;
- /** The device does not support this request. */
+ /**
+ * The request is unsupported.
+ */
public static final int ERROR_UNSUPPORTED = -30;
- /** @hide TODO: delete when telephony code has been updated. */
- public static final int ERROR_HARDWARE_UNSUPPORTED = ERROR_UNSUPPORTED;
- /** The hardware returned an error. */
+
+ /**
+ * There was a hardware error.
+ */
public static final int ERROR_HARDWARE_ERROR = -31;
- /** The limitation of resource is reached. */
+
+ /**
+ * Resources are insufficient (e.g. all hardware slots are in use).
+ */
public static final int ERROR_INSUFFICIENT_RESOURCES = -32;
+ /**
+ * There was no such slot. This should only be internally as it indicates
+ * a programming error in the system server. It should not propagate to
+ * applications.
+ * @hide
+ */
+ @SystemApi
+ public static final int ERROR_NO_SUCH_SLOT = -33;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -111,7 +157,8 @@
ERROR_INVALID_LENGTH,
ERROR_INVALID_INTERVAL,
ERROR_INVALID_SOCKET,
- ERROR_SOCKET_NOT_IDLE
+ ERROR_SOCKET_NOT_IDLE,
+ ERROR_NO_SUCH_SLOT
})
public @interface ErrorCode {}
@@ -122,7 +169,6 @@
ERROR_INVALID_LENGTH,
ERROR_UNSUPPORTED,
ERROR_INSUFFICIENT_RESOURCES,
- ERROR_HARDWARE_UNSUPPORTED
})
public @interface KeepaliveEvent {}
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 5d4078d..fbb84fd 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -242,7 +242,7 @@
<bool name="def_hdmiControlAutoDeviceOff">true</bool>
<!-- Default for Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED -->
- <bool name="def_swipe_bottom_to_notification_enabled">true</bool>
+ <bool name="def_swipe_bottom_to_notification_enabled">false</bool>
<!-- Default for Settings.Secure.ONE_HANDED_MODE_ENABLED -->
<bool name="def_one_handed_mode_enabled">false</bool>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index eedcdab..b1689f6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -48,7 +48,6 @@
import com.android.systemui.qs.carrier.QSCarrierGroupController;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusIconContainer;
import com.android.systemui.statusbar.policy.Clock;
@@ -86,7 +85,6 @@
private final View mRingerContainer;
private final QSTileHost mQSTileHost;
private final StatusBarIconController mStatusBarIconController;
- private final CommandQueue mCommandQueue;
private final DemoModeController mDemoModeController;
private final UserTracker mUserTracker;
private final StatusIconContainer mIconContainer;
@@ -204,7 +202,7 @@
PrivacyItemController privacyItemController, RingerModeTracker ringerModeTracker,
ActivityStarter activityStarter, UiEventLogger uiEventLogger,
QSTileHost qsTileHost, StatusBarIconController statusBarIconController,
- CommandQueue commandQueue, DemoModeController demoModeController,
+ DemoModeController demoModeController,
UserTracker userTracker, QuickQSPanelController quickQSPanelController,
QSCarrierGroupController.Builder qsCarrierGroupControllerBuilder,
PrivacyLogger privacyLogger,
@@ -219,7 +217,6 @@
mUiEventLogger = uiEventLogger;
mQSTileHost = qsTileHost;
mStatusBarIconController = statusBarIconController;
- mCommandQueue = commandQueue;
mDemoModeController = demoModeController;
mUserTracker = userTracker;
mLifecycle = new LifecycleRegistry(mLifecycleOwner);
@@ -238,7 +235,7 @@
mRingerContainer = mView.findViewById(R.id.ringer_container);
mIconContainer = mView.findViewById(R.id.statusIcons);
- mIconManager = new StatusBarIconController.TintedIconManager(mIconContainer, mCommandQueue);
+ mIconManager = new StatusBarIconController.TintedIconManager(mIconContainer);
mDemoModeReceiver = new ClockDemoModeReceiver(mClockView);
mColorExtractor = colorExtractor;
mOnColorsChangedListener = (extractor, which) -> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index ca3923f..c565a271 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -38,8 +38,6 @@
import android.os.AsyncTask;
import android.os.Trace;
import android.os.UserHandle;
-import android.provider.DeviceConfig;
-import android.provider.DeviceConfig.Properties;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
@@ -48,7 +46,6 @@
import android.view.View;
import android.widget.ImageView;
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
@@ -147,23 +144,6 @@
private ImageView mBackdropFront;
private ImageView mBackdropBack;
- private boolean mShowCompactMediaSeekbar;
- private final DeviceConfig.OnPropertiesChangedListener mPropertiesChangedListener =
- new DeviceConfig.OnPropertiesChangedListener() {
- @Override
- public void onPropertiesChanged(Properties properties) {
- for (String name : properties.getKeyset()) {
- if (SystemUiDeviceConfigFlags.COMPACT_MEDIA_SEEKBAR_ENABLED.equals(name)) {
- String value = properties.getString(name, null);
- if (DEBUG_MEDIA) {
- Log.v(TAG, "DEBUG_MEDIA: compact media seekbar flag updated: " + value);
- }
- mShowCompactMediaSeekbar = "true".equals(value);
- }
- }
- }
- };
-
private final MediaController.Callback mMediaListener = new MediaController.Callback() {
@Override
public void onPlaybackStateChanged(PlaybackState state) {
@@ -231,14 +211,6 @@
setupNotifPipeline();
mUsingNotifPipeline = true;
}
-
- mShowCompactMediaSeekbar = "true".equals(
- DeviceConfig.getProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.COMPACT_MEDIA_SEEKBAR_ENABLED));
-
- deviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
- mContext.getMainExecutor(),
- mPropertiesChangedListener);
}
private void setupNotifPipeline() {
@@ -405,10 +377,6 @@
return mMediaMetadata;
}
- public boolean getShowCompactMediaSeekbar() {
- return mShowCompactMediaSeekbar;
- }
-
public Icon getMediaIcon() {
if (mMediaNotificationKey == null) {
return null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageGradientColorizer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageGradientColorizer.java
deleted file mode 100644
index f5a76f0..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageGradientColorizer.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.notification;
-
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorMatrix;
-import android.graphics.ColorMatrixColorFilter;
-import android.graphics.LinearGradient;
-import android.graphics.Paint;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.Shader;
-import android.graphics.drawable.Drawable;
-
-/**
- * A utility class to colorize bitmaps with a color gradient and a special blending mode
- */
-public class ImageGradientColorizer {
- public Bitmap colorize(Drawable drawable, int backgroundColor, boolean isRtl) {
- int width = drawable.getIntrinsicWidth();
- int height = drawable.getIntrinsicHeight();
- int size = Math.min(width, height);
- int widthInset = (width - size) / 2;
- int heightInset = (height - size) / 2;
- drawable = drawable.mutate();
- drawable.setBounds(- widthInset, - heightInset, width - widthInset, height - heightInset);
- Bitmap newBitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(newBitmap);
-
- // Values to calculate the luminance of a color
- float lr = 0.2126f;
- float lg = 0.7152f;
- float lb = 0.0722f;
-
- // Extract the red, green, blue components of the color extraction color in
- // float and int form
- int tri = Color.red(backgroundColor);
- int tgi = Color.green(backgroundColor);
- int tbi = Color.blue(backgroundColor);
-
- float tr = tri / 255f;
- float tg = tgi / 255f;
- float tb = tbi / 255f;
-
- // Calculate the luminance of the color extraction color
- float cLum = (tr * lr + tg * lg + tb * lb) * 255;
-
- ColorMatrix m = new ColorMatrix(new float[] {
- lr, lg, lb, 0, tri - cLum,
- lr, lg, lb, 0, tgi - cLum,
- lr, lg, lb, 0, tbi - cLum,
- 0, 0, 0, 1, 0,
- });
-
- Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
- LinearGradient linearGradient = new LinearGradient(0, 0, size, 0,
- new int[] {0, Color.argb(0.5f, 1, 1, 1), Color.BLACK},
- new float[] {0.0f, 0.4f, 1.0f}, Shader.TileMode.CLAMP);
- paint.setShader(linearGradient);
- Bitmap fadeIn = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
- Canvas fadeInCanvas = new Canvas(fadeIn);
- drawable.clearColorFilter();
- drawable.draw(fadeInCanvas);
-
- if (isRtl) {
- // Let's flip the gradient
- fadeInCanvas.translate(size, 0);
- fadeInCanvas.scale(-1, 1);
- }
- paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
- fadeInCanvas.drawPaint(paint);
-
- Paint coloredPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- coloredPaint.setColorFilter(new ColorMatrixColorFilter(m));
- coloredPaint.setAlpha((int) (0.5f * 255));
- canvas.drawBitmap(fadeIn, 0, 0, coloredPaint);
-
- linearGradient = new LinearGradient(0, 0, size, 0,
- new int[] {0, Color.argb(0.5f, 1, 1, 1), Color.BLACK},
- new float[] {0.0f, 0.6f, 1.0f}, Shader.TileMode.CLAMP);
- paint.setShader(linearGradient);
- fadeInCanvas.drawPaint(paint);
- canvas.drawBitmap(fadeIn, 0, 0, null);
-
- return newBitmap;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
index 2586e94..732c115 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java
@@ -16,237 +16,30 @@
package com.android.systemui.statusbar.notification;
-import android.app.Notification;
-import android.content.Context;
import android.graphics.Bitmap;
-import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
-import android.util.LayoutDirection;
-import androidx.annotation.VisibleForTesting;
import androidx.palette.graphics.Palette;
-import com.android.internal.util.ContrastColorUtil;
-import com.android.settingslib.Utils;
-
import java.util.List;
/**
- * A class the processes media notifications and extracts the right text and background colors.
+ * A gutted class that now contains only a color extraction utility used by the
+ * MediaArtworkProcessor, which has otherwise supplanted this.
+ *
+ * TODO(b/182926117): move this into MediaArtworkProcessor.kt
*/
public class MediaNotificationProcessor {
/**
- * The fraction below which we select the vibrant instead of the light/dark vibrant color
- */
- private static final float POPULATION_FRACTION_FOR_MORE_VIBRANT = 1.0f;
-
- /**
- * Minimum saturation that a muted color must have if there exists if deciding between two
- * colors
- */
- private static final float MIN_SATURATION_WHEN_DECIDING = 0.19f;
-
- /**
- * Minimum fraction that any color must have to be picked up as a text color
- */
- private static final double MINIMUM_IMAGE_FRACTION = 0.002;
-
- /**
- * The population fraction to select the dominant color as the text color over a the colored
- * ones.
- */
- private static final float POPULATION_FRACTION_FOR_DOMINANT = 0.01f;
-
- /**
* The population fraction to select a white or black color as the background over a color.
*/
private static final float POPULATION_FRACTION_FOR_WHITE_OR_BLACK = 2.5f;
private static final float BLACK_MAX_LIGHTNESS = 0.08f;
private static final float WHITE_MIN_LIGHTNESS = 0.90f;
private static final int RESIZE_BITMAP_AREA = 150 * 150;
- private final ImageGradientColorizer mColorizer;
- private final Context mContext;
- private final Palette.Filter mBlackWhiteFilter = (rgb, hsl) -> !isWhiteOrBlack(hsl);
- /**
- * The context of the notification. This is the app context of the package posting the
- * notification.
- */
- private final Context mPackageContext;
-
- public MediaNotificationProcessor(Context context, Context packageContext) {
- this(context, packageContext, new ImageGradientColorizer());
- }
-
- @VisibleForTesting
- MediaNotificationProcessor(Context context, Context packageContext,
- ImageGradientColorizer colorizer) {
- mContext = context;
- mPackageContext = packageContext;
- mColorizer = colorizer;
- }
-
- /**
- * Processes a builder of a media notification and calculates the appropriate colors that should
- * be used.
- *
- * @param notification the notification that is being processed
- * @param builder the recovered builder for the notification. this will be modified
- */
- public void processNotification(Notification notification, Notification.Builder builder) {
- Icon largeIcon = notification.getLargeIcon();
- Bitmap bitmap = null;
- Drawable drawable = null;
- if (largeIcon != null) {
- // We're transforming the builder, let's make sure all baked in RemoteViews are
- // rebuilt!
- builder.setRebuildStyledRemoteViews(true);
- drawable = largeIcon.loadDrawable(mPackageContext);
- int backgroundColor = 0;
- if (notification.isColorizedMedia()) {
- int width = drawable.getIntrinsicWidth();
- int height = drawable.getIntrinsicHeight();
- int area = width * height;
- if (area > RESIZE_BITMAP_AREA) {
- double factor = Math.sqrt((float) RESIZE_BITMAP_AREA / area);
- width = (int) (factor * width);
- height = (int) (factor * height);
- }
- bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
- drawable.setBounds(0, 0, width, height);
- drawable.draw(canvas);
-
- Palette.Builder paletteBuilder = generateArtworkPaletteBuilder(bitmap);
- Palette palette = paletteBuilder.generate();
- Palette.Swatch backgroundSwatch = findBackgroundSwatch(palette);
- backgroundColor = backgroundSwatch.getRgb();
- // we want most of the full region again, slightly shifted to the right
- float textColorStartWidthFraction = 0.4f;
- paletteBuilder.setRegion((int) (bitmap.getWidth() * textColorStartWidthFraction), 0,
- bitmap.getWidth(),
- bitmap.getHeight());
- // We're not filtering on white or black
- if (!isWhiteOrBlack(backgroundSwatch.getHsl())) {
- final float backgroundHue = backgroundSwatch.getHsl()[0];
- paletteBuilder.addFilter((rgb, hsl) -> {
- // at least 10 degrees hue difference
- float diff = Math.abs(hsl[0] - backgroundHue);
- return diff > 10 && diff < 350;
- });
- }
- paletteBuilder.addFilter(mBlackWhiteFilter);
- palette = paletteBuilder.generate();
- int foregroundColor = selectForegroundColor(backgroundColor, palette);
- builder.setColorPalette(backgroundColor, foregroundColor);
- } else {
- backgroundColor = Utils.getColorAttr(mContext, android.R.attr.colorBackground)
- .getDefaultColor();
- }
- Bitmap colorized = mColorizer.colorize(drawable, backgroundColor,
- mContext.getResources().getConfiguration().getLayoutDirection() ==
- LayoutDirection.RTL);
- builder.setLargeIcon(Icon.createWithBitmap(colorized));
- }
- }
-
- /**
- * Select a foreground color depending on whether the background color is dark or light
- * @param backgroundColor Background color to coordinate with
- * @param palette Artwork palette, should be obtained from {@link generateArtworkPaletteBuilder}
- * @return foreground color
- */
- public static int selectForegroundColor(int backgroundColor, Palette palette) {
- if (ContrastColorUtil.isColorLight(backgroundColor)) {
- return selectForegroundColorForSwatches(palette.getDarkVibrantSwatch(),
- palette.getVibrantSwatch(),
- palette.getDarkMutedSwatch(),
- palette.getMutedSwatch(),
- palette.getDominantSwatch(),
- Color.BLACK);
- } else {
- return selectForegroundColorForSwatches(palette.getLightVibrantSwatch(),
- palette.getVibrantSwatch(),
- palette.getLightMutedSwatch(),
- palette.getMutedSwatch(),
- palette.getDominantSwatch(),
- Color.WHITE);
- }
- }
-
- private static int selectForegroundColorForSwatches(Palette.Swatch moreVibrant,
- Palette.Swatch vibrant, Palette.Swatch moreMutedSwatch, Palette.Swatch mutedSwatch,
- Palette.Swatch dominantSwatch, int fallbackColor) {
- Palette.Swatch coloredCandidate = selectVibrantCandidate(moreVibrant, vibrant);
- if (coloredCandidate == null) {
- coloredCandidate = selectMutedCandidate(mutedSwatch, moreMutedSwatch);
- }
- if (coloredCandidate != null) {
- if (dominantSwatch == coloredCandidate) {
- return coloredCandidate.getRgb();
- } else if ((float) coloredCandidate.getPopulation() / dominantSwatch.getPopulation()
- < POPULATION_FRACTION_FOR_DOMINANT
- && dominantSwatch.getHsl()[1] > MIN_SATURATION_WHEN_DECIDING) {
- return dominantSwatch.getRgb();
- } else {
- return coloredCandidate.getRgb();
- }
- } else if (hasEnoughPopulation(dominantSwatch)) {
- return dominantSwatch.getRgb();
- } else {
- return fallbackColor;
- }
- }
-
- private static Palette.Swatch selectMutedCandidate(Palette.Swatch first,
- Palette.Swatch second) {
- boolean firstValid = hasEnoughPopulation(first);
- boolean secondValid = hasEnoughPopulation(second);
- if (firstValid && secondValid) {
- float firstSaturation = first.getHsl()[1];
- float secondSaturation = second.getHsl()[1];
- float populationFraction = first.getPopulation() / (float) second.getPopulation();
- if (firstSaturation * populationFraction > secondSaturation) {
- return first;
- } else {
- return second;
- }
- } else if (firstValid) {
- return first;
- } else if (secondValid) {
- return second;
- }
- return null;
- }
-
- private static Palette.Swatch selectVibrantCandidate(Palette.Swatch first,
- Palette.Swatch second) {
- boolean firstValid = hasEnoughPopulation(first);
- boolean secondValid = hasEnoughPopulation(second);
- if (firstValid && secondValid) {
- int firstPopulation = first.getPopulation();
- int secondPopulation = second.getPopulation();
- if (firstPopulation / (float) secondPopulation
- < POPULATION_FRACTION_FOR_MORE_VIBRANT) {
- return second;
- } else {
- return first;
- }
- } else if (firstValid) {
- return first;
- } else if (secondValid) {
- return second;
- }
- return null;
- }
-
- private static boolean hasEnoughPopulation(Palette.Swatch swatch) {
- // We want a fraction that is at least 1% of the image
- return swatch != null
- && (swatch.getPopulation() / (float) RESIZE_BITMAP_AREA > MINIMUM_IMAGE_FRACTION);
+ private MediaNotificationProcessor() {
}
/**
@@ -279,7 +72,7 @@
List<Palette.Swatch> swatches = palette.getSwatches();
float highestNonWhitePopulation = -1;
Palette.Swatch second = null;
- for (Palette.Swatch swatch: swatches) {
+ for (Palette.Swatch swatch : swatches) {
if (swatch != dominantSwatch
&& swatch.getPopulation() > highestNonWhitePopulation
&& !isWhiteOrBlack(swatch.getHsl())) {
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 6cf5c30..815cfb3 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
@@ -668,7 +668,6 @@
&& expandedView.findViewById(com.android.internal.R.id.media_actions) != null;
boolean isMessagingLayout = contractedView instanceof MessagingLayout;
boolean isCallLayout = contractedView instanceof CallLayout;
- boolean showCompactMediaSeekbar = mMediaManager.getShowCompactMediaSeekbar();
if (customView && beforeS && !mIsSummaryWithChildren) {
if (beforeN) {
@@ -678,12 +677,6 @@
} else {
smallHeight = mMaxSmallHeightBeforeS;
}
- } else if (isMediaLayout) {
- // TODO(b/172652345): MediaStyle notifications currently look broken when we enforce
- // the standard notification height, so we have to afford them more vertical space to
- // make sure we don't crop them terribly. We actually need to revisit this and give
- // them a headerless design, then remove this hack.
- smallHeight = showCompactMediaSeekbar ? mMaxSmallHeightMedia : mMaxSmallHeightBeforeS;
} else if (isMessagingLayout) {
// TODO(b/173204301): MessagingStyle notifications currently look broken when we enforce
// the standard notification height, so we have to afford them more vertical space to
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 58b87cd..73c4b05 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
@@ -40,13 +40,11 @@
import com.android.internal.widget.ImageMessageConsumer;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.media.MediaDataManagerKt;
import com.android.systemui.media.MediaFeatureFlag;
import com.android.systemui.statusbar.InflationTask;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.notification.ConversationNotificationProcessor;
import com.android.systemui.statusbar.notification.InflationException;
-import com.android.systemui.statusbar.notification.MediaNotificationProcessor;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -799,13 +797,6 @@
// For all of our templates, we want it to be RTL
packageContext = new RtlEnabledContext(packageContext);
}
- Notification notification = sbn.getNotification();
- if (notification.isMediaNotification() && !(mIsMediaInQS
- && MediaDataManagerKt.isMediaNotification(sbn))) {
- MediaNotificationProcessor processor = new MediaNotificationProcessor(mContext,
- packageContext);
- processor.processNotification(notification, recoveredBuilder);
- }
if (mEntry.getRanking().isConversation()) {
mConversationProcessor.processNotification(mEntry, recoveredBuilder);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
index 2535e5d..c75cd78 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
@@ -16,341 +16,26 @@
package com.android.systemui.statusbar.notification.row.wrapper;
-import static com.android.systemui.Dependency.MAIN_HANDLER;
-
-import android.annotation.Nullable;
-import android.app.Notification;
import android.content.Context;
-import android.content.res.ColorStateList;
-import android.media.MediaMetadata;
-import android.media.session.MediaController;
-import android.media.session.MediaSession;
-import android.media.session.PlaybackState;
-import android.metrics.LogMaker;
-import android.os.Handler;
-import android.text.format.DateUtils;
-import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewStub;
-import android.widget.SeekBar;
-import android.widget.TextView;
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.widget.MediaNotificationView;
-import com.android.systemui.Dependency;
-import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.TransformableView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import java.util.Timer;
-import java.util.TimerTask;
-
/**
* Wraps a notification containing a media template
*/
public class NotificationMediaTemplateViewWrapper extends NotificationTemplateViewWrapper {
- private static final long PROGRESS_UPDATE_INTERVAL = 1000; // 1s
- private static final String COMPACT_MEDIA_TAG = "media";
- private final Handler mHandler = Dependency.get(MAIN_HANDLER);
- private Timer mSeekBarTimer;
private View mActions;
- private SeekBar mSeekBar;
- private TextView mSeekBarElapsedTime;
- private TextView mSeekBarTotalTime;
- private long mDuration = 0;
- private MediaController mMediaController;
- private MediaMetadata mMediaMetadata;
- private NotificationMediaManager mMediaManager;
- private View mSeekBarView;
- private Context mContext;
- private MetricsLogger mMetricsLogger;
- private boolean mIsViewVisible;
-
- @VisibleForTesting
- protected SeekBar.OnSeekBarChangeListener mSeekListener =
- new SeekBar.OnSeekBarChangeListener() {
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- }
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
- }
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
- if (mMediaController != null) {
- mMediaController.getTransportControls().seekTo(mSeekBar.getProgress());
- mMetricsLogger.write(newLog(MetricsEvent.TYPE_UPDATE));
- }
- }
- };
-
- private MediaNotificationView.VisibilityChangeListener mVisibilityListener =
- new MediaNotificationView.VisibilityChangeListener() {
- @Override
- public void onAggregatedVisibilityChanged(boolean isVisible) {
- mIsViewVisible = isVisible;
- if (isVisible && mMediaController != null) {
- // Restart timer if we're currently playing and didn't already have one going
- PlaybackState state = mMediaController.getPlaybackState();
- if (state != null && state.getState() == PlaybackState.STATE_PLAYING
- && mSeekBarTimer == null && mSeekBarView != null
- && mSeekBarView.getVisibility() != View.GONE) {
- startTimer();
- }
- } else {
- clearTimer();
- }
- }
- };
-
- private View.OnAttachStateChangeListener mAttachStateListener =
- new View.OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(View v) {
- }
-
- @Override
- public void onViewDetachedFromWindow(View v) {
- mIsViewVisible = false;
- }
- };
-
- private MediaController.Callback mMediaCallback = new MediaController.Callback() {
- @Override
- public void onSessionDestroyed() {
- clearTimer();
- mMediaController.unregisterCallback(this);
- if (mView instanceof MediaNotificationView) {
- ((MediaNotificationView) mView).removeVisibilityListener(mVisibilityListener);
- mView.removeOnAttachStateChangeListener(mAttachStateListener);
- }
- }
-
- @Override
- public void onPlaybackStateChanged(@Nullable PlaybackState state) {
- if (state == null) {
- return;
- }
-
- if (state.getState() != PlaybackState.STATE_PLAYING) {
- // Update the UI once, in case playback info changed while we were paused
- updatePlaybackUi(state);
- clearTimer();
- } else if (mSeekBarTimer == null && mSeekBarView != null
- && mSeekBarView.getVisibility() != View.GONE) {
- startTimer();
- }
- }
-
- @Override
- public void onMetadataChanged(@Nullable MediaMetadata metadata) {
- if (mMediaMetadata == null || !mMediaMetadata.equals(metadata)) {
- mMediaMetadata = metadata;
- updateDuration();
- }
- }
- };
protected NotificationMediaTemplateViewWrapper(Context ctx, View view,
ExpandableNotificationRow row) {
super(ctx, view, row);
- mContext = ctx;
- mMediaManager = Dependency.get(NotificationMediaManager.class);
- mMetricsLogger = Dependency.get(MetricsLogger.class);
}
private void resolveViews() {
mActions = mView.findViewById(com.android.internal.R.id.media_actions);
- mIsViewVisible = mView.isShown();
-
- final MediaSession.Token token = mRow.getEntry().getSbn().getNotification().extras
- .getParcelable(Notification.EXTRA_MEDIA_SESSION);
-
- boolean showCompactSeekbar = mMediaManager.getShowCompactMediaSeekbar();
- if (token == null || (COMPACT_MEDIA_TAG.equals(mView.getTag()) && !showCompactSeekbar)) {
- if (mSeekBarView != null) {
- mSeekBarView.setVisibility(View.GONE);
- }
- return;
- }
-
- // Check for existing media controller and clean up / create as necessary
- boolean shouldUpdateListeners = false;
- if (mMediaController == null || !mMediaController.getSessionToken().equals(token)) {
- if (mMediaController != null) {
- mMediaController.unregisterCallback(mMediaCallback);
- }
- mMediaController = new MediaController(mContext, token);
- shouldUpdateListeners = true;
- }
-
- mMediaMetadata = mMediaController.getMetadata();
- if (mMediaMetadata != null) {
- long duration = mMediaMetadata.getLong(MediaMetadata.METADATA_KEY_DURATION);
- if (duration <= 0) {
- // Don't include the seekbar if this is a livestream
- if (mSeekBarView != null && mSeekBarView.getVisibility() != View.GONE) {
- mSeekBarView.setVisibility(View.GONE);
- mMetricsLogger.write(newLog(MetricsEvent.TYPE_CLOSE));
- clearTimer();
- } else if (mSeekBarView == null && shouldUpdateListeners) {
- // Only log if the controller changed, otherwise we would log multiple times for
- // the same notification when user pauses/resumes
- mMetricsLogger.write(newLog(MetricsEvent.TYPE_CLOSE));
- }
- return;
- } else if (mSeekBarView != null && mSeekBarView.getVisibility() == View.GONE) {
- // Otherwise, make sure the seekbar is visible
- mSeekBarView.setVisibility(View.VISIBLE);
- mMetricsLogger.write(newLog(MetricsEvent.TYPE_OPEN));
- updateDuration();
- startTimer();
- }
- }
-
- // Inflate the seekbar template
- ViewStub stub = mView.findViewById(R.id.notification_media_seekbar_container);
- if (stub instanceof ViewStub) {
- LayoutInflater layoutInflater = LayoutInflater.from(stub.getContext());
- stub.setLayoutInflater(layoutInflater);
- stub.setLayoutResource(R.layout.notification_material_media_seekbar);
- mSeekBarView = stub.inflate();
- mMetricsLogger.write(newLog(MetricsEvent.TYPE_OPEN));
-
- mSeekBar = mSeekBarView.findViewById(R.id.notification_media_progress_bar);
- mSeekBar.setOnSeekBarChangeListener(mSeekListener);
-
- mSeekBarElapsedTime = mSeekBarView.findViewById(R.id.notification_media_elapsed_time);
- mSeekBarTotalTime = mSeekBarView.findViewById(R.id.notification_media_total_time);
-
- shouldUpdateListeners = true;
- }
-
- if (shouldUpdateListeners) {
- if (mView instanceof MediaNotificationView) {
- MediaNotificationView mediaView = (MediaNotificationView) mView;
- mediaView.addVisibilityListener(mVisibilityListener);
- mView.addOnAttachStateChangeListener(mAttachStateListener);
- }
-
- if (mSeekBarTimer == null) {
- if (mMediaController != null && canSeekMedia(mMediaController.getPlaybackState())) {
- // Log initial state, since it will not be updated
- mMetricsLogger.write(newLog(MetricsEvent.TYPE_DETAIL, 1));
- } else {
- setScrubberVisible(false);
- }
- updateDuration();
- startTimer();
- mMediaController.registerCallback(mMediaCallback);
- }
- }
- updateSeekBarTint(mSeekBarView);
- }
-
- private void startTimer() {
- clearTimer();
- if (mIsViewVisible) {
- mSeekBarTimer = new Timer(true /* isDaemon */);
- mSeekBarTimer.schedule(new TimerTask() {
- @Override
- public void run() {
- mHandler.post(mOnUpdateTimerTick);
- }
- }, 0, PROGRESS_UPDATE_INTERVAL);
- }
- }
-
- private void clearTimer() {
- if (mSeekBarTimer != null) {
- mSeekBarTimer.cancel();
- mSeekBarTimer.purge();
- mSeekBarTimer = null;
- }
- }
-
- @Override
- public void setRemoved() {
- clearTimer();
- if (mMediaController != null) {
- mMediaController.unregisterCallback(mMediaCallback);
- }
- if (mView instanceof MediaNotificationView) {
- ((MediaNotificationView) mView).removeVisibilityListener(mVisibilityListener);
- mView.removeOnAttachStateChangeListener(mAttachStateListener);
- }
- }
-
- private boolean canSeekMedia(@Nullable PlaybackState state) {
- if (state == null) {
- return false;
- }
-
- long actions = state.getActions();
- return ((actions & PlaybackState.ACTION_SEEK_TO) != 0);
- }
-
- private void setScrubberVisible(boolean isVisible) {
- if (mSeekBar == null || mSeekBar.isEnabled() == isVisible) {
- return;
- }
-
- mSeekBar.getThumb().setAlpha(isVisible ? 255 : 0);
- mSeekBar.setEnabled(isVisible);
- mMetricsLogger.write(newLog(MetricsEvent.TYPE_DETAIL, isVisible ? 1 : 0));
- }
-
- private void updateDuration() {
- if (mMediaMetadata != null && mSeekBar != null) {
- long duration = mMediaMetadata.getLong(MediaMetadata.METADATA_KEY_DURATION);
- if (mDuration != duration) {
- mDuration = duration;
- mSeekBar.setMax((int) mDuration);
- mSeekBarTotalTime.setText(millisecondsToTimeString(duration));
- }
- }
- }
-
- protected final Runnable mOnUpdateTimerTick = new Runnable() {
- @Override
- public void run() {
- if (mMediaController != null && mSeekBar != null) {
- PlaybackState playbackState = mMediaController.getPlaybackState();
- if (playbackState != null) {
- updatePlaybackUi(playbackState);
- } else {
- clearTimer();
- }
- } else {
- clearTimer();
- }
- }
- };
-
- private void updatePlaybackUi(PlaybackState state) {
- if (mSeekBar == null || mSeekBarElapsedTime == null) {
- return;
- }
-
- long position = state.getPosition();
- mSeekBar.setProgress((int) position);
-
- mSeekBarElapsedTime.setText(millisecondsToTimeString(position));
-
- // Update scrubber in case available actions have changed
- setScrubberVisible(canSeekMedia(state));
- }
-
- private String millisecondsToTimeString(long milliseconds) {
- long seconds = milliseconds / 1000;
- String text = DateUtils.formatElapsedTime(seconds);
- return text;
}
@Override
@@ -361,28 +46,6 @@
super.onContentUpdated(row);
}
- private void updateSeekBarTint(View seekBarContainer) {
- if (seekBarContainer == null) {
- return;
- }
-
- if (this.getNotificationHeader() == null) {
- return;
- }
-
- int tintColor = getOriginalIconColor();
- mSeekBarElapsedTime.setTextColor(tintColor);
- mSeekBarTotalTime.setTextColor(tintColor);
- mSeekBarTotalTime.setShadowLayer(1.5f, 1.5f, 1.5f, mBackgroundColor);
-
- ColorStateList tintList = ColorStateList.valueOf(tintColor);
- mSeekBar.setThumbTintList(tintList);
- tintList = tintList.withAlpha(192); // 75%
- mSeekBar.setProgressTintList(tintList);
- tintList = tintList.withAlpha(128); // 50%
- mSeekBar.setProgressBackgroundTintList(tintList);
- }
-
@Override
protected void updateTransformedTypes() {
// This also clears the existing types
@@ -394,36 +57,7 @@
}
@Override
- public boolean isDimmable() {
- return getCustomBackgroundColor() == 0;
- }
-
- @Override
public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) {
return true;
}
-
- /**
- * Returns an initialized LogMaker for logging changes to the seekbar
- * @return new LogMaker
- */
- private LogMaker newLog(int event) {
- String packageName = mRow.getEntry().getSbn().getPackageName();
-
- return new LogMaker(MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR)
- .setType(event)
- .setPackageName(packageName);
- }
-
- /**
- * Returns an initialized LogMaker for logging changes with subtypes
- * @return new LogMaker
- */
- private LogMaker newLog(int event, int subtype) {
- String packageName = mRow.getEntry().getSbn().getPackageName();
- return new LogMaker(MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR)
- .setType(event)
- .setSubtype(subtype)
- .setPackageName(packageName);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 39f5847c..562d0ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -42,6 +42,9 @@
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Contains the collapsed status bar and handles hiding/showing based on disable flags
* and keyguard state. Also manages lifecycle to make sure the views it contains are being
@@ -70,6 +73,8 @@
private View mOperatorNameFrame;
private CommandQueue mCommandQueue;
+ private List<String> mBlockedIcons = new ArrayList<>();
+
private SignalCallback mSignalCallback = new SignalCallback() {
@Override
public void setIsAirplaneMode(NetworkController.IconState icon) {
@@ -101,9 +106,12 @@
mStatusBar.restoreHierarchyState(
savedInstanceState.getSparseParcelableArray(EXTRA_PANEL_STATE));
}
- mDarkIconManager = new DarkIconManager(view.findViewById(R.id.statusIcons),
- Dependency.get(CommandQueue.class));
+ mDarkIconManager = new DarkIconManager(view.findViewById(R.id.statusIcons));
mDarkIconManager.setShouldLog(true);
+ mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_volume));
+ mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_alarm_clock));
+ mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_call_strength));
+ mDarkIconManager.setBlockList(mBlockedIcons);
Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager);
mSystemIconArea = mStatusBar.findViewById(R.id.system_icon_area);
mClockView = mStatusBar.findViewById(R.id.clock);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 33798d6..2d760e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -47,7 +47,6 @@
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
-import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
@@ -59,6 +58,8 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
/**
* The header group on Keyguard.
@@ -89,6 +90,7 @@
private int mSystemIconsBaseMargin;
private View mSystemIconsContainer;
private TintedIconManager mIconManager;
+ private List<String> mBlockedIcons = new ArrayList<>();
private View mCutoutSpace;
private ViewGroup mStatusIconArea;
@@ -121,6 +123,7 @@
mStatusIconContainer = findViewById(R.id.statusIcons);
loadDimens();
+ loadBlockList();
mBatteryController = Dependency.get(BatteryController.class);
}
@@ -181,6 +184,14 @@
R.dimen.rounded_corner_content_padding);
}
+ // Set hidden status bar items
+ private void loadBlockList() {
+ Resources r = getResources();
+ mBlockedIcons.add(r.getString(com.android.internal.R.string.status_bar_volume));
+ mBlockedIcons.add(r.getString(com.android.internal.R.string.status_bar_alarm_clock));
+ mBlockedIcons.add(r.getString(com.android.internal.R.string.status_bar_call_strength));
+ }
+
private void updateVisibilities() {
if (mMultiUserAvatar.getParent() != mStatusIconArea
&& !mKeyguardUserSwitcherEnabled) {
@@ -336,8 +347,8 @@
userInfoController.addCallback(this);
userInfoController.reloadUserInfo();
Dependency.get(ConfigurationController.class).addCallback(this);
- mIconManager = new TintedIconManager(findViewById(R.id.statusIcons),
- Dependency.get(CommandQueue.class));
+ mIconManager = new TintedIconManager(findViewById(R.id.statusIcons));
+ mIconManager.setBlockList(mBlockedIcons);
Dependency.get(StatusBarIconController.class).addIconGroup(mIconManager);
onThemeChanged();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 8fe9a48..93b83d3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -18,6 +18,7 @@
import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE;
import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI;
+import android.annotation.Nullable;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
@@ -37,7 +38,6 @@
import com.android.systemui.demomode.DemoModeCommandReceiver;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
-import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.StatusBarMobileView;
import com.android.systemui.statusbar.StatusBarWifiView;
@@ -46,6 +46,7 @@
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
+import java.util.ArrayList;
import java.util.List;
public interface StatusBarIconController {
@@ -54,15 +55,22 @@
* When an icon is added with TAG_PRIMARY, it will be treated as the primary icon
* in that slot and not added as a sub slot.
*/
- public static final int TAG_PRIMARY = 0;
+ int TAG_PRIMARY = 0;
- public void addIconGroup(IconManager iconManager);
- public void removeIconGroup(IconManager iconManager);
- public void setExternalIcon(String slot);
- public void setIcon(String slot, int resourceId, CharSequence contentDescription);
- public void setIcon(String slot, StatusBarIcon icon);
- public void setSignalIcon(String slot, WifiIconState state);
- public void setMobileIcons(String slot, List<MobileIconState> states);
+ /** */
+ void addIconGroup(IconManager iconManager);
+ /** */
+ void removeIconGroup(IconManager iconManager);
+ /** */
+ void setExternalIcon(String slot);
+ /** */
+ void setIcon(String slot, int resourceId, CharSequence contentDescription);
+ /** */
+ void setIcon(String slot, StatusBarIcon icon);
+ /** */
+ void setSignalIcon(String slot, WifiIconState state);
+ /** */
+ void setMobileIcons(String slot, List<MobileIconState> states);
/**
* Display the no calling & SMS icons.
*/
@@ -85,8 +93,9 @@
* If you don't know what to pass for `tag`, either remove all icons for slot, or use
* TAG_PRIMARY to refer to the first icon at a given slot.
*/
- public void removeIcon(String slot, int tag);
- public void removeAllIconsForSlot(String slot);
+ void removeIcon(String slot, int tag);
+ /** */
+ void removeAllIconsForSlot(String slot);
// TODO: See if we can rename this tunable name.
String ICON_HIDE_LIST = "icon_blacklist";
@@ -108,12 +117,12 @@
/**
* Version of ViewGroup that observes state from the DarkIconDispatcher.
*/
- public static class DarkIconManager extends IconManager {
+ class DarkIconManager extends IconManager {
private final DarkIconDispatcher mDarkIconDispatcher;
private int mIconHPadding;
- public DarkIconManager(LinearLayout linearLayout, CommandQueue commandQueue) {
- super(linearLayout, commandQueue);
+ public DarkIconManager(LinearLayout linearLayout) {
+ super(linearLayout);
mIconHPadding = mContext.getResources().getDimensionPixelSize(
R.dimen.status_bar_icon_padding);
mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class);
@@ -169,11 +178,12 @@
}
}
- public static class TintedIconManager extends IconManager {
+ /** */
+ class TintedIconManager extends IconManager {
private int mColor;
- public TintedIconManager(ViewGroup group, CommandQueue commandQueue) {
- super(group, commandQueue);
+ public TintedIconManager(ViewGroup group) {
+ super(group);
}
@Override
@@ -219,7 +229,9 @@
private boolean mIsInDemoMode;
protected DemoStatusIcons mDemoStatusIcons;
- public IconManager(ViewGroup group, CommandQueue commandQueue) {
+ protected ArrayList<String> mBlockList = new ArrayList<>();
+
+ public IconManager(ViewGroup group) {
mGroup = group;
mContext = group.getContext();
mIconSize = mContext.getResources().getDimensionPixelSize(
@@ -234,6 +246,15 @@
mDemoable = demoable;
}
+ public void setBlockList(@Nullable List<String> blockList) {
+ mBlockList.clear();
+ if (blockList == null || blockList.isEmpty()) {
+ return;
+ }
+
+ mBlockList.addAll(blockList);
+ }
+
public void setShouldLog(boolean should) {
mShouldLog = should;
}
@@ -249,6 +270,11 @@
protected StatusIconDisplayable addHolder(int index, String slot, boolean blocked,
StatusBarIconHolder holder) {
+ // This is a little hacky, and probably regrettable, but just set `blocked` on any icon
+ // that is in our blocked list, then we'll never see it
+ if (mBlockList.contains(slot)) {
+ blocked = true;
+ }
switch (holder.getType()) {
case TYPE_ICON:
return addIcon(index, slot, blocked, holder.getIcon());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index 6404aea..75900a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -66,6 +66,7 @@
private Context mContext;
+ /** */
@Inject
public StatusBarIconControllerImpl(
Context context,
@@ -84,6 +85,7 @@
demoModeController.addCallback(this);
}
+ /** */
@Override
public void addIconGroup(IconManager group) {
mIconGroups.add(group);
@@ -101,12 +103,14 @@
}
}
+ /** */
@Override
public void removeIconGroup(IconManager group) {
group.destroy();
mIconGroups.remove(group);
}
+ /** */
@Override
public void onTuningChanged(String key, String newValue) {
if (!ICON_HIDE_LIST.equals(key)) {
@@ -149,6 +153,7 @@
mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, hidden, holder));
}
+ /** */
@Override
public void setIcon(String slot, int resourceId, CharSequence contentDescription) {
int index = getSlotIndex(slot);
@@ -290,8 +295,9 @@
* For backwards compatibility, in the event that someone gives us a slot and a status bar icon
*/
private void setIcon(int index, StatusBarIcon icon) {
+ String slot = getSlotName(index);
if (icon == null) {
- removeAllIconsForSlot(getSlotName(index));
+ removeAllIconsForSlot(slot);
return;
}
@@ -299,6 +305,7 @@
setIcon(index, holder);
}
+ /** */
@Override
public void setIcon(int index, @NonNull StatusBarIconHolder holder) {
boolean isNew = getIcon(index, holder.getTag()) == null;
@@ -328,6 +335,7 @@
handleSet(index, holder);
}
+ /** */
@Override
public void setIconAccessibilityLiveRegion(String slotName, int accessibilityLiveRegion) {
Slot slot = getSlot(slotName);
@@ -344,15 +352,18 @@
}
}
+ /** */
public void removeIcon(String slot) {
removeAllIconsForSlot(slot);
}
+ /** */
@Override
public void removeIcon(String slot, int tag) {
removeIcon(getSlotIndex(slot), tag);
}
+ /** */
@Override
public void removeAllIconsForSlot(String slotName) {
Slot slot = getSlot(slotName);
@@ -369,6 +380,7 @@
}
}
+ /** */
@Override
public void removeIcon(int index, int tag) {
if (getIcon(index, tag) == null) {
@@ -384,6 +396,7 @@
mIconGroups.forEach(l -> l.onSetIconHolder(viewIndex, holder));
}
+ /** */
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println(TAG + " state:");
@@ -402,6 +415,7 @@
super.dump(pw);
}
+ /** */
@Override
public void onDemoModeStarted() {
for (IconManager manager : mIconGroups) {
@@ -411,6 +425,7 @@
}
}
+ /** */
@Override
public void onDemoModeFinished() {
for (IconManager manager : mIconGroups) {
@@ -420,6 +435,7 @@
}
}
+ /** */
@Override
public void dispatchDemoCommand(String command, Bundle args) {
for (IconManager manager : mIconGroups) {
@@ -429,6 +445,7 @@
}
}
+ /** */
@Override
public List<String> demoCommands() {
List<String> s = new ArrayList<>();
@@ -436,6 +453,7 @@
return s;
}
+ /** */
@Override
public void onDensityOrFontScaleChanged() {
loadDimens();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
index 19db02a..af342dd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
@@ -39,7 +39,10 @@
private MobileIconState mMobileState;
private int mType = TYPE_ICON;
private int mTag = 0;
- private boolean mVisible = true;
+
+ private StatusBarIconHolder() {
+
+ }
public static StatusBarIconHolder fromIcon(StatusBarIcon icon) {
StatusBarIconHolder wrapper = new StatusBarIconHolder();
@@ -48,7 +51,10 @@
return wrapper;
}
- public static StatusBarIconHolder fromResId(Context context, int resId,
+ /** */
+ public static StatusBarIconHolder fromResId(
+ Context context,
+ int resId,
CharSequence contentDescription) {
StatusBarIconHolder holder = new StatusBarIconHolder();
holder.mIcon = new StatusBarIcon(UserHandle.SYSTEM, context.getPackageName(),
@@ -56,6 +62,7 @@
return holder;
}
+ /** */
public static StatusBarIconHolder fromWifiIconState(WifiIconState state) {
StatusBarIconHolder holder = new StatusBarIconHolder();
holder.mWifiState = state;
@@ -63,6 +70,7 @@
return holder;
}
+ /** */
public static StatusBarIconHolder fromMobileIconState(MobileIconState state) {
StatusBarIconHolder holder = new StatusBarIconHolder();
holder.mMobileState = state;
@@ -75,7 +83,8 @@
* Creates a new StatusBarIconHolder from a CallIndicatorIconState.
*/
public static StatusBarIconHolder fromCallIndicatorState(
- Context context, CallIndicatorIconState state) {
+ Context context,
+ CallIndicatorIconState state) {
StatusBarIconHolder holder = new StatusBarIconHolder();
int resId = state.isNoCalling ? state.noCallingResId : state.callStrengthResId;
String contentDescription = state.isNoCalling
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
index 4948c2b..3595095 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
@@ -33,7 +33,6 @@
import com.android.systemui.qs.carrier.QSCarrierGroup
import com.android.systemui.qs.carrier.QSCarrierGroupController
import com.android.systemui.settings.UserTracker
-import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.phone.StatusBarIconController
import com.android.systemui.statusbar.phone.StatusIconContainer
import com.android.systemui.statusbar.policy.Clock
@@ -78,8 +77,6 @@
@Mock
private lateinit var statusBarIconController: StatusBarIconController
@Mock
- private lateinit var commandQueue: CommandQueue
- @Mock
private lateinit var demoModeController: DemoModeController
@Mock
private lateinit var userTracker: UserTracker
@@ -130,7 +127,6 @@
uiEventLogger,
qsTileHost,
statusBarIconController,
- commandQueue,
demoModeController,
userTracker,
quickQSPanelController,
@@ -233,4 +229,4 @@
`when`(privacyItemController.micCameraAvailable).thenReturn(micCamera)
`when`(privacyItemController.locationAvailable).thenReturn(location)
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java
index e6287e7..aeb5b03 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/MediaNotificationProcessorTest.java
@@ -18,31 +18,18 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.assertNotSame;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-
import android.annotation.Nullable;
-import android.app.Notification;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.drawable.Drawable;
import android.test.suitebuilder.annotation.SmallTest;
-import android.widget.RemoteViews;
import androidx.palette.graphics.Palette;
import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.tests.R;
import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -58,17 +45,8 @@
*/
private static final int COLOR_TOLERANCE = 8;
- private MediaNotificationProcessor mProcessor;
- private Bitmap mBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
- private ImageGradientColorizer mColorizer;
@Nullable private Bitmap mArtwork;
- @Before
- public void setUp() {
- mColorizer = spy(new TestableColorizer(mBitmap));
- mProcessor = new MediaNotificationProcessor(getContext(), getContext(), mColorizer);
- }
-
@After
public void tearDown() {
if (mArtwork != null) {
@@ -78,53 +56,6 @@
}
@Test
- public void testColorizedWithLargeIcon() {
- Notification.Builder builder = new Notification.Builder(getContext()).setSmallIcon(
- R.drawable.ic_person)
- .setContentTitle("Title")
- .setLargeIcon(mBitmap)
- .setContentText("Text");
- Notification notification = builder.build();
- mProcessor.processNotification(notification, builder);
- verify(mColorizer).colorize(any(), anyInt(), anyBoolean());
- }
-
- @Test
- public void testNotColorizedWithoutLargeIcon() {
- Notification.Builder builder = new Notification.Builder(getContext()).setSmallIcon(
- R.drawable.ic_person)
- .setContentTitle("Title")
- .setContentText("Text");
- Notification notification = builder.build();
- mProcessor.processNotification(notification, builder);
- verifyZeroInteractions(mColorizer);
- }
-
- @Test
- public void testRemoteViewsReset() {
- Notification.Builder builder = new Notification.Builder(getContext()).setSmallIcon(
- R.drawable.ic_person)
- .setContentTitle("Title")
- .setStyle(new Notification.MediaStyle())
- .setLargeIcon(mBitmap)
- .setContentText("Text");
- Notification notification = builder.build();
- RemoteViews remoteViews = new RemoteViews(getContext().getPackageName(),
- R.layout.custom_view_dark);
- notification.contentView = remoteViews;
- notification.bigContentView = remoteViews;
- notification.headsUpContentView = remoteViews;
- mProcessor.processNotification(notification, builder);
- verify(mColorizer).colorize(any(), anyInt(), anyBoolean());
- RemoteViews contentView = builder.createContentView();
- assertNotSame(contentView, remoteViews);
- contentView = builder.createBigContentView();
- assertNotSame(contentView, remoteViews);
- contentView = builder.createHeadsUpContentView();
- assertNotSame(contentView, remoteViews);
- }
-
- @Test
public void findBackgroundSwatch_white() {
// Given artwork that is completely white.
mArtwork = Bitmap.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, Bitmap.Config.ARGB_8888);
@@ -153,17 +84,4 @@
assertThat((float) Color.green(expected)).isWithin(COLOR_TOLERANCE).of(Color.green(actual));
assertThat((float) Color.blue(expected)).isWithin(COLOR_TOLERANCE).of(Color.blue(actual));
}
-
- public static class TestableColorizer extends ImageGradientColorizer {
- private final Bitmap mBitmap;
-
- private TestableColorizer(Bitmap bitmap) {
- mBitmap = bitmap;
- }
-
- @Override
- public Bitmap colorize(Drawable drawable, int backgroundColor, boolean isRtl) {
- return mBitmap;
- }
- }
}
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
deleted file mode 100644
index fbe4d73..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java
+++ /dev/null
@@ -1,181 +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.systemui.statusbar.notification.row.wrapper;
-
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.app.Notification;
-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;
-import android.testing.TestableLooper.RunWithLooper;
-import android.view.View;
-import android.widget.RemoteViews;
-import android.widget.SeekBar;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
-
-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)
-@RunWithLooper
-public class NotificationMediaTemplateViewWrapperTest extends SysuiTestCase {
-
- private ExpandableNotificationRow mRow;
- private Notification mNotif;
- private View mView;
- private NotificationMediaTemplateViewWrapper mWrapper;
-
- @Mock
- private MetricsLogger mMetricsLogger;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- 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 {
- Notification.Builder builder = new Notification.Builder(mContext)
- .setSmallIcon(R.drawable.ic_person)
- .setContentTitle("Title")
- .setContentText("Text");
-
- MediaMetadata metadata = new MediaMetadata.Builder()
- .putLong(MediaMetadata.METADATA_KEY_DURATION, duration)
- .build();
- MediaSession session = new MediaSession(mContext, "TEST_CHANNEL");
- session.setMetadata(metadata);
-
- PlaybackState playbackState = new PlaybackState.Builder()
- .setActions(allowSeeking ? PlaybackState.ACTION_SEEK_TO : 0)
- .build();
-
- session.setPlaybackState(playbackState);
-
- builder.setStyle(new Notification.MediaStyle()
- .setMediaSession(session.getSessionToken())
- );
-
- mNotif = builder.build();
- assertTrue(mNotif.hasMediaSession());
-
- NotificationTestHelper helper = new NotificationTestHelper(
- mContext,
- mDependency,
- TestableLooper.get(this));
- mRow = helper.createRow(mNotif);
-
- RemoteViews views = new RemoteViews(mContext.getPackageName(),
- com.android.internal.R.layout.notification_template_material_big_media);
- mView = views.apply(mContext, null);
- mWrapper = new NotificationMediaTemplateViewWrapper(mContext,
- mView, mRow);
- mWrapper.onContentUpdated(mRow);
- }
-
- @Test
- public void testLogging_NoSeekbar() throws Exception {
- // Media sessions with duration <= 0 should not include a seekbar
- makeTestNotification(0, false);
-
- verify(mMetricsLogger).write(argThat(logMaker ->
- logMaker.getCategory() == MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR
- && logMaker.getType() == MetricsEvent.TYPE_CLOSE
- ));
-
- verify(mMetricsLogger, times(0)).write(argThat(logMaker ->
- logMaker.getCategory() == MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR
- && logMaker.getType() == MetricsEvent.TYPE_OPEN
- ));
- }
-
- @Test
- public void testLogging_HasSeekbarNoScrubber() throws Exception {
- // Media sessions that do not support seeking should have a seekbar, but no scrubber
- makeTestNotification(1000, false);
-
- verify(mMetricsLogger).write(argThat(logMaker ->
- logMaker.getCategory() == MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR
- && logMaker.getType() == MetricsEvent.TYPE_OPEN
- ));
-
- // Ensure the callback runs at least once
- mWrapper.mOnUpdateTimerTick.run();
-
- verify(mMetricsLogger).write(argThat(logMaker ->
- logMaker.getCategory() == MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR
- && logMaker.getType() == MetricsEvent.TYPE_DETAIL
- && logMaker.getSubtype() == 0
- ));
- }
-
- @Test
- public void testLogging_HasSeekbarAndScrubber() throws Exception {
- makeTestNotification(1000, true);
-
- verify(mMetricsLogger).write(argThat(logMaker ->
- logMaker.getCategory() == MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR
- && logMaker.getType() == MetricsEvent.TYPE_OPEN
- ));
-
- verify(mMetricsLogger).write(argThat(logMaker ->
- logMaker.getCategory() == MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR
- && logMaker.getType() == MetricsEvent.TYPE_DETAIL
- && logMaker.getSubtype() == 1
- ));
- }
-
- @Test
- public void testLogging_UpdateSeekbar() throws Exception {
- makeTestNotification(1000, true);
-
- SeekBar seekbar = mView.findViewById(
- com.android.internal.R.id.notification_media_progress_bar);
- assertTrue(seekbar != null);
-
- mWrapper.mSeekListener.onStopTrackingTouch(seekbar);
-
- verify(mMetricsLogger).write(argThat(logMaker ->
- logMaker.getCategory() == MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR
- && logMaker.getType() == MetricsEvent.TYPE_UPDATE));
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
index 7b7e2d3..f147f1ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
@@ -31,7 +31,6 @@
import com.android.internal.statusbar.StatusBarIcon;
import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.StatusBarMobileView;
import com.android.systemui.statusbar.StatusBarWifiView;
@@ -60,14 +59,14 @@
@Test
public void testSetCalledOnAdd_IconManager() {
LinearLayout layout = new LinearLayout(mContext);
- TestIconManager manager = new TestIconManager(layout, new CommandQueue(mContext));
+ TestIconManager manager = new TestIconManager(layout);
testCallOnAdd_forManager(manager);
}
@Test
public void testSetCalledOnAdd_DarkIconManager() {
LinearLayout layout = new LinearLayout(mContext);
- TestDarkIconManager manager = new TestDarkIconManager(layout, new CommandQueue(mContext));
+ TestDarkIconManager manager = new TestDarkIconManager(layout);
testCallOnAdd_forManager(manager);
}
@@ -104,8 +103,8 @@
private static class TestDarkIconManager extends DarkIconManager
implements TestableIconManager {
- TestDarkIconManager(LinearLayout group, CommandQueue commandQueue) {
- super(group, commandQueue);
+ TestDarkIconManager(LinearLayout group) {
+ super(group);
}
@Override
@@ -139,8 +138,8 @@
}
private static class TestIconManager extends IconManager implements TestableIconManager {
- TestIconManager(ViewGroup group, CommandQueue commandQueue) {
- super(group, commandQueue);
+ TestIconManager(ViewGroup group) {
+ super(group);
}
@Override
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 9ac93d9..b160d78 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -1205,8 +1205,6 @@
}
public void schedule() {
- Slog.d(LOG_TAG,
- "TriggerDeviceDisappearedRunnable.schedule(address = " + mAddress + ")");
mMainHandler.removeCallbacks(this);
mMainHandler.postDelayed(this, this, DEVICE_DISAPPEARED_TIMEOUT_MS);
}
@@ -1244,8 +1242,6 @@
}
private void onDeviceNearby(String address) {
- Slog.i(LOG_TAG, "onDeviceNearby(address = " + address + ")");
-
Date timestamp = new Date();
Date oldTimestamp = mDevicesLastNearby.put(address, timestamp);
@@ -1259,13 +1255,11 @@
boolean justAppeared = oldTimestamp == null
|| timestamp.getTime() - oldTimestamp.getTime() >= DEVICE_DISAPPEARED_TIMEOUT_MS;
if (justAppeared) {
+ Slog.i(LOG_TAG, "onDeviceNearby(justAppeared, address = " + address + ")");
for (Association association : getAllAssociations(address)) {
if (association.isNotifyOnDeviceNearby()) {
- if (DEBUG) {
- Slog.i(LOG_TAG, "Device " + address
- + " managed by " + association.getPackageName()
- + " is nearby on " + timestamp);
- }
+ Slog.i(LOG_TAG,
+ "Sending onDeviceAppeared to " + association.getPackageName() + ")");
getDeviceListenerServiceConnector(association).run(
service -> service.onDeviceAppeared(association.getDeviceMacAddress()));
}
@@ -1279,12 +1273,8 @@
boolean hasDeviceListeners = false;
for (Association association : getAllAssociations(address)) {
if (association.isNotifyOnDeviceNearby()) {
- if (DEBUG) {
- Slog.i(LOG_TAG, "Device " + address
- + " managed by " + association.getPackageName()
- + " disappeared; last seen on " + mDevicesLastNearby.get(address));
- }
-
+ Slog.i(LOG_TAG,
+ "Sending onDeviceDisappeared to " + association.getPackageName() + ")");
getDeviceListenerServiceConnector(association).run(
service -> service.onDeviceDisappeared(address));
hasDeviceListeners = true;
diff --git a/services/core/Android.bp b/services/core/Android.bp
index ed2e625..bed76f3 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -226,6 +226,7 @@
"java/com/android/server/connectivity/AutodestructReference.java",
"java/com/android/server/connectivity/ConnectivityConstants.java",
"java/com/android/server/connectivity/DnsManager.java",
+ "java/com/android/server/connectivity/FullScore.java",
"java/com/android/server/connectivity/KeepaliveTracker.java",
"java/com/android/server/connectivity/LingerMonitor.java",
"java/com/android/server/connectivity/MockableSystemProperties.java",
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c4548a3..492759f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -182,6 +182,7 @@
import android.app.WaitResult;
import android.app.backup.BackupManager.OperationType;
import android.app.backup.IBackupManager;
+import android.app.job.JobParameters;
import android.app.usage.UsageEvents;
import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStatsManager;
@@ -3490,7 +3491,9 @@
// Clear its scheduled jobs
JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class);
- js.cancelJobsForUid(appInfo.uid, "clear data");
+ // Clearing data is akin to uninstalling. The app is force stopped before we
+ // get to this point, so the reason won't be checked by the app.
+ js.cancelJobsForUid(appInfo.uid, JobParameters.STOP_REASON_USER, "clear data");
// Clear its pending alarms
AlarmManagerInternal ami = LocalServices.getService(AlarmManagerInternal.class);
diff --git a/services/core/java/com/android/server/am/AssistDataRequester.java b/services/core/java/com/android/server/am/AssistDataRequester.java
index d8d6528..70a54cf 100644
--- a/services/core/java/com/android/server/am/AssistDataRequester.java
+++ b/services/core/java/com/android/server/am/AssistDataRequester.java
@@ -143,27 +143,45 @@
* Request that autofill data be loaded asynchronously. The resulting data will be provided
* through the {@link AssistDataRequesterCallbacks}.
*
- * See {@link #requestData(List, boolean, boolean, boolean, boolean, boolean, int, String)}.
+ * See {@link #requestData(List, boolean, boolean, boolean, boolean, boolean, int, String,
+ * boolean)}.
*/
public void requestAutofillData(List<IBinder> activityTokens, int callingUid,
String callingPackage) {
requestData(activityTokens, true /* requestAutofillData */,
true /* fetchData */, false /* fetchScreenshot */,
true /* allowFetchData */, false /* allowFetchScreenshot */,
- callingUid, callingPackage);
+ false /* ignoreTopActivityCheck */, callingUid, callingPackage);
}
/**
* Request that assist data be loaded asynchronously. The resulting data will be provided
* through the {@link AssistDataRequesterCallbacks}.
*
- * See {@link #requestData(List, boolean, boolean, boolean, boolean, boolean, int, String)}.
+ * See {@link #requestData(List, boolean, boolean, boolean, boolean, boolean, int, String,
+ * boolean)}.
*/
public void requestAssistData(List<IBinder> activityTokens, final boolean fetchData,
final boolean fetchScreenshot, boolean allowFetchData, boolean allowFetchScreenshot,
int callingUid, String callingPackage) {
+ requestAssistData(activityTokens, fetchData, fetchScreenshot, allowFetchData,
+ allowFetchScreenshot, false /* ignoreTopActivityCheck */, callingUid,
+ callingPackage);
+ }
+
+ /**
+ * Request that assist data be loaded asynchronously. The resulting data will be provided
+ * through the {@link AssistDataRequesterCallbacks}.
+ *
+ * See {@link #requestData(List, boolean, boolean, boolean, boolean, boolean, int, String,
+ * boolean)}.
+ */
+ public void requestAssistData(List<IBinder> activityTokens, final boolean fetchData,
+ final boolean fetchScreenshot, boolean allowFetchData, boolean allowFetchScreenshot,
+ boolean ignoreTopActivityCheck, int callingUid, String callingPackage) {
requestData(activityTokens, false /* requestAutofillData */, fetchData, fetchScreenshot,
- allowFetchData, allowFetchScreenshot, callingUid, callingPackage);
+ allowFetchData, allowFetchScreenshot, ignoreTopActivityCheck, callingUid,
+ callingPackage);
}
/**
@@ -183,10 +201,13 @@
* is allowed to fetch the assist data
* @param allowFetchScreenshot to be joined with other checks, determines whether or not the
* requester is allowed to fetch the assist screenshot
+ * @param ignoreTopActivityCheck overrides the check for whether the activity is in focus when
+ * making the request. Used when passing an activity from Recents.
*/
private void requestData(List<IBinder> activityTokens, final boolean requestAutofillData,
final boolean fetchData, final boolean fetchScreenshot, boolean allowFetchData,
- boolean allowFetchScreenshot, int callingUid, String callingPackage) {
+ boolean allowFetchScreenshot, boolean ignoreTopActivityCheck, int callingUid,
+ String callingPackage) {
// TODO(b/34090158): Known issue, if the assist data is not allowed on the current activity,
// then no assist data is requested for any of the other activities
@@ -230,7 +251,8 @@
receiverExtras, topActivity, 0 /* flags */)
: mActivityTaskManager.requestAssistContextExtras(
ASSIST_CONTEXT_FULL, this, receiverExtras, topActivity,
- /* focused= */ i == 0, /* newSessionId= */ i == 0);
+ /* checkActivityIsTop= */ (i == 0)
+ && !ignoreTopActivityCheck, /* newSessionId= */ i == 0);
if (result) {
mPendingDataCount++;
} else if (i == 0) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
index c6f39aa..3eb4759 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
@@ -424,6 +424,12 @@
});
}
+ @Override
+ public void onSessionClosed() {
+ mHandler.post(() -> {
+ // TODO: implement this.
+ });
+ }
}
Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
index 5631647..d843bc9 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
@@ -403,6 +403,13 @@
invalidationClient.onAuthenticatorIdInvalidated(newAuthenticatorId);
});
}
+
+ @Override
+ public void onSessionClosed() {
+ mHandler.post(() -> {
+ // TODO: implement this.
+ });
+ }
}
Sensor(@NonNull String tag, @NonNull FingerprintProvider provider, @NonNull Context context,
diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
index 74e4ae7..acf39f0 100644
--- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
+++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
@@ -26,6 +26,7 @@
import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS;
import static android.net.SocketKeepalive.ERROR_INVALID_NETWORK;
import static android.net.SocketKeepalive.ERROR_INVALID_SOCKET;
+import static android.net.SocketKeepalive.ERROR_NO_SUCH_SLOT;
import static android.net.SocketKeepalive.ERROR_STOP_REASON_UNINITIALIZED;
import static android.net.SocketKeepalive.ERROR_UNSUPPORTED;
import static android.net.SocketKeepalive.MAX_INTERVAL_SEC;
@@ -528,6 +529,8 @@
}
} else if (reason == ERROR_STOP_REASON_UNINITIALIZED) {
throw new IllegalStateException("Unexpected stop reason: " + reason);
+ } else if (reason == ERROR_NO_SUCH_SLOT) {
+ throw new IllegalStateException("No such slot: " + reason);
} else {
notifyErrorCallback(ki.mCallback, reason);
}
diff --git a/services/core/java/com/android/server/content/SyncJobService.java b/services/core/java/com/android/server/content/SyncJobService.java
index aaf9cbc..1f46061 100644
--- a/services/core/java/com/android/server/content/SyncJobService.java
+++ b/services/core/java/com/android/server/content/SyncJobService.java
@@ -119,7 +119,7 @@
public boolean onStopJob(JobParameters params) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Slog.v(TAG, "onStopJob called " + params.getJobId() + ", reason: "
- + params.getStopReason());
+ + params.getLegacyStopReason());
}
final SyncOperation op = SyncOperation.maybeCreateFromJobExtras(params.getExtras());
if (op == null) {
@@ -161,9 +161,9 @@
m.obj = op;
// Reschedule if this job was NOT explicitly canceled.
- m.arg1 = params.getStopReason() != JobParameters.REASON_CANCELED ? 1 : 0;
+ m.arg1 = params.getLegacyStopReason() != JobParameters.REASON_CANCELED ? 1 : 0;
// Apply backoff only if stop is called due to timeout.
- m.arg2 = params.getStopReason() == JobParameters.REASON_TIMEOUT ? 1 : 0;
+ m.arg2 = params.getLegacyStopReason() == JobParameters.REASON_TIMEOUT ? 1 : 0;
SyncManager.sendMessage(m);
return false;
@@ -204,7 +204,8 @@
return "job:null";
} else {
return "job:#" + params.getJobId() + ":"
- + "sr=[" + params.getStopReason() + "/" + params.getDebugStopReason() + "]:"
+ + "sr=[" + params.getLegacyStopReason()
+ + "/" + params.getDebugStopReason() + "]:"
+ SyncOperation.maybeCreateFromJobExtras(params.getExtras());
}
}
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 2920ddb..6d1606d 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -31,6 +31,7 @@
import static com.android.server.location.LocationPermissions.PERMISSION_COARSE;
import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
+import static com.android.server.location.eventlog.LocationEventLog.EVENT_LOG;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
@@ -158,10 +159,9 @@
public Lifecycle(Context context) {
super(context);
- LocationEventLog eventLog = new LocationEventLog();
mUserInfoHelper = new LifecycleUserInfoHelper(context);
- mSystemInjector = new SystemInjector(context, mUserInfoHelper, eventLog);
- mService = new LocationManagerService(context, mSystemInjector, eventLog);
+ mSystemInjector = new SystemInjector(context, mUserInfoHelper);
+ mService = new LocationManagerService(context, mSystemInjector);
}
@Override
@@ -233,7 +233,6 @@
private final Context mContext;
private final Injector mInjector;
- private final LocationEventLog mEventLog;
private final LocalService mLocalService;
private final GeofenceManager mGeofenceManager;
@@ -261,18 +260,20 @@
@GuardedBy("mLock")
private @Nullable OnProviderLocationTagsChangeListener mOnProviderLocationTagsChangeListener;
- LocationManagerService(Context context, Injector injector, LocationEventLog eventLog) {
+ LocationManagerService(Context context, Injector injector) {
mContext = context.createAttributionContext(ATTRIBUTION_TAG);
mInjector = injector;
- mEventLog = eventLog;
mLocalService = new LocalService();
LocalServices.addService(LocationManagerInternal.class, mLocalService);
mGeofenceManager = new GeofenceManager(mContext, injector);
+ mInjector.getSettingsHelper().addOnLocationEnabledChangedListener(
+ this::onLocationModeChanged);
+
// set up passive provider first since it will be required for all other location providers,
// which are loaded later once the system is ready.
- mPassiveManager = new PassiveLocationProviderManager(mContext, injector, mEventLog);
+ mPassiveManager = new PassiveLocationProviderManager(mContext, injector);
addLocationProviderManager(mPassiveManager, new PassiveLocationProvider(mContext));
// TODO: load the gps provider here as well, which will require refactoring
@@ -313,7 +314,7 @@
}
LocationProviderManager manager = new LocationProviderManager(mContext, mInjector,
- mEventLog, providerName, mPassiveManager);
+ providerName, mPassiveManager);
addLocationProviderManager(manager, null);
return manager;
}
@@ -335,7 +336,7 @@
Settings.Global.LOCATION_ENABLE_STATIONARY_THROTTLE, 1) != 0;
if (enableStationaryThrottling) {
realProvider = new StationaryThrottlingLocationProvider(manager.getName(),
- mInjector, realProvider, mEventLog);
+ mInjector, realProvider);
}
}
manager.setRealProvider(realProvider);
@@ -355,9 +356,6 @@
}
void onSystemReady() {
- mInjector.getSettingsHelper().addOnLocationEnabledChangedListener(
- this::onLocationModeChanged);
-
if (Build.IS_DEBUGGABLE) {
// on debug builds, watch for location noteOps while location is off. there are some
// scenarios (emergency location) where this is expected, but generally this should
@@ -385,7 +383,7 @@
com.android.internal.R.string.config_networkLocationProviderPackageName);
if (networkProvider != null) {
LocationProviderManager networkManager = new LocationProviderManager(mContext,
- mInjector, mEventLog, NETWORK_PROVIDER, mPassiveManager);
+ mInjector, NETWORK_PROVIDER, mPassiveManager);
addLocationProviderManager(networkManager, networkProvider);
} else {
Log.w(TAG, "no network location provider found");
@@ -404,7 +402,7 @@
com.android.internal.R.string.config_fusedLocationProviderPackageName);
if (fusedProvider != null) {
LocationProviderManager fusedManager = new LocationProviderManager(mContext, mInjector,
- mEventLog, FUSED_PROVIDER, mPassiveManager);
+ FUSED_PROVIDER, mPassiveManager);
addLocationProviderManager(fusedManager, fusedProvider);
} else {
Log.wtf(TAG, "no fused location provider found");
@@ -419,7 +417,7 @@
mGnssManagerService.onSystemReady();
LocationProviderManager gnssManager = new LocationProviderManager(mContext, mInjector,
- mEventLog, GPS_PROVIDER, mPassiveManager);
+ GPS_PROVIDER, mPassiveManager);
addLocationProviderManager(gnssManager, mGnssManagerService.getGnssLocationProvider());
}
@@ -476,7 +474,7 @@
Log.d(TAG, "[u" + userId + "] location enabled = " + enabled);
}
- mEventLog.logLocationEnabled(userId, enabled);
+ EVENT_LOG.logLocationEnabled(userId, enabled);
Intent intent = new Intent(LocationManager.MODE_CHANGED_ACTION)
.putExtra(LocationManager.EXTRA_LOCATION_ENABLED, enabled)
@@ -1268,7 +1266,7 @@
ipw.println("Event Log:");
ipw.increaseIndent();
- mEventLog.iterate(manager.getName(), ipw::println);
+ EVENT_LOG.iterate(manager.getName(), ipw::println);
ipw.decreaseIndent();
return;
}
@@ -1313,7 +1311,7 @@
ipw.println("Historical Aggregate Location Provider Data:");
ipw.increaseIndent();
ArrayMap<String, ArrayMap<CallerIdentity, LocationEventLog.AggregateStats>> aggregateStats =
- mEventLog.copyAggregateStats();
+ EVENT_LOG.copyAggregateStats();
for (int i = 0; i < aggregateStats.size(); i++) {
ipw.print(aggregateStats.keyAt(i));
ipw.println(":");
@@ -1344,7 +1342,7 @@
ipw.println("Event Log:");
ipw.increaseIndent();
- mEventLog.iterate(ipw::println);
+ EVENT_LOG.iterate(ipw::println);
ipw.decreaseIndent();
}
@@ -1456,7 +1454,7 @@
@GuardedBy("this")
private boolean mSystemReady;
- SystemInjector(Context context, UserInfoHelper userInfoHelper, LocationEventLog eventLog) {
+ SystemInjector(Context context, UserInfoHelper userInfoHelper) {
mContext = context;
mUserInfoHelper = userInfoHelper;
@@ -1466,7 +1464,7 @@
mAppOpsHelper);
mSettingsHelper = new SystemSettingsHelper(context);
mAppForegroundHelper = new SystemAppForegroundHelper(context);
- mLocationPowerSaveModeHelper = new SystemLocationPowerSaveModeHelper(context, eventLog);
+ mLocationPowerSaveModeHelper = new SystemLocationPowerSaveModeHelper(context);
mScreenInteractiveHelper = new SystemScreenInteractiveHelper(context);
mDeviceStationaryHelper = new SystemDeviceStationaryHelper();
mDeviceIdleHelper = new SystemDeviceIdleHelper(context);
diff --git a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
index 29ce378..045e06d0 100644
--- a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
+++ b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
@@ -44,6 +44,8 @@
/** In memory event log for location events. */
public class LocationEventLog extends LocalEventLog {
+ public static final LocationEventLog EVENT_LOG = new LocationEventLog();
+
private static int getLogSize() {
if (Build.IS_DEBUGGABLE || D) {
return 500;
@@ -52,16 +54,17 @@
}
}
- private static final int EVENT_LOCATION_ENABLED = 1;
- private static final int EVENT_PROVIDER_ENABLED = 2;
- private static final int EVENT_PROVIDER_MOCKED = 3;
- private static final int EVENT_PROVIDER_REGISTER_CLIENT = 4;
- private static final int EVENT_PROVIDER_UNREGISTER_CLIENT = 5;
- private static final int EVENT_PROVIDER_UPDATE_REQUEST = 6;
- private static final int EVENT_PROVIDER_RECEIVE_LOCATION = 7;
- private static final int EVENT_PROVIDER_DELIVER_LOCATION = 8;
- private static final int EVENT_PROVIDER_STATIONARY_THROTTLED = 9;
- private static final int EVENT_LOCATION_POWER_SAVE_MODE_CHANGE = 10;
+ private static final int EVENT_USER_SWITCHED = 1;
+ private static final int EVENT_LOCATION_ENABLED = 2;
+ private static final int EVENT_PROVIDER_ENABLED = 3;
+ private static final int EVENT_PROVIDER_MOCKED = 4;
+ private static final int EVENT_PROVIDER_REGISTER_CLIENT = 5;
+ private static final int EVENT_PROVIDER_UNREGISTER_CLIENT = 6;
+ private static final int EVENT_PROVIDER_UPDATE_REQUEST = 7;
+ private static final int EVENT_PROVIDER_RECEIVE_LOCATION = 8;
+ private static final int EVENT_PROVIDER_DELIVER_LOCATION = 9;
+ private static final int EVENT_PROVIDER_STATIONARY_THROTTLED = 10;
+ private static final int EVENT_LOCATION_POWER_SAVE_MODE_CHANGE = 11;
@GuardedBy("mAggregateStats")
private final ArrayMap<String, ArrayMap<CallerIdentity, AggregateStats>> mAggregateStats;
@@ -90,19 +93,24 @@
packageMap = new ArrayMap<>(2);
mAggregateStats.put(provider, packageMap);
}
- CallerIdentity stripped = identity.stripListenerId();
- AggregateStats stats = packageMap.get(stripped);
+ CallerIdentity aggregate = CallerIdentity.forAggregation(identity);
+ AggregateStats stats = packageMap.get(aggregate);
if (stats == null) {
stats = new AggregateStats();
- packageMap.put(stripped, stats);
+ packageMap.put(aggregate, stats);
}
return stats;
}
}
+ /** Logs a user switched event. */
+ public void logUserSwitched(int userIdFrom, int userIdTo) {
+ addLogEvent(EVENT_USER_SWITCHED, userIdFrom, userIdTo);
+ }
+
/** Logs a location enabled/disabled event. */
public void logLocationEnabled(int userId, boolean enabled) {
- addLogEvent(EVENT_LOCATION_POWER_SAVE_MODE_CHANGE, userId, enabled);
+ addLogEvent(EVENT_LOCATION_ENABLED, userId, enabled);
}
/** Logs a location provider enabled/disabled event. */
@@ -183,8 +191,10 @@
@Override
protected LogEvent createLogEvent(long timeDelta, int event, Object... args) {
switch (event) {
+ case EVENT_USER_SWITCHED:
+ return new UserSwitchedEvent(timeDelta, (Integer) args[0], (Integer) args[1]);
case EVENT_LOCATION_ENABLED:
- return new LocationEnabledEvent(timeDelta, (Integer) args[1], (Boolean) args[2]);
+ return new LocationEnabledEvent(timeDelta, (Integer) args[0], (Boolean) args[1]);
case EVENT_PROVIDER_ENABLED:
return new ProviderEnabledEvent(timeDelta, (String) args[0], (Integer) args[1],
(Boolean) args[2]);
@@ -397,6 +407,23 @@
}
}
+ private static final class UserSwitchedEvent extends LogEvent {
+
+ private final int mUserIdFrom;
+ private final int mUserIdTo;
+
+ UserSwitchedEvent(long timeDelta, int userIdFrom, int userIdTo) {
+ super(timeDelta);
+ mUserIdFrom = userIdFrom;
+ mUserIdTo = userIdTo;
+ }
+
+ @Override
+ public String getLogString() {
+ return "current user switched from u" + mUserIdFrom + " to u" + mUserIdTo;
+ }
+ }
+
private static final class LocationEnabledEvent extends LogEvent {
private final int mUserId;
@@ -410,7 +437,7 @@
@Override
public String getLogString() {
- return "[u" + mUserId + "] location setting " + (mEnabled ? "enabled" : "disabled");
+ return "location [u" + mUserId + "] " + (mEnabled ? "enabled" : "disabled");
}
}
diff --git a/services/core/java/com/android/server/location/injector/LocationPowerSaveModeHelper.java b/services/core/java/com/android/server/location/injector/LocationPowerSaveModeHelper.java
index cc00d56..53407d9 100644
--- a/services/core/java/com/android/server/location/injector/LocationPowerSaveModeHelper.java
+++ b/services/core/java/com/android/server/location/injector/LocationPowerSaveModeHelper.java
@@ -20,12 +20,11 @@
import static com.android.server.location.LocationManagerService.D;
import static com.android.server.location.LocationManagerService.TAG;
+import static com.android.server.location.eventlog.LocationEventLog.EVENT_LOG;
import android.os.PowerManager.LocationPowerSaveMode;
import android.util.Log;
-import com.android.server.location.eventlog.LocationEventLog;
-
import java.util.concurrent.CopyOnWriteArrayList;
/**
@@ -43,11 +42,9 @@
void onLocationPowerSaveModeChanged(@LocationPowerSaveMode int locationPowerSaveMode);
}
- private final LocationEventLog mLocationEventLog;
private final CopyOnWriteArrayList<LocationPowerSaveModeChangedListener> mListeners;
- public LocationPowerSaveModeHelper(LocationEventLog locationEventLog) {
- mLocationEventLog = locationEventLog;
+ public LocationPowerSaveModeHelper() {
mListeners = new CopyOnWriteArrayList<>();
}
@@ -72,7 +69,7 @@
Log.d(TAG, "location power save mode is now " + locationPowerSaveModeToString(
locationPowerSaveMode));
}
- mLocationEventLog.logLocationPowerSaveMode(locationPowerSaveMode);
+ EVENT_LOG.logLocationPowerSaveMode(locationPowerSaveMode);
for (LocationPowerSaveModeChangedListener listener : mListeners) {
listener.onLocationPowerSaveModeChanged(locationPowerSaveMode);
diff --git a/services/core/java/com/android/server/location/injector/SystemLocationPowerSaveModeHelper.java b/services/core/java/com/android/server/location/injector/SystemLocationPowerSaveModeHelper.java
index c47a64d..a675f54 100644
--- a/services/core/java/com/android/server/location/injector/SystemLocationPowerSaveModeHelper.java
+++ b/services/core/java/com/android/server/location/injector/SystemLocationPowerSaveModeHelper.java
@@ -25,7 +25,6 @@
import com.android.internal.util.Preconditions;
import com.android.server.FgThread;
import com.android.server.LocalServices;
-import com.android.server.location.eventlog.LocationEventLog;
import java.util.Objects;
import java.util.function.Consumer;
@@ -42,8 +41,7 @@
@LocationPowerSaveMode
private volatile int mLocationPowerSaveMode;
- public SystemLocationPowerSaveModeHelper(Context context, LocationEventLog locationEventLog) {
- super(locationEventLog);
+ public SystemLocationPowerSaveModeHelper(Context context) {
mContext = context;
}
diff --git a/services/core/java/com/android/server/location/injector/SystemSettingsHelper.java b/services/core/java/com/android/server/location/injector/SystemSettingsHelper.java
index 3f7345e..632ed6e 100644
--- a/services/core/java/com/android/server/location/injector/SystemSettingsHelper.java
+++ b/services/core/java/com/android/server/location/injector/SystemSettingsHelper.java
@@ -35,6 +35,7 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Handler;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
@@ -348,31 +349,73 @@
*/
@Override
public void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
- int userId = ActivityManager.getCurrentUser();
+ int[] userIds;
+ try {
+ userIds = ActivityManager.getService().getRunningUserIds();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
- ipw.print("Location Enabled: ");
- ipw.println(isLocationEnabled(userId));
-
- List<String> locationPackageBlacklist = mLocationPackageBlacklist.getValueForUser(userId);
- if (!locationPackageBlacklist.isEmpty()) {
- ipw.println("Location Deny Packages:");
- ipw.increaseIndent();
- for (String packageName : locationPackageBlacklist) {
- ipw.println(packageName);
+ ipw.print("Location Setting: ");
+ ipw.increaseIndent();
+ if (userIds.length > 1) {
+ ipw.println();
+ for (int userId : userIds) {
+ ipw.print("[u");
+ ipw.print(userId);
+ ipw.print("] ");
+ ipw.println(isLocationEnabled(userId));
}
- ipw.decreaseIndent();
+ } else {
+ ipw.println(isLocationEnabled(userIds[0]));
+ }
+ ipw.decreaseIndent();
- List<String> locationPackageWhitelist = mLocationPackageWhitelist.getValueForUser(
- userId);
- if (!locationPackageWhitelist.isEmpty()) {
- ipw.println("Location Allow Packages:");
+ ipw.println("Location Allow/Deny Packages:");
+ ipw.increaseIndent();
+ if (userIds.length > 1) {
+ for (int userId : userIds) {
+ List<String> locationPackageBlacklist = mLocationPackageBlacklist.getValueForUser(
+ userId);
+ if (locationPackageBlacklist.isEmpty()) {
+ continue;
+ }
+
+ ipw.print("user ");
+ ipw.print(userId);
+ ipw.println(":");
ipw.increaseIndent();
- for (String packageName : locationPackageWhitelist) {
+
+ for (String packageName : locationPackageBlacklist) {
+ ipw.print("[deny] ");
ipw.println(packageName);
}
+
+ List<String> locationPackageWhitelist = mLocationPackageWhitelist.getValueForUser(
+ userId);
+ for (String packageName : locationPackageWhitelist) {
+ ipw.print("[allow] ");
+ ipw.println(packageName);
+ }
+
ipw.decreaseIndent();
}
+ } else {
+ List<String> locationPackageBlacklist = mLocationPackageBlacklist.getValueForUser(
+ userIds[0]);
+ for (String packageName : locationPackageBlacklist) {
+ ipw.print("[deny] ");
+ ipw.println(packageName);
+ }
+
+ List<String> locationPackageWhitelist = mLocationPackageWhitelist.getValueForUser(
+ userIds[0]);
+ for (String packageName : locationPackageWhitelist) {
+ ipw.print("[allow] ");
+ ipw.println(packageName);
+ }
}
+ ipw.decreaseIndent();
Set<String> backgroundThrottlePackageWhitelist =
mBackgroundThrottlePackageWhitelist.getValue();
diff --git a/services/core/java/com/android/server/location/injector/SystemUserInfoHelper.java b/services/core/java/com/android/server/location/injector/SystemUserInfoHelper.java
index d4a8fbd..ed1e654 100644
--- a/services/core/java/com/android/server/location/injector/SystemUserInfoHelper.java
+++ b/services/core/java/com/android/server/location/injector/SystemUserInfoHelper.java
@@ -155,6 +155,11 @@
*/
@Override
public void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) {
+ int[] runningUserIds = getRunningUserIds();
+ if (runningUserIds.length > 1) {
+ pw.println("running users: u" + Arrays.toString(runningUserIds));
+ }
+
ActivityManagerInternal activityManagerInternal = getActivityManagerInternal();
if (activityManagerInternal == null) {
return;
diff --git a/services/core/java/com/android/server/location/injector/UserInfoHelper.java b/services/core/java/com/android/server/location/injector/UserInfoHelper.java
index 0fcc1ec..c835370 100644
--- a/services/core/java/com/android/server/location/injector/UserInfoHelper.java
+++ b/services/core/java/com/android/server/location/injector/UserInfoHelper.java
@@ -18,6 +18,7 @@
import static com.android.server.location.LocationManagerService.D;
import static com.android.server.location.LocationManagerService.TAG;
+import static com.android.server.location.eventlog.LocationEventLog.EVENT_LOG;
import static com.android.server.location.injector.UserInfoHelper.UserListener.CURRENT_USER_CHANGED;
import static com.android.server.location.injector.UserInfoHelper.UserListener.USER_STARTED;
import static com.android.server.location.injector.UserInfoHelper.UserListener.USER_STOPPED;
@@ -105,6 +106,7 @@
Log.d(TAG, "current user changed from u" + Arrays.toString(fromUserIds) + " to u"
+ Arrays.toString(toUserIds));
}
+ EVENT_LOG.logUserSwitched(fromUserId, toUserId);
for (UserListener listener : mListeners) {
for (int userId : fromUserIds) {
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index dc8b1d0..102263b 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -37,6 +37,7 @@
import static com.android.server.location.LocationPermissions.PERMISSION_COARSE;
import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
import static com.android.server.location.LocationPermissions.PERMISSION_NONE;
+import static com.android.server.location.eventlog.LocationEventLog.EVENT_LOG;
import static java.lang.Math.max;
import static java.lang.Math.min;
@@ -94,7 +95,6 @@
import com.android.server.LocalServices;
import com.android.server.location.LocationPermissions;
import com.android.server.location.LocationPermissions.PermissionLevel;
-import com.android.server.location.eventlog.LocationEventLog;
import com.android.server.location.fudger.LocationFudger;
import com.android.server.location.injector.AlarmHelper;
import com.android.server.location.injector.AppForegroundHelper;
@@ -333,7 +333,7 @@
+ getRequest());
}
- mEventLog.logProviderClientRegistered(mName, getIdentity(), super.getRequest());
+ EVENT_LOG.logProviderClientRegistered(mName, getIdentity(), super.getRequest());
// initialization order is important as there are ordering dependencies
mPermitted = mLocationPermissionsHelper.hasLocationPermissions(mPermissionLevel,
@@ -345,7 +345,7 @@
onProviderListenerRegister();
if (mForeground) {
- mEventLog.logProviderClientForeground(mName, getIdentity());
+ EVENT_LOG.logProviderClientForeground(mName, getIdentity());
}
}
@@ -358,7 +358,7 @@
onProviderListenerUnregister();
- mEventLog.logProviderClientUnregistered(mName, getIdentity());
+ EVENT_LOG.logProviderClientUnregistered(mName, getIdentity());
if (D) {
Log.d(TAG, mName + " provider removed registration from " + getIdentity());
@@ -383,7 +383,7 @@
Preconditions.checkState(Thread.holdsLock(mLock));
}
- mEventLog.logProviderClientActive(mName, getIdentity());
+ EVENT_LOG.logProviderClientActive(mName, getIdentity());
if (!getRequest().isHiddenFromAppOps()) {
mLocationAttributionHelper.reportLocationStart(getIdentity(), getName(), getKey());
@@ -406,7 +406,7 @@
onProviderListenerInactive();
- mEventLog.logProviderClientInactive(mName, getIdentity());
+ EVENT_LOG.logProviderClientInactive(mName, getIdentity());
}
/**
@@ -543,9 +543,9 @@
mForeground = foreground;
if (mForeground) {
- mEventLog.logProviderClientForeground(mName, getIdentity());
+ EVENT_LOG.logProviderClientForeground(mName, getIdentity());
} else {
- mEventLog.logProviderClientBackground(mName, getIdentity());
+ EVENT_LOG.logProviderClientBackground(mName, getIdentity());
}
// note that onProviderLocationRequestChanged() is always called
@@ -654,7 +654,7 @@
protected abstract class LocationRegistration extends Registration implements
OnAlarmListener, ProviderEnabledListener {
- private final PowerManager.WakeLock mWakeLock;
+ final PowerManager.WakeLock mWakeLock;
private volatile ProviderTransport mProviderTransport;
private int mNumLocationsDelivered = 0;
@@ -879,7 +879,7 @@
listener.deliverOnLocationChanged(deliverLocationResult,
mUseWakeLock ? mWakeLock::release : null);
- mEventLog.logProviderDeliveredLocations(mName, locationResult.size(),
+ EVENT_LOG.logProviderDeliveredLocations(mName, locationResult.size(),
getIdentity());
}
@@ -1178,7 +1178,7 @@
// we currently don't hold a wakelock for getCurrentLocation deliveries
listener.deliverOnLocationChanged(deliverLocationResult, null);
- mEventLog.logProviderDeliveredLocations(mName,
+ EVENT_LOG.logProviderDeliveredLocations(mName,
locationResult != null ? locationResult.size() : 0, getIdentity());
}
@@ -1247,7 +1247,6 @@
private final CopyOnWriteArrayList<IProviderRequestListener> mProviderRequestListeners;
- protected final LocationEventLog mEventLog;
protected final LocationManagerInternal mLocationManagerInternal;
protected final SettingsHelper mSettingsHelper;
protected final UserInfoHelper mUserHelper;
@@ -1300,7 +1299,7 @@
@GuardedBy("mLock")
private @Nullable OnProviderLocationTagsChangeListener mOnLocationTagsChangeListener;
- public LocationProviderManager(Context context, Injector injector, LocationEventLog eventLog,
+ public LocationProviderManager(Context context, Injector injector,
String name, @Nullable PassiveLocationProviderManager passiveManager) {
mContext = context;
mName = Objects.requireNonNull(name);
@@ -1312,7 +1311,6 @@
mEnabledListeners = new ArrayList<>();
mProviderRequestListeners = new CopyOnWriteArrayList<>();
- mEventLog = eventLog;
mLocationManagerInternal = Objects.requireNonNull(
LocalServices.getService(LocationManagerInternal.class));
mSettingsHelper = injector.getSettingsHelper();
@@ -1477,7 +1475,7 @@
synchronized (mLock) {
Preconditions.checkState(mState != STATE_STOPPED);
- mEventLog.logProviderMocked(mName, provider != null);
+ EVENT_LOG.logProviderMocked(mName, provider != null);
final long identity = Binder.clearCallingIdentity();
try {
@@ -1966,8 +1964,8 @@
}
@GuardedBy("mLock")
- private void setProviderRequest(ProviderRequest request) {
- mEventLog.logProviderUpdateRequest(mName, request);
+ void setProviderRequest(ProviderRequest request) {
+ EVENT_LOG.logProviderUpdateRequest(mName, request);
mProvider.getController().setRequest(request);
FgThread.getHandler().post(() -> {
@@ -2324,7 +2322,7 @@
}
// don't log location received for passive provider because it's spammy
- mEventLog.logProviderReceivedLocations(mName, filtered.size());
+ EVENT_LOG.logProviderReceivedLocations(mName, filtered.size());
} else {
// passive provider should get already filtered results as input
filtered = locationResult;
@@ -2424,7 +2422,7 @@
if (D) {
Log.d(TAG, "[u" + userId + "] " + mName + " provider enabled = " + enabled);
}
- mEventLog.logProviderEnabled(mName, userId, enabled);
+ EVENT_LOG.logProviderEnabled(mName, userId, enabled);
}
// clear last locations if we become disabled
@@ -2464,7 +2462,7 @@
updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
}
- private @Nullable Location getPermittedLocation(@Nullable Location fineLocation,
+ @Nullable Location getPermittedLocation(@Nullable Location fineLocation,
@PermissionLevel int permissionLevel) {
switch (permissionLevel) {
case PERMISSION_FINE:
@@ -2477,7 +2475,7 @@
}
}
- private @Nullable LocationResult getPermittedLocationResult(
+ @Nullable LocationResult getPermittedLocationResult(
@Nullable LocationResult fineLocationResult, @PermissionLevel int permissionLevel) {
switch (permissionLevel) {
case PERMISSION_FINE:
@@ -2538,6 +2536,8 @@
private @Nullable Location mFineBypassLocation;
private @Nullable Location mCoarseBypassLocation;
+ LastLocation() {}
+
public void clearMock() {
if (mFineLocation != null && mFineLocation.isFromMockProvider()) {
mFineLocation = null;
diff --git a/services/core/java/com/android/server/location/provider/PassiveLocationProviderManager.java b/services/core/java/com/android/server/location/provider/PassiveLocationProviderManager.java
index 027f4e9..b35af4f 100644
--- a/services/core/java/com/android/server/location/provider/PassiveLocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/PassiveLocationProviderManager.java
@@ -24,7 +24,6 @@
import android.os.Binder;
import com.android.internal.util.Preconditions;
-import com.android.server.location.eventlog.LocationEventLog;
import com.android.server.location.injector.Injector;
import java.util.Collection;
@@ -34,9 +33,8 @@
*/
public class PassiveLocationProviderManager extends LocationProviderManager {
- public PassiveLocationProviderManager(Context context, Injector injector,
- LocationEventLog eventLog) {
- super(context, injector, eventLog, LocationManager.PASSIVE_PROVIDER, null);
+ public PassiveLocationProviderManager(Context context, Injector injector) {
+ super(context, injector, LocationManager.PASSIVE_PROVIDER, null);
}
@Override
diff --git a/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java b/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
index 6f4aa64..ab7e526 100644
--- a/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
@@ -21,6 +21,7 @@
import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
import static com.android.server.location.LocationManagerService.D;
import static com.android.server.location.LocationManagerService.TAG;
+import static com.android.server.location.eventlog.LocationEventLog.EVENT_LOG;
import android.annotation.Nullable;
import android.location.Location;
@@ -33,7 +34,6 @@
import com.android.internal.util.Preconditions;
import com.android.server.DeviceIdleInternal;
import com.android.server.FgThread;
-import com.android.server.location.eventlog.LocationEventLog;
import com.android.server.location.injector.DeviceIdleHelper;
import com.android.server.location.injector.DeviceStationaryHelper;
import com.android.server.location.injector.Injector;
@@ -54,12 +54,11 @@
private static final long MAX_STATIONARY_LOCATION_AGE_MS = 30000;
- private final Object mLock = new Object();
+ final Object mLock = new Object();
private final String mName;
private final DeviceIdleHelper mDeviceIdleHelper;
private final DeviceStationaryHelper mDeviceStationaryHelper;
- private final LocationEventLog mEventLog;
@GuardedBy("mLock")
private boolean mDeviceIdle = false;
@@ -72,21 +71,19 @@
@GuardedBy("mLock")
private ProviderRequest mOutgoingRequest = ProviderRequest.EMPTY_REQUEST;
@GuardedBy("mLock")
- private long mThrottlingIntervalMs = INTERVAL_DISABLED;
+ long mThrottlingIntervalMs = INTERVAL_DISABLED;
@GuardedBy("mLock")
- private @Nullable DeliverLastLocationRunnable mDeliverLastLocationCallback = null;
-
+ @Nullable DeliverLastLocationRunnable mDeliverLastLocationCallback = null;
@GuardedBy("mLock")
- private @Nullable Location mLastLocation;
+ @Nullable Location mLastLocation;
public StationaryThrottlingLocationProvider(String name, Injector injector,
- AbstractLocationProvider delegate, LocationEventLog eventLog) {
+ AbstractLocationProvider delegate) {
super(DIRECT_EXECUTOR, delegate);
mName = name;
mDeviceIdleHelper = injector.getDeviceIdleHelper();
mDeviceStationaryHelper = injector.getDeviceStationaryHelper();
- mEventLog = eventLog;
// must be last statement in the constructor because reference is escaping
initializeDelegate();
@@ -209,7 +206,7 @@
if (D) {
Log.d(TAG, mName + " provider stationary throttled");
}
- mEventLog.logProviderStationaryThrottled(mName, true);
+ EVENT_LOG.logProviderStationaryThrottled(mName, true);
}
if (mDeliverLastLocationCallback != null) {
@@ -227,7 +224,7 @@
}
} else {
if (oldThrottlingIntervalMs != INTERVAL_DISABLED) {
- mEventLog.logProviderStationaryThrottled(mName, false);
+ EVENT_LOG.logProviderStationaryThrottled(mName, false);
if (D) {
Log.d(TAG, mName + " provider stationary unthrottled");
}
@@ -257,6 +254,9 @@
}
private class DeliverLastLocationRunnable implements Runnable {
+
+ DeliverLastLocationRunnable() {}
+
@Override
public void run() {
Location location;
diff --git a/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java b/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
index c86e49b..317e61b 100644
--- a/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
@@ -36,6 +36,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
+import com.android.server.FgThread;
import com.android.server.location.provider.AbstractLocationProvider;
import com.android.server.servicewatcher.ServiceWatcher;
import com.android.server.servicewatcher.ServiceWatcher.BoundService;
@@ -55,6 +56,8 @@
private static final String KEY_EXTRA_ATTRIBUTION_TAGS = "android:location_allow_listed_tags";
private static final String EXTRA_ATTRIBUTION_TAGS_SEPARATOR = ";";
+ private static final long RESET_DELAY_MS = 1000;
+
/**
* Creates and registers this proxy. If no suitable service is available for the proxy, returns
* null.
@@ -80,6 +83,9 @@
final ArrayList<Runnable> mFlushListeners = new ArrayList<>(0);
@GuardedBy("mLock")
+ @Nullable Runnable mResetter;
+
+ @GuardedBy("mLock")
Proxy mProxy;
@GuardedBy("mLock")
@Nullable ComponentName mService;
@@ -111,6 +117,11 @@
mProxy = new Proxy();
mService = boundService.component;
+ if (mResetter != null) {
+ FgThread.getHandler().removeCallbacks(mResetter);
+ mResetter = null;
+ }
+
// update extra attribution tag info from manifest
if (boundService.metadata != null) {
String tagsList = boundService.metadata.getString(KEY_EXTRA_ATTRIBUTION_TAGS);
@@ -134,7 +145,22 @@
synchronized (mLock) {
mProxy = null;
mService = null;
- setState(prevState -> State.EMPTY_STATE);
+
+ // we need to clear the state - but most disconnections are very temporary. we give a
+ // grace period where we don't clear the state immediately so that transient
+ // interruptions are not visible to clients
+ mResetter = new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mLock) {
+ if (mResetter == this) {
+ setState(prevState -> State.EMPTY_STATE);
+ }
+ }
+ }
+ };
+ FgThread.getHandler().postDelayed(mResetter, RESET_DELAY_MS);
+
flushListeners = mFlushListeners.toArray(new Runnable[0]);
mFlushListeners.clear();
}
@@ -245,7 +271,8 @@
identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag);
}
- setState(prevState -> prevState
+ setState(prevState -> State.EMPTY_STATE
+ .withExtraAttributionTags(prevState.extraAttributionTags)
.withAllowed(allowed)
.withProperties(properties)
.withIdentity(identity));
diff --git a/services/core/java/com/android/server/net/IpConfigStore.java b/services/core/java/com/android/server/net/IpConfigStore.java
index cc3a002..df1eb6d 100644
--- a/services/core/java/com/android/server/net/IpConfigStore.java
+++ b/services/core/java/com/android/server/net/IpConfigStore.java
@@ -22,7 +22,6 @@
import android.net.IpConfiguration.ProxySettings;
import android.net.LinkAddress;
import android.net.ProxyInfo;
-import android.net.RouteInfo;
import android.net.StaticIpConfiguration;
import android.net.Uri;
import android.util.ArrayMap;
@@ -42,6 +41,8 @@
import java.io.InputStream;
import java.net.Inet4Address;
import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.List;
public class IpConfigStore {
private static final String TAG = "IpConfigStore";
@@ -83,25 +84,25 @@
boolean written = false;
try {
- switch (config.ipAssignment) {
+ switch (config.getIpAssignment()) {
case STATIC:
out.writeUTF(IP_ASSIGNMENT_KEY);
- out.writeUTF(config.ipAssignment.toString());
- StaticIpConfiguration staticIpConfiguration = config.staticIpConfiguration;
+ out.writeUTF(config.getIpAssignment().toString());
+ StaticIpConfiguration staticIpConfiguration = config.getStaticIpConfiguration();
if (staticIpConfiguration != null) {
- if (staticIpConfiguration.ipAddress != null) {
- LinkAddress ipAddress = staticIpConfiguration.ipAddress;
+ if (staticIpConfiguration.getIpAddress() != null) {
+ LinkAddress ipAddress = staticIpConfiguration.getIpAddress();
out.writeUTF(LINK_ADDRESS_KEY);
out.writeUTF(ipAddress.getAddress().getHostAddress());
out.writeInt(ipAddress.getPrefixLength());
}
- if (staticIpConfiguration.gateway != null) {
+ if (staticIpConfiguration.getGateway() != null) {
out.writeUTF(GATEWAY_KEY);
out.writeInt(0); // Default route.
out.writeInt(1); // Have a gateway.
- out.writeUTF(staticIpConfiguration.gateway.getHostAddress());
+ out.writeUTF(staticIpConfiguration.getGateway().getHostAddress());
}
- for (InetAddress inetAddr : staticIpConfiguration.dnsServers) {
+ for (InetAddress inetAddr : staticIpConfiguration.getDnsServers()) {
out.writeUTF(DNS_KEY);
out.writeUTF(inetAddr.getHostAddress());
}
@@ -110,7 +111,7 @@
break;
case DHCP:
out.writeUTF(IP_ASSIGNMENT_KEY);
- out.writeUTF(config.ipAssignment.toString());
+ out.writeUTF(config.getIpAssignment().toString());
written = true;
break;
case UNASSIGNED:
@@ -121,13 +122,13 @@
break;
}
- switch (config.proxySettings) {
+ switch (config.getProxySettings()) {
case STATIC:
- ProxyInfo proxyProperties = config.httpProxy;
+ ProxyInfo proxyProperties = config.getHttpProxy();
String exclusionList = ProxyUtils.exclusionListAsString(
proxyProperties.getExclusionList());
out.writeUTF(PROXY_SETTINGS_KEY);
- out.writeUTF(config.proxySettings.toString());
+ out.writeUTF(config.getProxySettings().toString());
out.writeUTF(PROXY_HOST_KEY);
out.writeUTF(proxyProperties.getHost());
out.writeUTF(PROXY_PORT_KEY);
@@ -139,16 +140,16 @@
written = true;
break;
case PAC:
- ProxyInfo proxyPacProperties = config.httpProxy;
+ ProxyInfo proxyPacProperties = config.getHttpProxy();
out.writeUTF(PROXY_SETTINGS_KEY);
- out.writeUTF(config.proxySettings.toString());
+ out.writeUTF(config.getProxySettings().toString());
out.writeUTF(PROXY_PAC_FILE);
out.writeUTF(proxyPacProperties.getPacFileUrl().toString());
written = true;
break;
case NONE:
out.writeUTF(PROXY_SETTINGS_KEY);
- out.writeUTF(config.proxySettings.toString());
+ out.writeUTF(config.getProxySettings().toString());
written = true;
break;
case UNASSIGNED:
@@ -267,11 +268,14 @@
IpAssignment ipAssignment = IpAssignment.DHCP;
ProxySettings proxySettings = ProxySettings.NONE;
StaticIpConfiguration staticIpConfiguration = new StaticIpConfiguration();
+ LinkAddress linkAddress = null;
+ InetAddress gatewayAddress = null;
String proxyHost = null;
String pacFileUrl = null;
int proxyPort = -1;
String exclusionList = null;
String key;
+ final List<InetAddress> dnsServers = new ArrayList<>();
do {
key = in.readUTF();
@@ -286,15 +290,15 @@
} else if (key.equals(IP_ASSIGNMENT_KEY)) {
ipAssignment = IpAssignment.valueOf(in.readUTF());
} else if (key.equals(LINK_ADDRESS_KEY)) {
- LinkAddress linkAddr =
+ LinkAddress parsedLinkAddress =
new LinkAddress(
InetAddresses.parseNumericAddress(in.readUTF()),
in.readInt());
- if (linkAddr.getAddress() instanceof Inet4Address &&
- staticIpConfiguration.ipAddress == null) {
- staticIpConfiguration.ipAddress = linkAddr;
+ if (parsedLinkAddress.getAddress() instanceof Inet4Address
+ && linkAddress == null) {
+ linkAddress = parsedLinkAddress;
} else {
- loge("Non-IPv4 or duplicate address: " + linkAddr);
+ loge("Non-IPv4 or duplicate address: " + parsedLinkAddress);
}
} else if (key.equals(GATEWAY_KEY)) {
LinkAddress dest = null;
@@ -302,8 +306,8 @@
if (version == 1) {
// only supported default gateways - leave the dest/prefix empty
gateway = InetAddresses.parseNumericAddress(in.readUTF());
- if (staticIpConfiguration.gateway == null) {
- staticIpConfiguration.gateway = gateway;
+ if (gatewayAddress == null) {
+ gatewayAddress = gateway;
} else {
loge("Duplicate gateway: " + gateway.getHostAddress());
}
@@ -317,17 +321,18 @@
if (in.readInt() == 1) {
gateway = InetAddresses.parseNumericAddress(in.readUTF());
}
- RouteInfo route = new RouteInfo(dest, gateway);
- if (route.isIPv4Default() &&
- staticIpConfiguration.gateway == null) {
- staticIpConfiguration.gateway = gateway;
+ // If the destination is a default IPv4 route, use the gateway
+ // address unless already set.
+ if (dest.getAddress() instanceof Inet4Address
+ && dest.getPrefixLength() == 0 && gatewayAddress == null) {
+ gatewayAddress = gateway;
} else {
- loge("Non-IPv4 default or duplicate route: " + route);
+ loge("Non-IPv4 default or duplicate route: "
+ + dest.getAddress());
}
}
} else if (key.equals(DNS_KEY)) {
- staticIpConfiguration.dnsServers.add(
- InetAddresses.parseNumericAddress(in.readUTF()));
+ dnsServers.add(InetAddresses.parseNumericAddress(in.readUTF()));
} else if (key.equals(PROXY_SETTINGS_KEY)) {
proxySettings = ProxySettings.valueOf(in.readUTF());
} else if (key.equals(PROXY_HOST_KEY)) {
@@ -348,25 +353,31 @@
}
} while (true);
+ staticIpConfiguration = new StaticIpConfiguration.Builder()
+ .setIpAddress(linkAddress)
+ .setGateway(gatewayAddress)
+ .setDnsServers(dnsServers)
+ .build();
+
if (uniqueToken != null) {
IpConfiguration config = new IpConfiguration();
networks.put(uniqueToken, config);
switch (ipAssignment) {
case STATIC:
- config.staticIpConfiguration = staticIpConfiguration;
- config.ipAssignment = ipAssignment;
+ config.setStaticIpConfiguration(staticIpConfiguration);
+ config.setIpAssignment(ipAssignment);
break;
case DHCP:
- config.ipAssignment = ipAssignment;
+ config.setIpAssignment(ipAssignment);
break;
case UNASSIGNED:
loge("BUG: Found UNASSIGNED IP on file, use DHCP");
- config.ipAssignment = IpAssignment.DHCP;
+ config.setIpAssignment(IpAssignment.DHCP);
break;
default:
loge("Ignore invalid ip assignment while reading.");
- config.ipAssignment = IpAssignment.UNASSIGNED;
+ config.setIpAssignment(IpAssignment.UNASSIGNED);
break;
}
@@ -374,25 +385,25 @@
case STATIC:
ProxyInfo proxyInfo = ProxyInfo.buildDirectProxy(proxyHost, proxyPort,
ProxyUtils.exclusionStringAsList(exclusionList));
- config.proxySettings = proxySettings;
- config.httpProxy = proxyInfo;
+ config.setProxySettings(proxySettings);
+ config.setHttpProxy(proxyInfo);
break;
case PAC:
ProxyInfo proxyPacProperties =
ProxyInfo.buildPacProxy(Uri.parse(pacFileUrl));
- config.proxySettings = proxySettings;
- config.httpProxy = proxyPacProperties;
+ config.setProxySettings(proxySettings);
+ config.setHttpProxy(proxyPacProperties);
break;
case NONE:
- config.proxySettings = proxySettings;
+ config.setProxySettings(proxySettings);
break;
case UNASSIGNED:
loge("BUG: Found UNASSIGNED proxy on file, use NONE");
- config.proxySettings = ProxySettings.NONE;
+ config.setProxySettings(ProxySettings.NONE);
break;
default:
loge("Ignore invalid proxy settings while reading");
- config.proxySettings = ProxySettings.UNASSIGNED;
+ config.setProxySettings(ProxySettings.UNASSIGNED);
break;
}
} else {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index d0aa28b..1b5bb95 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2341,8 +2341,6 @@
params.packageName = packageName;
params.windowAnimations = win.getWindowStyle().getResourceId(
com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
- params.privateFlags |=
- WindowManager.LayoutParams.PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED;
params.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
// Setting as trusted overlay to let touches pass through. This is safe because this
// window is controlled by the system.
diff --git a/services/core/java/com/android/server/timezone/TimeZoneUpdateIdler.java b/services/core/java/com/android/server/timezone/TimeZoneUpdateIdler.java
index a7767a4..23e3eba 100644
--- a/services/core/java/com/android/server/timezone/TimeZoneUpdateIdler.java
+++ b/services/core/java/com/android/server/timezone/TimeZoneUpdateIdler.java
@@ -16,8 +16,6 @@
package com.android.server.timezone;
-import com.android.server.LocalServices;
-
import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
@@ -26,6 +24,8 @@
import android.content.Context;
import android.util.Slog;
+import com.android.server.LocalServices;
+
/**
* A JobService used to trigger time zone rules update work when a device falls idle.
*/
@@ -55,7 +55,7 @@
@Override
public boolean onStopJob(JobParameters params) {
// Reschedule if stopped unless it was cancelled due to unschedule().
- boolean reschedule = params.getStopReason() != JobParameters.REASON_CANCELED;
+ boolean reschedule = params.getStopReason() != JobParameters.STOP_REASON_CANCELLED_BY_APP;
Slog.d(TAG, "onStopJob() called: Reschedule=" + reschedule);
return reschedule;
}
diff --git a/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java b/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java
index 4fa920e..f054c57 100644
--- a/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java
@@ -47,7 +47,7 @@
@NonNull String providerName,
@NonNull LocationTimeZoneProviderProxy proxy) {
super(providerMetricsLogger, threadingDomain, providerName,
- new ZoneInfoDbTimeZoneIdValidator());
+ new ZoneInfoDbTimeZoneProviderEventPreProcessor());
mProxy = Objects.requireNonNull(proxy);
}
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
index cc815dc6..e116a87 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
@@ -20,7 +20,6 @@
import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_SUCCESS_KEY;
import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.debugLog;
-import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.infoLog;
import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.warnLog;
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
@@ -86,18 +85,6 @@
}
/**
- * Used by {@link LocationTimeZoneProvider} to check if time zone IDs are understood
- * by the platform.
- */
- interface TimeZoneIdValidator {
-
- /**
- * Returns whether {@code timeZoneId} is supported by the platform or not.
- */
- boolean isValid(@NonNull String timeZoneId);
- }
-
- /**
* Listener interface used to log provider events for metrics.
*/
interface ProviderMetricsLogger {
@@ -386,19 +373,20 @@
// Non-null and effectively final after initialize() is called.
ProviderListener mProviderListener;
- @NonNull private TimeZoneIdValidator mTimeZoneIdValidator;
+ @NonNull private final TimeZoneProviderEventPreProcessor mTimeZoneProviderEventPreProcessor;
/** Creates the instance. */
LocationTimeZoneProvider(@NonNull ProviderMetricsLogger providerMetricsLogger,
@NonNull ThreadingDomain threadingDomain,
@NonNull String providerName,
- @NonNull TimeZoneIdValidator timeZoneIdValidator) {
+ @NonNull TimeZoneProviderEventPreProcessor timeZoneProviderEventPreProcessor) {
mThreadingDomain = Objects.requireNonNull(threadingDomain);
mProviderMetricsLogger = Objects.requireNonNull(providerMetricsLogger);
mInitializationTimeoutQueue = threadingDomain.createSingleRunnableQueue();
mSharedLock = threadingDomain.getLockObject();
mProviderName = Objects.requireNonNull(providerName);
- mTimeZoneIdValidator = Objects.requireNonNull(timeZoneIdValidator);
+ mTimeZoneProviderEventPreProcessor =
+ Objects.requireNonNull(timeZoneProviderEventPreProcessor);
}
/**
@@ -639,24 +627,8 @@
mThreadingDomain.assertCurrentThread();
Objects.requireNonNull(timeZoneProviderEvent);
- // If the provider has made a suggestion with unknown time zone IDs it cannot be used to set
- // the device's time zone. This logic prevents bad time zone IDs entering the time zone
- // detection logic from third party code.
- //
- // An event containing an unknown time zone ID could occur if the provider is using a
- // different TZDB version than the device. Provider developers are expected to take steps to
- // avoid version skew problem, e.g. by ensuring atomic updates with the platform time zone
- // rules, or providing IDs based on the device's TZDB version, so this is not considered a
- // common case.
- //
- // Treating a suggestion containing unknown time zone IDs as "uncertain" in the primary
- // enables immediate failover to a secondary provider, one that might provide valid IDs for
- // the same location, which should provide better behavior than just ignoring the event.
- if (hasInvalidTimeZones(timeZoneProviderEvent)) {
- infoLog("event=" + timeZoneProviderEvent + " has unsupported time zones. "
- + "Replacing it with uncertain event.");
- timeZoneProviderEvent = TimeZoneProviderEvent.createUncertainEvent();
- }
+ timeZoneProviderEvent =
+ mTimeZoneProviderEventPreProcessor.preProcess(timeZoneProviderEvent);
synchronized (mSharedLock) {
debugLog("handleTimeZoneProviderEvent: mProviderName=" + mProviderName
@@ -755,20 +727,6 @@
}
}
- private boolean hasInvalidTimeZones(@NonNull TimeZoneProviderEvent event) {
- if (event.getSuggestion() == null) {
- return false;
- }
-
- for (String timeZone : event.getSuggestion().getTimeZoneIds()) {
- if (!mTimeZoneIdValidator.isValid(timeZone)) {
- return true;
- }
- }
-
- return false;
- }
-
@GuardedBy("mSharedLock")
private void assertIsStarted() {
ProviderState currentState = mCurrentState.get();
diff --git a/services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneIdValidator.java b/services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderEventPreProcessor.java
similarity index 64%
rename from services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneIdValidator.java
rename to services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderEventPreProcessor.java
index cab5ad2..951e9d0 100644
--- a/services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneIdValidator.java
+++ b/services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderEventPreProcessor.java
@@ -18,13 +18,16 @@
import android.annotation.NonNull;
-import com.android.i18n.timezone.ZoneInfoDb;
+/**
+ * Used by {@link LocationTimeZoneProvider} to ensure that all time zone IDs are understood by the
+ * platform.
+ */
+public interface TimeZoneProviderEventPreProcessor {
-class ZoneInfoDbTimeZoneIdValidator implements
- LocationTimeZoneProvider.TimeZoneIdValidator {
+ /**
+ * May return uncertain event if {@code timeZoneProviderEvent} is ill-formed or drop/rewrite
+ * time zone IDs.
+ */
+ TimeZoneProviderEvent preProcess(@NonNull TimeZoneProviderEvent timeZoneProviderEvent);
- @Override
- public boolean isValid(@NonNull String timeZoneId) {
- return ZoneInfoDb.getInstance().hasTimeZone(timeZoneId);
- }
}
diff --git a/services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessor.java b/services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessor.java
new file mode 100644
index 0000000..0f4367d
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessor.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezonedetector.location;
+
+import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.infoLog;
+
+import android.annotation.NonNull;
+
+import com.android.i18n.timezone.ZoneInfoDb;
+
+/**
+ * {@link TimeZoneProviderEventPreProcessor} implementation which makes validations against
+ * {@link ZoneInfoDb}.
+ */
+public class ZoneInfoDbTimeZoneProviderEventPreProcessor
+ implements TimeZoneProviderEventPreProcessor {
+
+ /**
+ * Returns uncertain event if {@code event} has at least one unsupported time zone ID.
+ */
+ @Override
+ public TimeZoneProviderEvent preProcess(@NonNull TimeZoneProviderEvent event) {
+ if (event.getSuggestion() == null || event.getSuggestion().getTimeZoneIds().isEmpty()) {
+ return event;
+ }
+
+ // If the provider has made a suggestion with unknown time zone IDs it cannot be used to set
+ // the device's time zone. This logic prevents bad time zone IDs entering the time zone
+ // detection logic from third party code.
+ //
+ // An event containing an unknown time zone ID could occur if the provider is using a
+ // different TZDB version than the device. Provider developers are expected to take steps to
+ // avoid version skew problem, e.g. by ensuring atomic updates with the platform time zone
+ // rules, or providing IDs based on the device's TZDB version, so this is not considered a
+ // common case.
+ //
+ // Treating a suggestion containing unknown time zone IDs as "uncertain" in the primary
+ // enables immediate failover to a secondary provider, one that might provide valid IDs for
+ // the same location, which should provide better behavior than just ignoring the event.
+ if (hasInvalidZones(event)) {
+ return TimeZoneProviderEvent.createUncertainEvent();
+ }
+
+ return event;
+ }
+
+ private static boolean hasInvalidZones(TimeZoneProviderEvent event) {
+ for (String timeZone : event.getSuggestion().getTimeZoneIds()) {
+ if (!ZoneInfoDb.getInstance().hasTimeZone(timeZone)) {
+ infoLog("event=" + event + " has unsupported zone(" + timeZone + ")");
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 46913eb..29c5cec 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -251,6 +251,7 @@
import com.android.server.am.ActivityManagerService;
import com.android.server.am.ActivityManagerServiceDumpProcessesProto;
import com.android.server.am.AppTimeTracker;
+import com.android.server.am.AssistDataRequester;
import com.android.server.am.BaseErrorDialog;
import com.android.server.am.PendingIntentController;
import com.android.server.am.PendingIntentRecord;
@@ -2828,10 +2829,45 @@
@Override
public boolean requestAssistContextExtras(int requestType, IAssistDataReceiver receiver,
- Bundle receiverExtras, IBinder activityToken, boolean focused, boolean newSessionId) {
+ Bundle receiverExtras, IBinder activityToken, boolean checkActivityIsTop,
+ boolean newSessionId) {
return enqueueAssistContext(requestType, null, null, receiver, receiverExtras,
- activityToken, focused, newSessionId, UserHandle.getCallingUserId(), null,
- PENDING_ASSIST_EXTRAS_LONG_TIMEOUT, 0) != null;
+ activityToken, checkActivityIsTop, newSessionId, UserHandle.getCallingUserId(),
+ null, PENDING_ASSIST_EXTRAS_LONG_TIMEOUT, 0) != null;
+ }
+
+ @Override
+ public boolean requestAssistDataForTask(IAssistDataReceiver receiver, int taskId,
+ String callingPackageName) {
+ mAmInternal.enforceCallingPermission(android.Manifest.permission.GET_TOP_ACTIVITY_INFO,
+ "requestAssistDataForTask()");
+ final long callingId = Binder.clearCallingIdentity();
+ LocalService.ActivityTokens tokens = null;
+ try {
+ tokens = mInternal.getTopActivityForTask(taskId);
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
+ }
+ if (tokens == null) {
+ Log.e(TAG, "Could not find activity for task " + taskId);
+ return false;
+ }
+
+ final AssistDataReceiverProxy proxy =
+ new AssistDataReceiverProxy(receiver, callingPackageName);
+ Object lock = new Object();
+ AssistDataRequester requester = new AssistDataRequester(mContext, mWindowManager,
+ getAppOpsManager(), proxy, lock, AppOpsManager.OP_ASSIST_STRUCTURE,
+ AppOpsManager.OP_NONE);
+
+ List<IBinder> topActivityToken = new ArrayList<>();
+ topActivityToken.add(tokens.getActivityToken());
+ requester.requestAssistData(topActivityToken, true /* fetchData */,
+ false /* fetchScreenshot */, true /* allowFetchData */,
+ false /* allowFetchScreenshot*/, true /* ignoreFocusCheck */,
+ Binder.getCallingUid(), callingPackageName);
+
+ return true;
}
@Override
@@ -2845,7 +2881,7 @@
@Override
public Bundle getAssistContextExtras(int requestType) {
PendingAssistExtras pae = enqueueAssistContext(requestType, null, null, null,
- null, null, true /* focused */, true /* newSessionId */,
+ null, null, true /* checkActivityIsTop */, true /* newSessionId */,
UserHandle.getCallingUserId(), null, PENDING_ASSIST_EXTRAS_TIMEOUT, 0);
if (pae == null) {
return null;
@@ -3048,8 +3084,8 @@
private PendingAssistExtras enqueueAssistContext(int requestType, Intent intent, String hint,
IAssistDataReceiver receiver, Bundle receiverExtras, IBinder activityToken,
- boolean focused, boolean newSessionId, int userHandle, Bundle args, long timeout,
- int flags) {
+ boolean checkActivityIsTop, boolean newSessionId, int userHandle, Bundle args,
+ long timeout, int flags) {
mAmInternal.enforceCallingPermission(android.Manifest.permission.GET_TOP_ACTIVITY_INFO,
"enqueueAssistContext()");
@@ -3065,7 +3101,7 @@
Slog.w(TAG, "getAssistContextExtras failed: no process for " + activity);
return null;
}
- if (focused) {
+ if (checkActivityIsTop) {
if (activityToken != null) {
ActivityRecord caller = ActivityRecord.forTokenLocked(activityToken);
if (activity != caller) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
index 7925b69..0fcda81 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
@@ -660,15 +660,19 @@
new JobInfo.Builder(101, new ComponentName("foo", "bar")).build());
markImplicitConstraintsSatisfied(job, false);
- job.setBackgroundNotRestrictedConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
+ job.setBackgroundNotRestrictedConstraintSatisfied(
+ sElapsedRealtimeClock.millis(), false, false);
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_BACKGROUND_NOT_RESTRICTED));
- job.setBackgroundNotRestrictedConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
+ job.setBackgroundNotRestrictedConstraintSatisfied(
+ sElapsedRealtimeClock.millis(), true, false);
assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_BACKGROUND_NOT_RESTRICTED));
markImplicitConstraintsSatisfied(job, true);
- job.setBackgroundNotRestrictedConstraintSatisfied(sElapsedRealtimeClock.millis(), false);
+ job.setBackgroundNotRestrictedConstraintSatisfied(
+ sElapsedRealtimeClock.millis(), false, false);
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_BACKGROUND_NOT_RESTRICTED));
- job.setBackgroundNotRestrictedConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
+ job.setBackgroundNotRestrictedConstraintSatisfied(
+ sElapsedRealtimeClock.millis(), true, false);
assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_BACKGROUND_NOT_RESTRICTED));
}
@@ -677,7 +681,7 @@
job.setDeviceNotDozingConstraintSatisfied(
sElapsedRealtimeClock.millis(), isSatisfied, false);
job.setBackgroundNotRestrictedConstraintSatisfied(
- sElapsedRealtimeClock.millis(), isSatisfied);
+ sElapsedRealtimeClock.millis(), isSatisfied, false);
}
private static JobStatus createJobStatus(long earliestRunTimeElapsedMillis,
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index f73af53..ee1a4f4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -384,7 +384,8 @@
// Make sure Doze and background-not-restricted don't affect tests.
js.setDeviceNotDozingConstraintSatisfied(/* nowElapsed */ sElapsedRealtimeClock.millis(),
/* state */ true, /* allowlisted */false);
- js.setBackgroundNotRestrictedConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
+ js.setBackgroundNotRestrictedConstraintSatisfied(
+ sElapsedRealtimeClock.millis(), true, false);
return js;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeLocationPowerSaveModeHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeLocationPowerSaveModeHelper.java
index 0597443..ef48646 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeLocationPowerSaveModeHelper.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeLocationPowerSaveModeHelper.java
@@ -19,8 +19,6 @@
import android.os.IPowerManager;
import android.os.PowerManager.LocationPowerSaveMode;
-import com.android.server.location.eventlog.LocationEventLog;
-
/**
* Version of LocationPowerSaveModeHelper for testing. Power save mode is initialized as "no
* change".
@@ -30,8 +28,7 @@
@LocationPowerSaveMode
private int mLocationPowerSaveMode;
- public FakeLocationPowerSaveModeHelper(LocationEventLog locationEventLog) {
- super(locationEventLog);
+ public FakeLocationPowerSaveModeHelper() {
mLocationPowerSaveMode = IPowerManager.LOCATION_MODE_NO_CHANGE;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/SystemLocationPowerSaveModeHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/SystemLocationPowerSaveModeHelperTest.java
index 6156ba9..28da027 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/SystemLocationPowerSaveModeHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/SystemLocationPowerSaveModeHelperTest.java
@@ -43,7 +43,6 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.server.LocalServices;
-import com.android.server.location.eventlog.LocationEventLog;
import com.android.server.location.injector.LocationPowerSaveModeHelper.LocationPowerSaveModeChangedListener;
import org.junit.After;
@@ -85,7 +84,7 @@
Context context = mock(Context.class);
doReturn(powerManager).when(context).getSystemService(PowerManager.class);
- mHelper = new SystemLocationPowerSaveModeHelper(context, new LocationEventLog());
+ mHelper = new SystemLocationPowerSaveModeHelper(context);
mHelper.onSystemReady();
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
index 1f102ac..ae70dad 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
@@ -16,8 +16,6 @@
package com.android.server.location.injector;
-import com.android.server.location.eventlog.LocationEventLog;
-
public class TestInjector implements Injector {
private final FakeUserInfoHelper mUserInfoHelper;
@@ -35,17 +33,13 @@
private final LocationUsageLogger mLocationUsageLogger;
public TestInjector() {
- this(new LocationEventLog());
- }
-
- public TestInjector(LocationEventLog eventLog) {
mUserInfoHelper = new FakeUserInfoHelper();
mAlarmHelper = new FakeAlarmHelper();
mAppOpsHelper = new FakeAppOpsHelper();
mLocationPermissionsHelper = new FakeLocationPermissionsHelper(mAppOpsHelper);
mSettingsHelper = new FakeSettingsHelper();
mAppForegroundHelper = new FakeAppForegroundHelper();
- mLocationPowerSaveModeHelper = new FakeLocationPowerSaveModeHelper(eventLog);
+ mLocationPowerSaveModeHelper = new FakeLocationPowerSaveModeHelper();
mScreenInteractiveHelper = new FakeScreenInteractiveHelper();
mDeviceStationaryHelper = new FakeDeviceStationaryHelper();
mDeviceIdleHelper = new FakeDeviceIdleHelper();
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index 1b58e92..24b85f0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -83,7 +83,6 @@
import com.android.server.FgThread;
import com.android.server.LocalServices;
-import com.android.server.location.eventlog.LocationEventLog;
import com.android.server.location.injector.FakeUserInfoHelper;
import com.android.server.location.injector.TestInjector;
@@ -161,19 +160,17 @@
doReturn(mPowerManager).when(mContext).getSystemService(PowerManager.class);
doReturn(mWakeLock).when(mPowerManager).newWakeLock(anyInt(), anyString());
- LocationEventLog eventLog = new LocationEventLog();
-
- mInjector = new TestInjector(eventLog);
+ mInjector = new TestInjector();
mInjector.getUserInfoHelper().startUser(OTHER_USER);
- mPassive = new PassiveLocationProviderManager(mContext, mInjector, eventLog);
+ mPassive = new PassiveLocationProviderManager(mContext, mInjector);
mPassive.startManager();
mPassive.setRealProvider(new PassiveLocationProvider(mContext));
mProvider = new TestProvider(PROPERTIES, PROVIDER_IDENTITY);
mProvider.setProviderAllowed(true);
- mManager = new LocationProviderManager(mContext, mInjector, eventLog, NAME, mPassive);
+ mManager = new LocationProviderManager(mContext, mInjector, NAME, mPassive);
mManager.startManager();
mManager.setRealProvider(mProvider);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
index c3cca64..04e0151 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
@@ -36,7 +36,6 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.server.location.eventlog.LocationEventLog;
import com.android.server.location.injector.TestInjector;
import com.android.server.location.test.FakeProvider;
@@ -77,7 +76,7 @@
mDelegateProvider = new FakeProvider(mDelegate);
mProvider = new StationaryThrottlingLocationProvider("test_provider", mInjector,
- mDelegateProvider, new LocationEventLog());
+ mDelegateProvider);
mProvider.getController().setListener(mListener);
mProvider.getController().start();
}
diff --git a/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java b/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
index a7b32ac..68a6e60 100644
--- a/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
@@ -96,7 +96,7 @@
case ACTION_JOB_STOPPED:
mTestJobStatus.running = false;
mTestJobStatus.jobId = params.getJobId();
- mTestJobStatus.stopReason = params.getStopReason();
+ mTestJobStatus.stopReason = params.getLegacyStopReason();
break;
}
}
diff --git a/services/tests/servicestests/src/com/android/server/job/MockPriorityJobService.java b/services/tests/servicestests/src/com/android/server/job/MockPriorityJobService.java
index 3ea86f2..87881bf 100644
--- a/services/tests/servicestests/src/com/android/server/job/MockPriorityJobService.java
+++ b/services/tests/servicestests/src/com/android/server/job/MockPriorityJobService.java
@@ -47,7 +47,7 @@
int reason = params.getStopReason();
int event = TestEnvironment.EVENT_STOP_JOB;
Log.d(TAG, "stop reason: " + String.valueOf(reason));
- if (reason == JobParameters.REASON_PREEMPT) {
+ if (reason == JobParameters.STOP_REASON_PREEMPT) {
event = TestEnvironment.EVENT_PREEMPT_JOB;
Log.d(TAG, "preempted " + String.valueOf(params.getJobId()));
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
index 5a100a2..a0e9d97 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
@@ -72,7 +72,6 @@
private TestCallback mTestCallback;
private TestLocationTimeZoneProvider mTestPrimaryLocationTimeZoneProvider;
private TestLocationTimeZoneProvider mTestSecondaryLocationTimeZoneProvider;
- private FakeTimeZoneIdValidator mTimeZoneAvailabilityChecker;
@Before
public void setUp() {
@@ -84,13 +83,10 @@
};
mTestThreadingDomain = new TestThreadingDomain();
mTestCallback = new TestCallback(mTestThreadingDomain);
- mTimeZoneAvailabilityChecker = new FakeTimeZoneIdValidator();
mTestPrimaryLocationTimeZoneProvider = new TestLocationTimeZoneProvider(
- stubbedProviderMetricsLogger, mTestThreadingDomain, "primary",
- mTimeZoneAvailabilityChecker);
+ stubbedProviderMetricsLogger, mTestThreadingDomain, "primary");
mTestSecondaryLocationTimeZoneProvider = new TestLocationTimeZoneProvider(
- stubbedProviderMetricsLogger, mTestThreadingDomain, "secondary",
- mTimeZoneAvailabilityChecker);
+ stubbedProviderMetricsLogger, mTestThreadingDomain, "secondary");
}
@Test
@@ -1185,10 +1181,9 @@
* Creates the instance.
*/
TestLocationTimeZoneProvider(ProviderMetricsLogger providerMetricsLogger,
- ThreadingDomain threadingDomain, String providerName,
- TimeZoneIdValidator timeZoneIdValidator) {
+ ThreadingDomain threadingDomain, String providerName) {
super(providerMetricsLogger, threadingDomain, providerName,
- timeZoneIdValidator);
+ new FakeTimeZoneProviderEventPreProcessor());
}
public void setFailDuringInitialization(boolean failInitialization) {
@@ -1321,14 +1316,4 @@
mTestProviderState.commitLatest();
}
}
-
- private static final class FakeTimeZoneIdValidator
- implements LocationTimeZoneProvider.TimeZoneIdValidator {
-
- @Override
- public boolean isValid(@NonNull String timeZoneId) {
- return true;
- }
-
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/FakeTimeZoneProviderEventPreProcessor.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/FakeTimeZoneProviderEventPreProcessor.java
new file mode 100644
index 0000000..e75d05c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/FakeTimeZoneProviderEventPreProcessor.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezonedetector.location;
+
+/**
+ * Fake implementation of {@link TimeZoneProviderEventPreProcessor} which assumes that all events
+ * are valid or always uncertain if {@link #enterUncertainMode()} was called.
+ */
+public final class FakeTimeZoneProviderEventPreProcessor
+ implements TimeZoneProviderEventPreProcessor {
+
+ private boolean mIsUncertain = false;
+
+ @Override
+ public TimeZoneProviderEvent preProcess(TimeZoneProviderEvent timeZoneProviderEvent) {
+ if (mIsUncertain) {
+ return TimeZoneProviderEvent.createUncertainEvent();
+ }
+ return timeZoneProviderEvent;
+ }
+
+ public void enterUncertainMode() {
+ mIsUncertain = true;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java
index d13a04e..0edb559 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java
@@ -52,10 +52,8 @@
import java.time.Duration;
import java.util.Arrays;
-import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
-import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
/**
@@ -68,13 +66,13 @@
private TestThreadingDomain mTestThreadingDomain;
private TestProviderListener mProviderListener;
- private FakeTimeZoneIdValidator mTimeZoneAvailabilityChecker;
+ private FakeTimeZoneProviderEventPreProcessor mTimeZoneProviderEventPreProcessor;
@Before
public void setUp() {
mTestThreadingDomain = new TestThreadingDomain();
mProviderListener = new TestProviderListener();
- mTimeZoneAvailabilityChecker = new FakeTimeZoneIdValidator();
+ mTimeZoneProviderEventPreProcessor = new FakeTimeZoneProviderEventPreProcessor();
}
@Test
@@ -82,9 +80,10 @@
String providerName = "arbitrary";
RecordingProviderMetricsLogger providerMetricsLogger = new RecordingProviderMetricsLogger();
TestLocationTimeZoneProvider provider = new TestLocationTimeZoneProvider(
- providerMetricsLogger, mTestThreadingDomain, providerName,
- mTimeZoneAvailabilityChecker);
- mTimeZoneAvailabilityChecker.validIds("Europe/London");
+ providerMetricsLogger,
+ mTestThreadingDomain,
+ providerName,
+ mTimeZoneProviderEventPreProcessor);
// initialize()
provider.initialize(mProviderListener);
@@ -174,8 +173,10 @@
String providerName = "primary";
StubbedProviderMetricsLogger providerMetricsLogger = new StubbedProviderMetricsLogger();
TestLocationTimeZoneProvider provider = new TestLocationTimeZoneProvider(
- providerMetricsLogger, mTestThreadingDomain, providerName,
- mTimeZoneAvailabilityChecker);
+ providerMetricsLogger,
+ mTestThreadingDomain,
+ providerName,
+ mTimeZoneProviderEventPreProcessor);
TestCommand testCommand = TestCommand.createForTests("test", new Bundle());
AtomicReference<Bundle> resultReference = new AtomicReference<>();
@@ -193,10 +194,11 @@
String providerName = "primary";
StubbedProviderMetricsLogger providerMetricsLogger = new StubbedProviderMetricsLogger();
TestLocationTimeZoneProvider provider = new TestLocationTimeZoneProvider(
- providerMetricsLogger, mTestThreadingDomain, providerName,
- mTimeZoneAvailabilityChecker);
+ providerMetricsLogger,
+ mTestThreadingDomain,
+ providerName,
+ mTimeZoneProviderEventPreProcessor);
provider.setStateChangeRecordingEnabled(true);
- mTimeZoneAvailabilityChecker.validIds("Europe/London");
// initialize()
provider.initialize(mProviderListener);
@@ -234,14 +236,17 @@
}
@Test
- public void considerSuggestionWithInvalidTimeZoneIdsAsUncertain() {
+ public void entersUncertainState_whenEventHasUnsupportedZones() {
String providerName = "primary";
StubbedProviderMetricsLogger providerMetricsLogger = new StubbedProviderMetricsLogger();
TestLocationTimeZoneProvider provider = new TestLocationTimeZoneProvider(
- providerMetricsLogger, mTestThreadingDomain, providerName,
- mTimeZoneAvailabilityChecker);
+ providerMetricsLogger,
+ mTestThreadingDomain,
+ providerName,
+ mTimeZoneProviderEventPreProcessor);
provider.setStateChangeRecordingEnabled(true);
provider.initialize(mProviderListener);
+ mTimeZoneProviderEventPreProcessor.enterUncertainMode();
ConfigurationInternal config = USER1_CONFIG_GEO_DETECTION_ENABLED;
Duration arbitraryInitializationTimeout = Duration.ofMinutes(5);
@@ -309,8 +314,9 @@
TestLocationTimeZoneProvider(@NonNull ProviderMetricsLogger providerMetricsLogger,
@NonNull ThreadingDomain threadingDomain,
@NonNull String providerName,
- @NonNull TimeZoneIdValidator timeZoneIdValidator) {
- super(providerMetricsLogger, threadingDomain, providerName, timeZoneIdValidator);
+ @NonNull TimeZoneProviderEventPreProcessor timeZoneProviderEventPreProcessor) {
+ super(providerMetricsLogger,
+ threadingDomain, providerName, timeZoneProviderEventPreProcessor);
}
@Override
@@ -367,20 +373,6 @@
}
}
- private static final class FakeTimeZoneIdValidator
- implements LocationTimeZoneProvider.TimeZoneIdValidator {
- private final Set<String> mValidTimeZoneIds = new HashSet<>();
-
- @Override
- public boolean isValid(@NonNull String timeZoneId) {
- return mValidTimeZoneIds.contains(timeZoneId);
- }
-
- public void validIds(String... timeZoneIdss) {
- mValidTimeZoneIds.addAll(asList(timeZoneIdss));
- }
- }
-
private static class StubbedProviderMetricsLogger implements
LocationTimeZoneProvider.ProviderMetricsLogger {
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneIdValidatorTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneIdValidatorTest.java
deleted file mode 100644
index 5561b2c..0000000
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneIdValidatorTest.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.timezonedetector.location;
-
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import android.platform.test.annotations.Presubmit;
-
-import org.junit.Test;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.TimeZone;
-
-@Presubmit
-public class ZoneInfoDbTimeZoneIdValidatorTest {
- private final LocationTimeZoneProvider.TimeZoneIdValidator mTzChecker =
- new ZoneInfoDbTimeZoneIdValidator();
-
- @Test
- public void timeZoneIdsFromZoneInfoDbAreValid() {
- for (String timeZone : TimeZone.getAvailableIDs()) {
- assertWithMessage("Time zone %s should be supported", timeZone)
- .that(mTzChecker.isValid(timeZone)).isTrue();
- }
- }
-
- @Test
- public void nonExistingZones_areNotSupported() {
- List<String> nonExistingTimeZones = Arrays.asList(
- "SystemV/HST10", "Atlantic/Atlantis", "EUROPE/LONDON", "Etc/GMT-5:30"
- );
-
- for (String timeZone : nonExistingTimeZones) {
- assertWithMessage(timeZone + " is not a valid time zone")
- .that(mTzChecker.isValid(timeZone))
- .isFalse();
- }
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessorTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessorTest.java
new file mode 100644
index 0000000..173705be
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessorTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezonedetector.location;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.platform.test.annotations.Presubmit;
+import android.service.timezone.TimeZoneProviderSuggestion;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.TimeZone;
+
+/** Tests for {@link ZoneInfoDbTimeZoneProviderEventPreProcessor}. */
+@Presubmit
+public class ZoneInfoDbTimeZoneProviderEventPreProcessorTest {
+
+ private static final long ARBITRARY_TIME_MILLIS = 11223344;
+
+ private final ZoneInfoDbTimeZoneProviderEventPreProcessor mPreProcessor =
+ new ZoneInfoDbTimeZoneProviderEventPreProcessor();
+
+ @Test
+ public void timeZoneIdsFromZoneInfoDbAreValid() {
+ for (String timeZone : TimeZone.getAvailableIDs()) {
+ TimeZoneProviderEvent event = timeZoneProviderEvent(timeZone);
+ assertWithMessage("Time zone %s should be supported", timeZone)
+ .that(mPreProcessor.preProcess(event)).isEqualTo(event);
+ }
+ }
+
+ @Test
+ public void eventWithNonExistingZones_areMappedToUncertainEvent() {
+ List<String> nonExistingTimeZones = Arrays.asList(
+ "SystemV/HST10", "Atlantic/Atlantis", "EUROPE/LONDON", "Etc/GMT-5:30");
+
+ for (String timeZone : nonExistingTimeZones) {
+ TimeZoneProviderEvent event = timeZoneProviderEvent(timeZone);
+
+ assertWithMessage(timeZone + " is not a valid time zone")
+ .that(mPreProcessor.preProcess(event))
+ .isEqualTo(TimeZoneProviderEvent.createUncertainEvent());
+ }
+ }
+
+ private static TimeZoneProviderEvent timeZoneProviderEvent(String... timeZoneIds) {
+ return TimeZoneProviderEvent.createSuggestionEvent(
+ new TimeZoneProviderSuggestion.Builder()
+ .setTimeZoneIds(Arrays.asList(timeZoneIds))
+ .setElapsedRealtimeMillis(ARBITRARY_TIME_MILLIS)
+ .build());
+ }
+
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
index b9ffd65..a37d5c8 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
@@ -31,9 +31,7 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.media.session.MediaSession;
import android.os.Build;
-import android.os.Process;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
@@ -73,7 +71,6 @@
private NotificationRecord mRecordMinCallNonInterruptive;
private NotificationRecord mRecordMinCall;
private NotificationRecord mRecordHighCall;
- private NotificationRecord mRecordDefaultMedia;
private NotificationRecord mRecordEmail;
private NotificationRecord mRecordInlineReply;
private NotificationRecord mRecordSms;
@@ -139,15 +136,6 @@
new UserHandle(userId), "", 1999), getDefaultChannel());
mRecordHighCall.setSystemImportance(NotificationManager.IMPORTANCE_HIGH);
- Notification n3 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
- .setStyle(new Notification.MediaStyle()
- .setMediaSession(new MediaSession.Token(Process.myUid(), null)))
- .build();
- mRecordDefaultMedia = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
- pkg2, 1, "media", uid2, uid2, n3, new UserHandle(userId),
- "", 1499), getDefaultChannel());
- mRecordDefaultMedia.setSystemImportance(NotificationManager.IMPORTANCE_DEFAULT);
-
Notification n4 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setStyle(new Notification.MessagingStyle("sender!")).build();
mRecordInlineReply = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
@@ -218,7 +206,7 @@
.setStyle(new Notification.MediaStyle())
.build();
mNoMediaSessionMedia = new NotificationRecord(mContext, new StatusBarNotification(
- pkg2, pkg2, 1, "cheater", uid2, uid2, n12, new UserHandle(userId),
+ pkg2, pkg2, 1, "media", uid2, uid2, n12, new UserHandle(userId),
"", 9258), getDefaultChannel());
mNoMediaSessionMedia.setSystemImportance(NotificationManager.IMPORTANCE_DEFAULT);
@@ -247,7 +235,6 @@
final List<NotificationRecord> expected = new ArrayList<>();
expected.add(mRecordColorizedCall);
expected.add(mRecordColorized);
- expected.add(mRecordDefaultMedia);
expected.add(mRecordHighCall);
expected.add(mRecordInlineReply);
if (mRecordSms != null) {
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index f7580d7..a46621a 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -630,7 +630,7 @@
D2D_SHARING_STARRED_CONTACTS,
D2D_SHARING_ALL
})
- public @interface DeviceToDeviceStatusSharing {}
+ public @interface DeviceToDeviceStatusSharingPreference {}
/**
* TelephonyProvider column name for device to device sharing status.
@@ -3415,29 +3415,31 @@
* app uses this method to indicate with whom they wish to share device to device status
* information.
* @param sharing the status sharing preference
- * @param subId the unique Subscription ID in database
+ * @param subscriptionId the unique Subscription ID in database
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
- public void setDeviceToDeviceStatusSharing(@DeviceToDeviceStatusSharing int sharing,
- int subId) {
+ public void setDeviceToDeviceStatusSharingPreference(
+ @DeviceToDeviceStatusSharingPreference int sharing, int subscriptionId) {
if (VDBG) {
- logd("[setDeviceToDeviceStatusSharing] + sharing: " + sharing + " subId: " + subId);
+ logd("[setDeviceToDeviceStatusSharing] + sharing: " + sharing + " subId: "
+ + subscriptionId);
}
- setSubscriptionPropertyHelper(subId, "setDeviceToDeviceSharingStatus",
- (iSub)->iSub.setDeviceToDeviceStatusSharing(sharing, subId));
+ setSubscriptionPropertyHelper(subscriptionId, "setDeviceToDeviceSharingStatus",
+ (iSub)->iSub.setDeviceToDeviceStatusSharing(sharing, subscriptionId));
}
/**
* Returns the user-chosen device to device status sharing preference
- * @param subId Subscription id of subscription
+ * @param subscriptionId Subscription id of subscription
* @return The device to device status sharing preference
*/
- public @DeviceToDeviceStatusSharing int getDeviceToDeviceStatusSharing(int subId) {
+ public @DeviceToDeviceStatusSharingPreference int getDeviceToDeviceStatusSharingPreference(
+ int subscriptionId) {
if (VDBG) {
- logd("[getDeviceToDeviceStatusSharing] + subId: " + subId);
+ logd("[getDeviceToDeviceStatusSharing] + subId: " + subscriptionId);
}
- return getIntegerSubscriptionProperty(subId, D2D_STATUS_SHARING, D2D_SHARING_DISABLED,
- mContext);
+ return getIntegerSubscriptionProperty(subscriptionId, D2D_STATUS_SHARING,
+ D2D_SHARING_DISABLED, mContext);
}
/**
diff --git a/tests/net/common/java/android/net/NetworkAgentConfigTest.kt b/tests/net/common/java/android/net/NetworkAgentConfigTest.kt
index a4d8353..fd126ad 100644
--- a/tests/net/common/java/android/net/NetworkAgentConfigTest.kt
+++ b/tests/net/common/java/android/net/NetworkAgentConfigTest.kt
@@ -44,7 +44,7 @@
setPartialConnectivityAcceptable(false)
setUnvalidatedConnectivityAcceptable(true)
}.build()
- assertParcelSane(config, 10)
+ assertParcelSane(config, 12)
}
@Test @IgnoreUpTo(Build.VERSION_CODES.Q)
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index d40b88c..f161e52 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -531,11 +531,22 @@
assertFalse(nc1.equalsNetCapabilities(nc2));
nc2.addUnwantedCapability(NET_CAPABILITY_INTERNET);
assertTrue(nc1.equalsNetCapabilities(nc2));
+ if (isAtLeastS()) {
+ // Remove a required capability doesn't affect unwanted capabilities.
+ // This is a behaviour change from S.
+ nc1.removeCapability(NET_CAPABILITY_INTERNET);
+ assertTrue(nc1.equalsNetCapabilities(nc2));
- nc1.removeCapability(NET_CAPABILITY_INTERNET);
- assertFalse(nc1.equalsNetCapabilities(nc2));
- nc2.removeCapability(NET_CAPABILITY_INTERNET);
- assertTrue(nc1.equalsNetCapabilities(nc2));
+ nc1.removeUnwantedCapability(NET_CAPABILITY_INTERNET);
+ assertFalse(nc1.equalsNetCapabilities(nc2));
+ nc2.removeUnwantedCapability(NET_CAPABILITY_INTERNET);
+ assertTrue(nc1.equalsNetCapabilities(nc2));
+ } else {
+ nc1.removeCapability(NET_CAPABILITY_INTERNET);
+ assertFalse(nc1.equalsNetCapabilities(nc2));
+ nc2.removeCapability(NET_CAPABILITY_INTERNET);
+ assertTrue(nc1.equalsNetCapabilities(nc2));
+ }
}
@Test
@@ -596,11 +607,20 @@
// This will effectively move NOT_ROAMING capability from required to unwanted for nc1.
nc1.addUnwantedCapability(NET_CAPABILITY_NOT_ROAMING);
- nc2.combineCapabilities(nc1);
- // We will get this capability in both requested and unwanted lists thus this request
- // will never be satisfied.
- assertTrue(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING));
- assertTrue(nc2.hasUnwantedCapability(NET_CAPABILITY_NOT_ROAMING));
+ if (isAtLeastS()) {
+ // From S, it is not allowed to have the same capability in both wanted and
+ // unwanted list.
+ assertThrows(IllegalArgumentException.class, () -> nc2.combineCapabilities(nc1));
+ } else {
+ nc2.combineCapabilities(nc1);
+ // We will get this capability in both requested and unwanted lists thus this request
+ // will never be satisfied.
+ assertTrue(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+ assertTrue(nc2.hasUnwantedCapability(NET_CAPABILITY_NOT_ROAMING));
+ }
+
+ // Remove unwanted capability to continue other tests.
+ nc1.removeUnwantedCapability(NET_CAPABILITY_NOT_ROAMING);
nc1.setSSID(TEST_SSID);
nc2.combineCapabilities(nc1);