Merge "Add API for TranslationContext" into sc-dev
diff --git a/Android.bp b/Android.bp
index ee5e992..f47ee20 100644
--- a/Android.bp
+++ b/Android.bp
@@ -401,6 +401,13 @@
":platform-compat-native-aidl",
// AIDL sources from external directories
+ ":android.hardware.security.keymint-V1-java-source",
+ ":android.hardware.security.secureclock-V1-java-source",
+ ":android.security.apc-java-source",
+ ":android.security.authorization-java-source",
+ ":android.security.maintenance-java-source",
+ ":android.security.vpnprofilestore-java-source",
+ ":android.system.keystore2-V1-java-source",
":credstore_aidl",
":dumpstate_aidl",
":framework_native_aidl",
@@ -582,13 +589,7 @@
"android.hardware.vibrator-V1.2-java",
"android.hardware.vibrator-V1.3-java",
"android.hardware.vibrator-V2-java",
- "android.security.apc-java",
- "android.security.authorization-java",
- "android.security.maintenance-java",
- "android.security.vpnprofilestore-java",
- "android.system.keystore2-V1-java",
"android.system.suspend.control.internal-java",
- "cameraprotosnano",
"devicepolicyprotosnano",
"com.android.sysprop.apex",
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/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
index 88f21a5..63b3959 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
@@ -40,9 +40,9 @@
/**
* Interface to access and modify the permanent and temporary power save allow list. The two lists
* are kept separately. Apps placed on the permanent allow list are only removed via an explicit
- * {@link #removeFromAllowList(String)} call. Apps allow-listed by default by the system cannot be
- * removed. Apps placed on the temporary allow list are removed from that allow list after a
- * predetermined amount of time.
+ * {@link #removeFromPermanentAllowList(String)} call. Apps allow-listed by default by the system
+ * cannot be removed. Apps placed on the temporary allow list are removed from that allow list after
+ * a predetermined amount of time.
*
* @hide
*/
@@ -402,9 +402,9 @@
*
* @param includingIdle Set to true if the app should be allow-listed from device idle as well
* as other power save restrictions
- * @hide
*/
@NonNull
+ @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
public int[] getAllowListedAppIds(boolean includingIdle) {
try {
if (includingIdle) {
@@ -445,7 +445,7 @@
* @param packageName The app to remove from the allow list
*/
@RequiresPermission(android.Manifest.permission.DEVICE_POWER)
- public void removeFromAllowList(@NonNull String packageName) {
+ public void removeFromPermanentAllowList(@NonNull String packageName) {
try {
mService.removePowerSaveWhitelistApp(packageName);
} catch (RemoteException e) {
@@ -463,8 +463,8 @@
*/
@UserHandleAware
@RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
- public void addToTemporaryAllowList(@NonNull String packageName, long durationMs,
- @ReasonCode int reasonCode, @Nullable String reason) {
+ public void addToTemporaryAllowList(@NonNull String packageName, @ReasonCode int reasonCode,
+ @Nullable String reason, long durationMs) {
try {
mService.addPowerSaveTempWhitelistApp(packageName, durationMs, mContext.getUserId(),
reasonCode, reason);
@@ -488,7 +488,7 @@
@UserHandleAware
@RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
public long addToTemporaryAllowListForEvent(@NonNull String packageName,
- @AllowListEvent int event, @ReasonCode int reasonCode, @Nullable String reason) {
+ @ReasonCode int reasonCode, @Nullable String reason, @AllowListEvent int event) {
try {
switch (event) {
case EVENT_MMS:
diff --git a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
index eba39c7..07231b0 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
@@ -439,12 +439,12 @@
* whitelisted by default by the system cannot be removed.
*
* @param packageName The app to remove from the whitelist
- * @deprecated Use {@link PowerExemptionManager#removeFromAllowList(String)} instead
+ * @deprecated Use {@link PowerExemptionManager#removeFromPermanentAllowList(String)} instead
*/
@Deprecated
@RequiresPermission(android.Manifest.permission.DEVICE_POWER)
public void removeFromWhitelist(@NonNull String packageName) {
- mPowerExemptionManager.removeFromAllowList(packageName);
+ mPowerExemptionManager.removeFromPermanentAllowList(packageName);
}
/**
@@ -455,13 +455,13 @@
* @param reasonCode one of {@link ReasonCode}, use {@link #REASON_UNKNOWN} if not sure.
* @param reason a optional human readable reason string, could be null or empty string.
* @deprecated Use {@link PowerExemptionManager#addToTemporaryAllowList(
- * String, long, int, String)} instead
+ * String, int, String, long)} instead
*/
@Deprecated
@RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
public void whitelistAppTemporarily(@NonNull String packageName, long durationMs,
@ReasonCode int reasonCode, @Nullable String reason) {
- mPowerExemptionManager.addToTemporaryAllowList(packageName, durationMs, reasonCode, reason);
+ mPowerExemptionManager.addToTemporaryAllowList(packageName, reasonCode, reason, durationMs);
}
/**
@@ -470,13 +470,13 @@
* @param packageName The package to add to the temp whitelist
* @param durationMs How long to keep the app on the temp whitelist for (in milliseconds)
* @deprecated Use {@link PowerExemptionManager#addToTemporaryAllowList(
- * String, long, int, String)} instead
+ * String, int, String, long)} instead
*/
@Deprecated
@RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
public void whitelistAppTemporarily(@NonNull String packageName, long durationMs) {
mPowerExemptionManager.addToTemporaryAllowList(
- packageName, durationMs, REASON_UNKNOWN, packageName);
+ packageName, REASON_UNKNOWN, packageName, durationMs);
}
/**
@@ -490,14 +490,14 @@
* used for logging purposes. Could be null or empty string.
* @return The duration (in milliseconds) that the app is whitelisted for
* @deprecated Use {@link PowerExemptionManager#addToTemporaryAllowListForEvent(
- * String, int, int, String)} instead
+ * String, int, String, int)} instead
*/
@Deprecated
@RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
public long whitelistAppTemporarilyForEvent(@NonNull String packageName,
@WhitelistEvent int event, @Nullable String reason) {
return mPowerExemptionManager.addToTemporaryAllowListForEvent(
- packageName, event, REASON_UNKNOWN, reason);
+ packageName, REASON_UNKNOWN, reason, event);
}
/**
@@ -512,14 +512,14 @@
* used for logging purposes. Could be null or empty string.
* @return The duration (in milliseconds) that the app is whitelisted for
* @deprecated Use {@link PowerExemptionManager#addToTemporaryAllowListForEvent(
- * String, int, int, String)} instead
+ * String, int, String, int)} instead
*/
@Deprecated
@RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
public long whitelistAppTemporarilyForEvent(@NonNull String packageName,
@WhitelistEvent int event, @ReasonCode int reasonCode, @Nullable String reason) {
return mPowerExemptionManager.addToTemporaryAllowListForEvent(
- packageName, event, reasonCode, reason);
+ packageName, reasonCode, reason, event);
}
/**
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 d7ca309..dd0ea28 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1729,42 +1729,66 @@
field @Deprecated public static final int secondary_text_dark_nodisable = 17170438; // 0x1060006
field @Deprecated public static final int secondary_text_light = 17170439; // 0x1060007
field @Deprecated public static final int secondary_text_light_nodisable = 17170440; // 0x1060008
- field public static final int system_neutral_0 = 17170485; // 0x1060035
- field public static final int system_neutral_100 = 17170487; // 0x1060037
- field public static final int system_neutral_1000 = 17170496; // 0x1060040
- field public static final int system_neutral_200 = 17170488; // 0x1060038
- field public static final int system_neutral_300 = 17170489; // 0x1060039
- field public static final int system_neutral_400 = 17170490; // 0x106003a
- field public static final int system_neutral_50 = 17170486; // 0x1060036
- field public static final int system_neutral_500 = 17170491; // 0x106003b
- field public static final int system_neutral_600 = 17170492; // 0x106003c
- field public static final int system_neutral_700 = 17170493; // 0x106003d
- field public static final int system_neutral_800 = 17170494; // 0x106003e
- field public static final int system_neutral_900 = 17170495; // 0x106003f
- field public static final int system_primary_0 = 17170461; // 0x106001d
- field public static final int system_primary_100 = 17170463; // 0x106001f
- field public static final int system_primary_1000 = 17170472; // 0x1060028
- field public static final int system_primary_200 = 17170464; // 0x1060020
- field public static final int system_primary_300 = 17170465; // 0x1060021
- field public static final int system_primary_400 = 17170466; // 0x1060022
- field public static final int system_primary_50 = 17170462; // 0x106001e
- field public static final int system_primary_500 = 17170467; // 0x1060023
- field public static final int system_primary_600 = 17170468; // 0x1060024
- field public static final int system_primary_700 = 17170469; // 0x1060025
- field public static final int system_primary_800 = 17170470; // 0x1060026
- field public static final int system_primary_900 = 17170471; // 0x1060027
- field public static final int system_secondary_0 = 17170473; // 0x1060029
- field public static final int system_secondary_100 = 17170475; // 0x106002b
- field public static final int system_secondary_1000 = 17170484; // 0x1060034
- field public static final int system_secondary_200 = 17170476; // 0x106002c
- field public static final int system_secondary_300 = 17170477; // 0x106002d
- field public static final int system_secondary_400 = 17170478; // 0x106002e
- field public static final int system_secondary_50 = 17170474; // 0x106002a
- field public static final int system_secondary_500 = 17170479; // 0x106002f
- field public static final int system_secondary_600 = 17170480; // 0x1060030
- field public static final int system_secondary_700 = 17170481; // 0x1060031
- field public static final int system_secondary_800 = 17170482; // 0x1060032
- field public static final int system_secondary_900 = 17170483; // 0x1060033
+ field public static final int system_accent1_0 = 17170485; // 0x1060035
+ field public static final int system_accent1_100 = 17170487; // 0x1060037
+ field public static final int system_accent1_1000 = 17170496; // 0x1060040
+ field public static final int system_accent1_200 = 17170488; // 0x1060038
+ field public static final int system_accent1_300 = 17170489; // 0x1060039
+ field public static final int system_accent1_400 = 17170490; // 0x106003a
+ field public static final int system_accent1_50 = 17170486; // 0x1060036
+ field public static final int system_accent1_500 = 17170491; // 0x106003b
+ field public static final int system_accent1_600 = 17170492; // 0x106003c
+ field public static final int system_accent1_700 = 17170493; // 0x106003d
+ field public static final int system_accent1_800 = 17170494; // 0x106003e
+ field public static final int system_accent1_900 = 17170495; // 0x106003f
+ field public static final int system_accent2_0 = 17170497; // 0x1060041
+ field public static final int system_accent2_100 = 17170499; // 0x1060043
+ field public static final int system_accent2_1000 = 17170508; // 0x106004c
+ field public static final int system_accent2_200 = 17170500; // 0x1060044
+ field public static final int system_accent2_300 = 17170501; // 0x1060045
+ field public static final int system_accent2_400 = 17170502; // 0x1060046
+ field public static final int system_accent2_50 = 17170498; // 0x1060042
+ field public static final int system_accent2_500 = 17170503; // 0x1060047
+ field public static final int system_accent2_600 = 17170504; // 0x1060048
+ field public static final int system_accent2_700 = 17170505; // 0x1060049
+ field public static final int system_accent2_800 = 17170506; // 0x106004a
+ field public static final int system_accent2_900 = 17170507; // 0x106004b
+ field public static final int system_accent3_0 = 17170509; // 0x106004d
+ field public static final int system_accent3_100 = 17170511; // 0x106004f
+ field public static final int system_accent3_1000 = 17170520; // 0x1060058
+ field public static final int system_accent3_200 = 17170512; // 0x1060050
+ field public static final int system_accent3_300 = 17170513; // 0x1060051
+ field public static final int system_accent3_400 = 17170514; // 0x1060052
+ field public static final int system_accent3_50 = 17170510; // 0x106004e
+ field public static final int system_accent3_500 = 17170515; // 0x1060053
+ field public static final int system_accent3_600 = 17170516; // 0x1060054
+ field public static final int system_accent3_700 = 17170517; // 0x1060055
+ field public static final int system_accent3_800 = 17170518; // 0x1060056
+ field public static final int system_accent3_900 = 17170519; // 0x1060057
+ field public static final int system_neutral1_0 = 17170461; // 0x106001d
+ field public static final int system_neutral1_100 = 17170463; // 0x106001f
+ field public static final int system_neutral1_1000 = 17170472; // 0x1060028
+ field public static final int system_neutral1_200 = 17170464; // 0x1060020
+ field public static final int system_neutral1_300 = 17170465; // 0x1060021
+ field public static final int system_neutral1_400 = 17170466; // 0x1060022
+ field public static final int system_neutral1_50 = 17170462; // 0x106001e
+ field public static final int system_neutral1_500 = 17170467; // 0x1060023
+ field public static final int system_neutral1_600 = 17170468; // 0x1060024
+ field public static final int system_neutral1_700 = 17170469; // 0x1060025
+ field public static final int system_neutral1_800 = 17170470; // 0x1060026
+ field public static final int system_neutral1_900 = 17170471; // 0x1060027
+ field public static final int system_neutral2_0 = 17170473; // 0x1060029
+ field public static final int system_neutral2_100 = 17170475; // 0x106002b
+ field public static final int system_neutral2_1000 = 17170484; // 0x1060034
+ field public static final int system_neutral2_200 = 17170476; // 0x106002c
+ field public static final int system_neutral2_300 = 17170477; // 0x106002d
+ field public static final int system_neutral2_400 = 17170478; // 0x106002e
+ field public static final int system_neutral2_50 = 17170474; // 0x106002a
+ field public static final int system_neutral2_500 = 17170479; // 0x106002f
+ field public static final int system_neutral2_600 = 17170480; // 0x1060030
+ field public static final int system_neutral2_700 = 17170481; // 0x1060031
+ field public static final int system_neutral2_800 = 17170482; // 0x1060032
+ field public static final int system_neutral2_900 = 17170483; // 0x1060033
field public static final int tab_indicator_text = 17170441; // 0x1060009
field @Deprecated public static final int tertiary_text_dark = 17170448; // 0x1060010
field @Deprecated public static final int tertiary_text_light = 17170449; // 0x1060011
@@ -5656,6 +5680,7 @@
field public static final String EXTRA_PEOPLE_LIST = "android.people.list";
field public static final String EXTRA_PICTURE = "android.picture";
field public static final String EXTRA_PICTURE_CONTENT_DESCRIPTION = "android.pictureContentDescription";
+ field public static final String EXTRA_PICTURE_ICON = "android.pictureIcon";
field public static final String EXTRA_PROGRESS = "android.progress";
field public static final String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
field public static final String EXTRA_PROGRESS_MAX = "android.progressMax";
@@ -5804,8 +5829,9 @@
method @NonNull public android.app.Notification.BigPictureStyle bigLargeIcon(@Nullable android.graphics.Bitmap);
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 bigPictureContentDescription(@Nullable CharSequence);
+ method @NonNull public android.app.Notification.BigPictureStyle bigPicture(@Nullable android.graphics.drawable.Icon);
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);
}
@@ -7944,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();
@@ -7952,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 {
@@ -8967,6 +9009,8 @@
field public static final String ACTION_NAME_CHANGED = "android.bluetooth.device.action.NAME_CHANGED";
field public static final String ACTION_PAIRING_REQUEST = "android.bluetooth.device.action.PAIRING_REQUEST";
field public static final String ACTION_UUID = "android.bluetooth.device.action.UUID";
+ field public static final int ADDRESS_TYPE_PUBLIC = 0; // 0x0
+ field public static final int ADDRESS_TYPE_RANDOM = 1; // 0x1
field public static final int BOND_BONDED = 12; // 0xc
field public static final int BOND_BONDING = 11; // 0xb
field public static final int BOND_NONE = 10; // 0xa
@@ -24418,11 +24462,16 @@
public abstract class Event {
ctor protected Event(long);
+ method @NonNull public android.os.Bundle getMetricsBundle();
method @IntRange(from=0xffffffff) public long getTimeSinceCreatedMillis();
}
+ public final class LogSessionId {
+ }
+
public class MediaMetricsManager {
method @NonNull public android.media.metrics.PlaybackSession createPlaybackSession();
+ method @NonNull public android.media.metrics.RecordingSession createRecordingSession();
field public static final long INVALID_TIMESTAMP = -1L; // 0xffffffffffffffffL
}
@@ -24437,14 +24486,16 @@
field public static final int NETWORK_TYPE_5G_NSA = 7; // 0x7
field public static final int NETWORK_TYPE_5G_SA = 8; // 0x8
field public static final int NETWORK_TYPE_ETHERNET = 3; // 0x3
- field public static final int NETWORK_TYPE_NONE = 0; // 0x0
+ field public static final int NETWORK_TYPE_OFFLINE = 9; // 0x9
field public static final int NETWORK_TYPE_OTHER = 1; // 0x1
+ field public static final int NETWORK_TYPE_UNKNOWN = 0; // 0x0
field public static final int NETWORK_TYPE_WIFI = 2; // 0x2
}
public static final class NetworkEvent.Builder {
ctor public NetworkEvent.Builder();
method @NonNull public android.media.metrics.NetworkEvent build();
+ method @NonNull public android.media.metrics.NetworkEvent.Builder setMetricsBundle(@NonNull android.os.Bundle);
method @NonNull public android.media.metrics.NetworkEvent.Builder setNetworkType(int);
method @NonNull public android.media.metrics.NetworkEvent.Builder setTimeSinceCreatedMillis(@IntRange(from=0xffffffff) long);
}
@@ -24460,9 +24511,37 @@
method @IntRange(from=java.lang.Integer.MIN_VALUE, to=java.lang.Integer.MAX_VALUE) public int getSubErrorCode();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.media.metrics.PlaybackErrorEvent> CREATOR;
- field public static final int ERROR_CODE_OTHER = 1; // 0x1
- field public static final int ERROR_CODE_RUNTIME = 2; // 0x2
- field public static final int ERROR_CODE_UNKNOWN = 0; // 0x0
+ field public static final int ERROR_AUDIOTRACK_INIT = 17; // 0x11
+ field public static final int ERROR_AUDIOTRACK_OTHER = 19; // 0x13
+ field public static final int ERROR_AUDIOTRACK_WRITE = 18; // 0x12
+ field public static final int ERROR_DECODER_DECODE = 14; // 0xe
+ field public static final int ERROR_DECODER_INIT = 13; // 0xd
+ field public static final int ERROR_DECODER_OOM = 15; // 0xf
+ field public static final int ERROR_DECODER_OTHER = 16; // 0x10
+ field public static final int ERROR_DRM_CONTENT_ERROR = 28; // 0x1c
+ field public static final int ERROR_DRM_DISALLOWED = 26; // 0x1a
+ field public static final int ERROR_DRM_LICENSE_ERROR = 25; // 0x19
+ field public static final int ERROR_DRM_OTHER = 30; // 0x1e
+ field public static final int ERROR_DRM_PROVISIONING_FAILED = 24; // 0x18
+ field public static final int ERROR_DRM_REVOKED = 29; // 0x1d
+ field public static final int ERROR_DRM_SYSTEM_ERROR = 27; // 0x1b
+ field public static final int ERROR_DRM_UNAVAILABLE = 23; // 0x17
+ field public static final int ERROR_MEDIA_MANIFEST = 10; // 0xa
+ field public static final int ERROR_MEDIA_OTHER = 12; // 0xc
+ field public static final int ERROR_MEDIA_PARSER = 11; // 0xb
+ field public static final int ERROR_NETWORK_BAD_STATUS = 5; // 0x5
+ field public static final int ERROR_NETWORK_CLOSED = 8; // 0x8
+ field public static final int ERROR_NETWORK_CONNECT = 4; // 0x4
+ field public static final int ERROR_NETWORK_DNS = 6; // 0x6
+ field public static final int ERROR_NETWORK_OFFLINE = 3; // 0x3
+ field public static final int ERROR_NETWORK_OTHER = 9; // 0x9
+ field public static final int ERROR_NETWORK_TIMEOUT = 7; // 0x7
+ field public static final int ERROR_OTHER = 1; // 0x1
+ field public static final int ERROR_PLAYER_BEHIND_LIVE_WINDOW = 21; // 0x15
+ field public static final int ERROR_PLAYER_OTHER = 22; // 0x16
+ field public static final int ERROR_PLAYER_REMOTE = 20; // 0x14
+ field public static final int ERROR_RUNTIME = 2; // 0x2
+ field public static final int ERROR_UNKNOWN = 0; // 0x0
}
public static final class PlaybackErrorEvent.Builder {
@@ -24470,6 +24549,7 @@
method @NonNull public android.media.metrics.PlaybackErrorEvent build();
method @NonNull public android.media.metrics.PlaybackErrorEvent.Builder setErrorCode(int);
method @NonNull public android.media.metrics.PlaybackErrorEvent.Builder setException(@NonNull Exception);
+ method @NonNull public android.media.metrics.PlaybackErrorEvent.Builder setMetricsBundle(@NonNull android.os.Bundle);
method @NonNull public android.media.metrics.PlaybackErrorEvent.Builder setSubErrorCode(@IntRange(from=java.lang.Integer.MIN_VALUE, to=java.lang.Integer.MAX_VALUE) int);
method @NonNull public android.media.metrics.PlaybackErrorEvent.Builder setTimeSinceCreatedMillis(@IntRange(from=0xffffffff) long);
}
@@ -24478,10 +24558,12 @@
method public int describeContents();
method @IntRange(from=0xffffffff, to=java.lang.Integer.MAX_VALUE) public int getAudioUnderrunCount();
method public int getContentType();
+ method @NonNull public byte[] getDrmSessionId();
method public int getDrmType();
method @NonNull public long[] getExperimentIds();
method @IntRange(from=0xffffffff) public long getLocalBytesRead();
method @IntRange(from=0xffffffff) public long getMediaDurationMillis();
+ method @NonNull public android.os.Bundle getMetricsBundle();
method @IntRange(from=0xffffffff) public long getNetworkBytesRead();
method @IntRange(from=0xffffffff) public long getNetworkTransferDurationMillis();
method public int getPlaybackType();
@@ -24492,9 +24574,10 @@
method @IntRange(from=0xffffffff, to=java.lang.Integer.MAX_VALUE) public int getVideoFramesDropped();
method @IntRange(from=0xffffffff, to=java.lang.Integer.MAX_VALUE) public int getVideoFramesPlayed();
method public void writeToParcel(@NonNull android.os.Parcel, int);
- field public static final int CONTENT_TYPE_AD = 1; // 0x1
- field public static final int CONTENT_TYPE_MAIN = 0; // 0x0
- field public static final int CONTENT_TYPE_OTHER = 2; // 0x2
+ field public static final int CONTENT_TYPE_AD = 2; // 0x2
+ field public static final int CONTENT_TYPE_MAIN = 1; // 0x1
+ field public static final int CONTENT_TYPE_OTHER = 3; // 0x3
+ field public static final int CONTENT_TYPE_UNKNOWN = 0; // 0x0
field @NonNull public static final android.os.Parcelable.Creator<android.media.metrics.PlaybackMetrics> CREATOR;
field public static final int DRM_TYPE_CLEARKEY = 6; // 0x6
field public static final int DRM_TYPE_NONE = 0; // 0x0
@@ -24503,9 +24586,10 @@
field public static final int DRM_TYPE_WIDEVINE_L1 = 3; // 0x3
field public static final int DRM_TYPE_WIDEVINE_L3 = 4; // 0x4
field public static final int DRM_TYPE_WV_L3_FALLBACK = 5; // 0x5
- field public static final int PLAYBACK_TYPE_LIVE = 1; // 0x1
- field public static final int PLAYBACK_TYPE_OTHER = 2; // 0x2
- field public static final int PLAYBACK_TYPE_VOD = 0; // 0x0
+ field public static final int PLAYBACK_TYPE_LIVE = 2; // 0x2
+ field public static final int PLAYBACK_TYPE_OTHER = 3; // 0x3
+ field public static final int PLAYBACK_TYPE_UNKNOWN = 0; // 0x0
+ field public static final int PLAYBACK_TYPE_VOD = 1; // 0x1
field public static final int STREAM_SOURCE_DEVICE = 2; // 0x2
field public static final int STREAM_SOURCE_MIXED = 3; // 0x3
field public static final int STREAM_SOURCE_NETWORK = 1; // 0x1
@@ -24524,9 +24608,11 @@
method @NonNull public android.media.metrics.PlaybackMetrics build();
method @NonNull public android.media.metrics.PlaybackMetrics.Builder setAudioUnderrunCount(@IntRange(from=0xffffffff, to=java.lang.Integer.MAX_VALUE) int);
method @NonNull public android.media.metrics.PlaybackMetrics.Builder setContentType(int);
+ method @NonNull public android.media.metrics.PlaybackMetrics.Builder setDrmSessionId(@NonNull byte[]);
method @NonNull public android.media.metrics.PlaybackMetrics.Builder setDrmType(int);
method @NonNull public android.media.metrics.PlaybackMetrics.Builder setLocalBytesRead(@IntRange(from=0xffffffff) long);
method @NonNull public android.media.metrics.PlaybackMetrics.Builder setMediaDurationMillis(@IntRange(from=0xffffffff) long);
+ method @NonNull public android.media.metrics.PlaybackMetrics.Builder setMetricsBundle(@NonNull android.os.Bundle);
method @NonNull public android.media.metrics.PlaybackMetrics.Builder setNetworkBytesRead(@IntRange(from=0xffffffff) long);
method @NonNull public android.media.metrics.PlaybackMetrics.Builder setNetworkTransferDurationMillis(@IntRange(from=0xffffffff) long);
method @NonNull public android.media.metrics.PlaybackMetrics.Builder setPlaybackType(int);
@@ -24540,7 +24626,7 @@
public final class PlaybackSession implements java.lang.AutoCloseable {
method public void close();
- method @NonNull public String getId();
+ method @NonNull public android.media.metrics.LogSessionId getSessionId();
method public void reportNetworkEvent(@NonNull android.media.metrics.NetworkEvent);
method public void reportPlaybackErrorEvent(@NonNull android.media.metrics.PlaybackErrorEvent);
method public void reportPlaybackMetrics(@NonNull android.media.metrics.PlaybackMetrics);
@@ -24573,13 +24659,19 @@
public static final class PlaybackStateEvent.Builder {
ctor public PlaybackStateEvent.Builder();
method @NonNull public android.media.metrics.PlaybackStateEvent build();
+ method @NonNull public android.media.metrics.PlaybackStateEvent.Builder setMetricsBundle(@NonNull android.os.Bundle);
method @NonNull public android.media.metrics.PlaybackStateEvent.Builder setState(int);
method @NonNull public android.media.metrics.PlaybackStateEvent.Builder setTimeSinceCreatedMillis(@IntRange(from=0xffffffff) long);
}
+ public final class RecordingSession implements java.lang.AutoCloseable {
+ method public void close();
+ method @NonNull public android.media.metrics.LogSessionId getSessionId();
+ }
+
public final class TrackChangeEvent extends android.media.metrics.Event implements android.os.Parcelable {
- ctor public TrackChangeEvent(int, int, @Nullable String, @Nullable String, @Nullable String, int, long, int, @Nullable String, @Nullable String, int, int, int, int);
method public int describeContents();
+ method @IntRange(from=0xffffffff, to=java.lang.Integer.MAX_VALUE) public int getAudioSampleRate();
method @IntRange(from=0xffffffff, to=java.lang.Integer.MAX_VALUE) public int getBitrate();
method @IntRange(from=0xffffffff, to=java.lang.Integer.MAX_VALUE) public int getChannelCount();
method @Nullable public String getCodecName();
@@ -24588,10 +24680,10 @@
method @Nullable public String getLanguage();
method @Nullable public String getLanguageRegion();
method @Nullable public String getSampleMimeType();
- method @IntRange(from=0xffffffff, to=java.lang.Integer.MAX_VALUE) public int getSampleRate();
method public int getTrackChangeReason();
method public int getTrackState();
method public int getTrackType();
+ method @FloatRange(from=0xffffffff, to=java.lang.Float.MAX_VALUE) public float getVideoFrameRate();
method @IntRange(from=0xffffffff, to=java.lang.Integer.MAX_VALUE) public int getWidth();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.media.metrics.TrackChangeEvent> CREATOR;
@@ -24610,6 +24702,7 @@
public static final class TrackChangeEvent.Builder {
ctor public TrackChangeEvent.Builder(int);
method @NonNull public android.media.metrics.TrackChangeEvent build();
+ method @NonNull public android.media.metrics.TrackChangeEvent.Builder setAudioSampleRate(@IntRange(from=0xffffffff, to=java.lang.Integer.MAX_VALUE) int);
method @NonNull public android.media.metrics.TrackChangeEvent.Builder setBitrate(@IntRange(from=0xffffffff, to=java.lang.Integer.MAX_VALUE) int);
method @NonNull public android.media.metrics.TrackChangeEvent.Builder setChannelCount(@IntRange(from=0xffffffff, to=java.lang.Integer.MAX_VALUE) int);
method @NonNull public android.media.metrics.TrackChangeEvent.Builder setCodecName(@NonNull String);
@@ -24617,11 +24710,12 @@
method @NonNull public android.media.metrics.TrackChangeEvent.Builder setHeight(@IntRange(from=0xffffffff, to=java.lang.Integer.MAX_VALUE) int);
method @NonNull public android.media.metrics.TrackChangeEvent.Builder setLanguage(@NonNull String);
method @NonNull public android.media.metrics.TrackChangeEvent.Builder setLanguageRegion(@NonNull String);
+ method @NonNull public android.media.metrics.TrackChangeEvent.Builder setMetricsBundle(@NonNull android.os.Bundle);
method @NonNull public android.media.metrics.TrackChangeEvent.Builder setSampleMimeType(@NonNull String);
- method @NonNull public android.media.metrics.TrackChangeEvent.Builder setSampleRate(@IntRange(from=0xffffffff, to=java.lang.Integer.MAX_VALUE) int);
method @NonNull public android.media.metrics.TrackChangeEvent.Builder setTimeSinceCreatedMillis(@IntRange(from=0xffffffff) long);
method @NonNull public android.media.metrics.TrackChangeEvent.Builder setTrackChangeReason(int);
method @NonNull public android.media.metrics.TrackChangeEvent.Builder setTrackState(int);
+ method @NonNull public android.media.metrics.TrackChangeEvent.Builder setVideoFrameRate(@FloatRange(from=0xffffffff, to=java.lang.Float.MAX_VALUE) float);
method @NonNull public android.media.metrics.TrackChangeEvent.Builder setWidth(@IntRange(from=0xffffffff, to=java.lang.Integer.MAX_VALUE) int);
}
@@ -38271,6 +38365,8 @@
field public static final int REASON_CANCEL = 2; // 0x2
field public static final int REASON_CANCEL_ALL = 3; // 0x3
field public static final int REASON_CHANNEL_BANNED = 17; // 0x11
+ field public static final int REASON_CHANNEL_REMOVED = 20; // 0x14
+ field public static final int REASON_CLEAR_DATA = 21; // 0x15
field public static final int REASON_CLICK = 1; // 0x1
field public static final int REASON_ERROR = 4; // 0x4
field public static final int REASON_GROUP_OPTIMIZATION = 13; // 0xd
@@ -39107,7 +39203,7 @@
method public android.telecom.Call getParent();
method public String getRemainingPostDialSequence();
method @Nullable public android.telecom.Call.RttCall getRttCall();
- method public int getState();
+ method @Deprecated public int getState();
method public android.telecom.InCallService.VideoCall getVideoCall();
method public void handoverTo(android.telecom.PhoneAccountHandle, int, android.os.Bundle);
method public void hold();
@@ -39202,6 +39298,7 @@
method public android.net.Uri getHandle();
method public int getHandlePresentation();
method public android.os.Bundle getIntentExtras();
+ method public final int getState();
method public android.telecom.StatusHints getStatusHints();
method public int getVideoState();
method public static boolean hasProperty(int, int);
@@ -42047,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);
@@ -42060,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);
@@ -42283,6 +42380,7 @@
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getNetworkSlicingConfiguration(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.data.SlicingConfig,android.telephony.TelephonyManager.SlicingException>);
method public String getNetworkSpecifier();
method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getNetworkType();
+ method @Nullable @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public android.telecom.PhoneAccountHandle getPhoneAccountHandle();
method @Deprecated public int getPhoneCount();
method public int getPhoneType();
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE}) public int getPreferredOpportunisticDataSubscription();
@@ -42319,6 +42417,7 @@
method @Deprecated public String iccTransmitApduBasicChannel(int, int, int, int, int, String);
method @Deprecated public String iccTransmitApduLogicalChannel(int, int, int, int, int, int, String);
method public boolean isConcurrentVoiceAndDataSupported();
+ method public boolean isDataCapable();
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public boolean isDataConnectionAllowed();
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataEnabled();
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataEnabledForReason(int);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index bcc4b91..a2f895b 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -65,6 +65,7 @@
field public static final String BIND_TV_REMOTE_SERVICE = "android.permission.BIND_TV_REMOTE_SERVICE";
field public static final String BRICK = "android.permission.BRICK";
field public static final String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE";
+ field public static final String BROADCAST_CLOSE_SYSTEM_DIALOGS = "android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS";
field @Deprecated public static final String BROADCAST_NETWORK_PRIVILEGED = "android.permission.BROADCAST_NETWORK_PRIVILEGED";
field public static final String CAMERA_DISABLE_TRANSMIT_LED = "android.permission.CAMERA_DISABLE_TRANSMIT_LED";
field public static final String CAMERA_OPEN_CLOSE_LISTENER = "android.permission.CAMERA_OPEN_CLOSE_LISTENER";
@@ -1191,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;
}
@@ -1832,6 +1835,7 @@
package android.apphibernation {
public final class AppHibernationManager {
+ method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public java.util.List<java.lang.String> getHibernatingPackagesForUser();
method @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public boolean isHibernatingForUser(@NonNull String);
method @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public boolean isHibernatingGlobally(@NonNull String);
method @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public void setHibernatingForUser(@NonNull String, boolean);
@@ -2138,7 +2142,19 @@
field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.le.ResultStorageDescriptor> CREATOR;
}
+ public final class ScanFilter implements android.os.Parcelable {
+ method public int getAddressType();
+ method @Nullable public byte[] getIrk();
+ }
+
+ public static final class ScanFilter.Builder {
+ method @NonNull public android.bluetooth.le.ScanFilter.Builder setDeviceAddress(@NonNull String, int);
+ method @NonNull public android.bluetooth.le.ScanFilter.Builder setDeviceAddress(@NonNull String, int, @NonNull byte[]);
+ field public static final int LEN_IRK_OCTETS = 16; // 0x10
+ }
+
public final class ScanSettings implements android.os.Parcelable {
+ field public static final int SCAN_MODE_AMBIENT_DISCOVERY = 3; // 0x3
field public static final int SCAN_RESULT_TYPE_ABBREVIATED = 1; // 0x1
field public static final int SCAN_RESULT_TYPE_FULL = 0; // 0x0
}
@@ -8254,9 +8270,10 @@
public class PowerExemptionManager {
method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToPermanentAllowList(@NonNull String);
method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToPermanentAllowList(@NonNull java.util.List<java.lang.String>);
- method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void addToTemporaryAllowList(@NonNull String, long, int, @Nullable String);
- method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long addToTemporaryAllowListForEvent(@NonNull String, int, int, @Nullable String);
- method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void removeFromAllowList(@NonNull String);
+ method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void addToTemporaryAllowList(@NonNull String, int, @Nullable String, long);
+ method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long addToTemporaryAllowListForEvent(@NonNull String, int, @Nullable String, int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public int[] getAllowListedAppIds(boolean);
+ method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void removeFromPermanentAllowList(@NonNull String);
field public static final int EVENT_MMS = 2; // 0x2
field public static final int EVENT_SMS = 1; // 0x1
field public static final int EVENT_UNSPECIFIED = 0; // 0x0
@@ -11056,7 +11073,8 @@
public final class DataSpecificRegistrationInfo implements android.os.Parcelable {
method public int describeContents();
- method @NonNull public android.telephony.LteVopsSupportInfo getLteVopsSupportInfo();
+ method @Deprecated @NonNull public android.telephony.LteVopsSupportInfo getLteVopsSupportInfo();
+ method @Nullable public android.telephony.VopsSupportInfo getVopsSupportInfo();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.DataSpecificRegistrationInfo> CREATOR;
}
@@ -11101,14 +11119,16 @@
field public static final int LCE_TYPE_SECONDARY = 1; // 0x1
}
- public final class LteVopsSupportInfo implements android.os.Parcelable {
+ public final class LteVopsSupportInfo extends android.telephony.VopsSupportInfo {
ctor public LteVopsSupportInfo(int, int);
- method public int describeContents();
method public int getEmcBearerSupport();
method public int getVopsSupport();
- method public void writeToParcel(android.os.Parcel, int);
+ method public boolean isEmergencyServiceFallbackSupported();
+ method public boolean isEmergencyServiceSupported();
+ method public boolean isVopsSupported();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.LteVopsSupportInfo> CREATOR;
- field public static final int LTE_STATUS_NOT_AVAILABLE = 1; // 0x1
+ field @Deprecated public static final int LTE_STATUS_NOT_AVAILABLE = 1; // 0x1
field public static final int LTE_STATUS_NOT_SUPPORTED = 3; // 0x3
field public static final int LTE_STATUS_SUPPORTED = 2; // 0x2
}
@@ -11198,6 +11218,29 @@
field public static final int RESULT_SUCCESS = 0; // 0x0
}
+ public final class NrVopsSupportInfo extends android.telephony.VopsSupportInfo {
+ ctor public NrVopsSupportInfo(int, int, int);
+ method public int getEmcSupport();
+ method public int getEmfSupport();
+ method public int getVopsSupport();
+ method public boolean isEmergencyServiceFallbackSupported();
+ method public boolean isEmergencyServiceSupported();
+ method public boolean isVopsSupported();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.NrVopsSupportInfo> CREATOR;
+ field public static final int NR_STATUS_EMC_5GCN_ONLY = 1; // 0x1
+ field public static final int NR_STATUS_EMC_EUTRA_5GCN_ONLY = 2; // 0x2
+ field public static final int NR_STATUS_EMC_NOT_SUPPORTED = 0; // 0x0
+ field public static final int NR_STATUS_EMC_NR_EUTRA_5GCN = 3; // 0x3
+ field public static final int NR_STATUS_EMF_5GCN_ONLY = 1; // 0x1
+ field public static final int NR_STATUS_EMF_EUTRA_5GCN_ONLY = 2; // 0x2
+ field public static final int NR_STATUS_EMF_NOT_SUPPORTED = 0; // 0x0
+ field public static final int NR_STATUS_EMF_NR_EUTRA_5GCN = 3; // 0x3
+ field public static final int NR_STATUS_VOPS_3GPP_SUPPORTED = 1; // 0x1
+ field public static final int NR_STATUS_VOPS_NON_3GPP_SUPPORTED = 2; // 0x2
+ field public static final int NR_STATUS_VOPS_NOT_SUPPORTED = 0; // 0x0
+ }
+
public interface NumberVerificationCallback {
method public default void onCallReceived(@NonNull String);
method public default void onVerificationFailed(int);
@@ -11863,6 +11906,7 @@
field public static final String CAPABILITY_ALLOWED_NETWORK_TYPES_USED = "CAPABILITY_ALLOWED_NETWORK_TYPES_USED";
field public static final String CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE = "CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE";
field public static final String CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE = "CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE";
+ field public static final String CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING = "CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING";
field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
@@ -12024,6 +12068,16 @@
method public static final void setSmsFilterSettings(android.content.Context, android.telecom.PhoneAccountHandle, android.telephony.VisualVoicemailSmsFilterSettings);
}
+ public abstract class VopsSupportInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public abstract boolean equals(Object);
+ method public abstract int hashCode();
+ method public abstract boolean isEmergencyServiceFallbackSupported();
+ method public abstract boolean isEmergencyServiceSupported();
+ method public abstract boolean isVopsSupported();
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.VopsSupportInfo> CREATOR;
+ }
+
}
package android.telephony.cdma {
@@ -13241,6 +13295,7 @@
method @Nullable public android.telephony.ims.RcsContactPresenceTuple getCapabilityTuple(@NonNull String);
method @NonNull public java.util.List<android.telephony.ims.RcsContactPresenceTuple> getCapabilityTuples();
method @NonNull public android.net.Uri getContactUri();
+ method @NonNull public java.util.Set<java.lang.String> getFeatureTags();
method public int getRequestResult();
method public int getSourceType();
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -13255,6 +13310,14 @@
field public static final int SOURCE_TYPE_NETWORK = 0; // 0x0
}
+ public static final class RcsContactUceCapability.OptionsBuilder {
+ ctor public RcsContactUceCapability.OptionsBuilder(@NonNull android.net.Uri);
+ method @NonNull public android.telephony.ims.RcsContactUceCapability.OptionsBuilder addFeatureTag(@NonNull String);
+ method @NonNull public android.telephony.ims.RcsContactUceCapability.OptionsBuilder addFeatureTags(@NonNull java.util.Set<java.lang.String>);
+ method @NonNull public android.telephony.ims.RcsContactUceCapability build();
+ method @NonNull public android.telephony.ims.RcsContactUceCapability.OptionsBuilder setRequestResult(int);
+ }
+
public static final class RcsContactUceCapability.PresenceBuilder {
ctor public RcsContactUceCapability.PresenceBuilder(@NonNull android.net.Uri, int, int);
method @NonNull public android.telephony.ims.RcsContactUceCapability.PresenceBuilder addCapabilityTuple(@NonNull android.telephony.ims.RcsContactPresenceTuple);
@@ -13548,7 +13611,7 @@
package android.telephony.ims.stub {
public interface CapabilityExchangeEventListener {
- method public void onRemoteCapabilityRequest(@NonNull android.net.Uri, @NonNull java.util.List<java.lang.String>, @NonNull android.telephony.ims.stub.CapabilityExchangeEventListener.OptionsRequestCallback) throws android.telephony.ims.ImsException;
+ method public void onRemoteCapabilityRequest(@NonNull android.net.Uri, @NonNull java.util.Set<java.lang.String>, @NonNull android.telephony.ims.stub.CapabilityExchangeEventListener.OptionsRequestCallback) throws android.telephony.ims.ImsException;
method public void onRequestPublishCapabilities(int) throws android.telephony.ims.ImsException;
method public void onUnpublish() throws android.telephony.ims.ImsException;
}
@@ -13745,7 +13808,7 @@
public class RcsCapabilityExchangeImplBase {
ctor public RcsCapabilityExchangeImplBase(@NonNull java.util.concurrent.Executor);
method public void publishCapabilities(@NonNull String, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.PublishResponseCallback);
- method public void sendOptionsCapabilityRequest(@NonNull android.net.Uri, @NonNull java.util.List<java.lang.String>, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.OptionsResponseCallback);
+ method public void sendOptionsCapabilityRequest(@NonNull android.net.Uri, @NonNull java.util.Set<java.lang.String>, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.OptionsResponseCallback);
method public void subscribeForCapabilities(@NonNull java.util.Collection<android.net.Uri>, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.SubscribeResponseCallback);
field public static final int COMMAND_CODE_FETCH_ERROR = 3; // 0x3
field public static final int COMMAND_CODE_GENERIC_FAILURE = 1; // 0x1
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 4be6206..ae1cbf7 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -29,7 +29,6 @@
field public static final String NETWORK_STACK = "android.permission.NETWORK_STACK";
field public static final String OVERRIDE_DISPLAY_MODE_REQUESTS = "android.permission.OVERRIDE_DISPLAY_MODE_REQUESTS";
field public static final String QUERY_AUDIO_STATE = "android.permission.QUERY_AUDIO_STATE";
- field public static final String QUERY_USERS = "android.permission.QUERY_USERS";
field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
field public static final String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE";
field public static final String RECORD_BACKGROUND_AUDIO = "android.permission.RECORD_BACKGROUND_AUDIO";
@@ -107,7 +106,6 @@
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public boolean stopUser(int, boolean);
method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public boolean updateMccMncConfiguration(@NonNull String, @NonNull String);
method @RequiresPermission(android.Manifest.permission.DUMP) public void waitForBroadcastIdle();
- field public static final long DROP_CLOSE_SYSTEM_DIALOGS = 174664120L; // 0xa6929b8L
field public static final long LOCK_DOWN_CLOSE_SYSTEM_DIALOGS = 174664365L; // 0xa692aadL
field public static final int PROCESS_CAPABILITY_ALL = 15; // 0xf
field public static final int PROCESS_CAPABILITY_ALL_EXPLICIT = 1; // 0x1
@@ -1475,6 +1473,14 @@
}
+package android.media.metrics {
+
+ public final class LogSessionId {
+ method @NonNull public String getStringId();
+ }
+
+}
+
package android.media.tv {
public final class TvInputManager {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 3fedda3..36c66e5 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -901,7 +901,6 @@
*
* @hide
*/
- @TestApi
@ChangeId
public static final long DROP_CLOSE_SYSTEM_DIALOGS = 174664120L;
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index a1dce77..006b2b5 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -1606,7 +1606,8 @@
/**
* Sets a launch cookie that can be used to track the activity and task that are launch as a
- * result of this option.
+ * result of this option. If the launched activity is a trampoline that starts another activity
+ * immediately, the cookie will be transferred to the next activity.
*
* @hide
*/
diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java
index 146d648..618eda8 100644
--- a/core/java/android/app/Application.java
+++ b/core/java/android/app/Application.java
@@ -22,6 +22,7 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentCallbacks;
import android.content.ComponentCallbacks2;
+import android.content.ComponentCallbacksController;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
@@ -53,14 +54,14 @@
public class Application extends ContextWrapper implements ComponentCallbacks2 {
private static final String TAG = "Application";
@UnsupportedAppUsage
- private ArrayList<ComponentCallbacks> mComponentCallbacks =
- new ArrayList<ComponentCallbacks>();
- @UnsupportedAppUsage
private ArrayList<ActivityLifecycleCallbacks> mActivityLifecycleCallbacks =
new ArrayList<ActivityLifecycleCallbacks>();
@UnsupportedAppUsage
private ArrayList<OnProvideAssistDataListener> mAssistCallbacks = null;
+ private final ComponentCallbacksController mCallbacksController =
+ new ComponentCallbacksController();
+
/** @hide */
@UnsupportedAppUsage
public LoadedApk mLoadedApk;
@@ -260,47 +261,25 @@
@CallSuper
public void onConfigurationChanged(@NonNull Configuration newConfig) {
- Object[] callbacks = collectComponentCallbacks();
- if (callbacks != null) {
- for (int i=0; i<callbacks.length; i++) {
- ((ComponentCallbacks)callbacks[i]).onConfigurationChanged(newConfig);
- }
- }
+ mCallbacksController.dispatchConfigurationChanged(newConfig);
}
@CallSuper
public void onLowMemory() {
- Object[] callbacks = collectComponentCallbacks();
- if (callbacks != null) {
- for (int i=0; i<callbacks.length; i++) {
- ((ComponentCallbacks)callbacks[i]).onLowMemory();
- }
- }
+ mCallbacksController.dispatchLowMemory();
}
@CallSuper
public void onTrimMemory(int level) {
- Object[] callbacks = collectComponentCallbacks();
- if (callbacks != null) {
- for (int i=0; i<callbacks.length; i++) {
- Object c = callbacks[i];
- if (c instanceof ComponentCallbacks2) {
- ((ComponentCallbacks2)c).onTrimMemory(level);
- }
- }
- }
+ mCallbacksController.dispatchTrimMemory(level);
}
public void registerComponentCallbacks(ComponentCallbacks callback) {
- synchronized (mComponentCallbacks) {
- mComponentCallbacks.add(callback);
- }
+ mCallbacksController.registerCallbacks(callback);
}
public void unregisterComponentCallbacks(ComponentCallbacks callback) {
- synchronized (mComponentCallbacks) {
- mComponentCallbacks.remove(callback);
- }
+ mCallbacksController.unregisterCallbacks(callback);
}
public void registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback) {
@@ -575,16 +554,6 @@
}
}
- private Object[] collectComponentCallbacks() {
- Object[] callbacks = null;
- synchronized (mComponentCallbacks) {
- if (mComponentCallbacks.size() > 0) {
- callbacks = mComponentCallbacks.toArray();
- }
- }
- return callbacks;
- }
-
@UnsupportedAppUsage
private Object[] collectActivityLifecycleCallbacks() {
Object[] callbacks = null;
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 f4e214c..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;
@@ -1207,9 +1205,16 @@
public static final String EXTRA_PICTURE = "android.picture";
/**
+ * {@link #extras} key: this is an {@link Icon} of an image to be
+ * shown in {@link BigPictureStyle} expanded notifications, supplied to
+ * {@link BigPictureStyle#bigPicture(Icon)}.
+ */
+ public static final String EXTRA_PICTURE_ICON = "android.pictureIcon";
+
+ /**
* {@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";
@@ -2668,6 +2673,14 @@
}
}
+ private static void visitIconUri(@NonNull Consumer<Uri> visitor, @Nullable Icon icon) {
+ if (icon == null) return;
+ final int iconType = icon.getType();
+ if (iconType == TYPE_URI || iconType == TYPE_URI_ADAPTIVE_BITMAP) {
+ visitor.accept(icon.getUri());
+ }
+ }
+
/**
* Note all {@link Uri} that are referenced internally, with the expectation
* that Uri permission grants will need to be issued to ensure the recipient
@@ -2683,7 +2696,19 @@
if (bigContentView != null) bigContentView.visitUris(visitor);
if (headsUpContentView != null) headsUpContentView.visitUris(visitor);
+ visitIconUri(visitor, mSmallIcon);
+ visitIconUri(visitor, mLargeIcon);
+
+ if (actions != null) {
+ for (Action action : actions) {
+ visitIconUri(visitor, action.getIcon());
+ }
+ }
+
if (extras != null) {
+ visitIconUri(visitor, extras.getParcelable(EXTRA_LARGE_ICON_BIG));
+ visitIconUri(visitor, extras.getParcelable(EXTRA_PICTURE_ICON));
+
// NOTE: The documentation of EXTRA_AUDIO_CONTENTS_URI explicitly says that it is a
// String representation of a Uri, but the previous implementation (and unit test) of
// this method has always treated it as a Uri object. Given the inconsistency,
@@ -2702,14 +2727,12 @@
ArrayList<Person> people = extras.getParcelableArrayList(EXTRA_PEOPLE_LIST);
if (people != null && !people.isEmpty()) {
for (Person p : people) {
- if (p.getIconUri() != null) {
- visitor.accept(p.getIconUri());
- }
+ visitor.accept(p.getIconUri());
}
}
final Person person = extras.getParcelable(EXTRA_MESSAGING_PERSON);
- if (person != null && person.getIconUri() != null) {
+ if (person != null) {
visitor.accept(person.getIconUri());
}
}
@@ -2722,7 +2745,7 @@
visitor.accept(message.getDataUri());
Person senderPerson = message.getSenderPerson();
- if (senderPerson != null && senderPerson.getIconUri() != null) {
+ if (senderPerson != null) {
visitor.accept(senderPerson.getIconUri());
}
}
@@ -2735,19 +2758,15 @@
visitor.accept(message.getDataUri());
Person senderPerson = message.getSenderPerson();
- if (senderPerson != null && senderPerson.getIconUri() != null) {
+ if (senderPerson != null) {
visitor.accept(senderPerson.getIconUri());
}
}
}
}
- if (mBubbleMetadata != null && mBubbleMetadata.getIcon() != null) {
- final Icon icon = mBubbleMetadata.getIcon();
- final int iconType = icon.getType();
- if (iconType == TYPE_URI_ADAPTIVE_BITMAP || iconType == TYPE_URI) {
- visitor.accept(icon.getUri());
- }
+ if (mBubbleMetadata != null) {
+ visitIconUri(visitor, mBubbleMetadata.getIcon());
}
}
@@ -3680,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;
@@ -5020,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);
@@ -5071,7 +5089,7 @@
}
private CharSequence processTextSpans(CharSequence text) {
- if (hasForegroundColor() || mInNightMode) {
+ if (mInNightMode) {
return ContrastColorUtil.clearColorSpans(text);
}
return text;
@@ -5082,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
@@ -5119,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);
}
}
}
@@ -5233,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);
@@ -5257,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(
@@ -5278,7 +5230,7 @@
}
}
}
- final float extraMarginEndDpIfVisible = viewWidthDp + contentMarginDp;
+ final float extraMarginEndDpIfVisible = viewWidthDp + iconMarginDp;
result.setRightIconState(largeIconShown, viewWidthDp,
extraMarginEndDpIfVisible, expanderSizeDp);
}
@@ -5342,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);
@@ -5373,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()) {
@@ -5403,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)));
@@ -6633,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;
}
@@ -6661,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
@@ -6731,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);
@@ -6835,9 +6768,6 @@
* @hide
*/
public boolean isColorized() {
- if (isColorizedMedia()) {
- return true;
- }
return extras.getBoolean(EXTRA_COLORIZED)
&& (hasColorizedPermission() || isForegroundService());
}
@@ -6851,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
@@ -7159,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() {
@@ -7231,7 +7131,7 @@
* @see Notification#bigContentView
*/
public static class BigPictureStyle extends Style {
- private Bitmap mPicture;
+ private Icon mPictureIcon;
private Icon mBigLargeIcon;
private boolean mBigLargeIconSet = false;
private CharSequence mPictureContentDescription;
@@ -7271,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;
@@ -7280,8 +7180,12 @@
/**
* @hide
*/
- public Bitmap getBigPicture() {
- return mPicture;
+ @Nullable
+ public Icon getBigPicture() {
+ if (mPictureIcon != null) {
+ return mPictureIcon;
+ }
+ return null;
}
/**
@@ -7289,7 +7193,16 @@
*/
@NonNull
public BigPictureStyle bigPicture(@Nullable Bitmap b) {
- mPicture = b;
+ mPictureIcon = b == null ? null : Icon.createWithBitmap(b);
+ return this;
+ }
+
+ /**
+ * Provide the content Uri to be used as the payload for the BigPicture notification.
+ */
+ @NonNull
+ public BigPictureStyle bigPicture(@Nullable Icon icon) {
+ mPictureIcon = icon;
return this;
}
@@ -7331,10 +7244,8 @@
@Override
public void purgeResources() {
super.purgeResources();
- if (mPicture != null &&
- mPicture.isMutable() &&
- mPicture.getAllocationByteCount() >= MIN_ASHMEM_BITMAP_SIZE) {
- mPicture = mPicture.asShared();
+ if (mPictureIcon != null) {
+ mPictureIcon.convertToAshmem();
}
if (mBigLargeIcon != null) {
mBigLargeIcon.convertToAshmem();
@@ -7349,14 +7260,14 @@
super.reduceImageSizes(context);
Resources resources = context.getResources();
boolean isLowRam = ActivityManager.isLowRamDeviceStatic();
- if (mPicture != null) {
+ if (mPictureIcon != null) {
int maxPictureWidth = resources.getDimensionPixelSize(isLowRam
? R.dimen.notification_big_picture_max_height_low_ram
: R.dimen.notification_big_picture_max_height);
int maxPictureHeight = resources.getDimensionPixelSize(isLowRam
? R.dimen.notification_big_picture_max_width_low_ram
: R.dimen.notification_big_picture_max_width);
- mPicture = Icon.scaleDownIfNecessary(mPicture, maxPictureWidth, maxPictureHeight);
+ mPictureIcon.scaleDownIfNecessary(maxPictureWidth, maxPictureHeight);
}
if (mBigLargeIcon != null) {
int rightIconSize = resources.getDimensionPixelSize(isLowRam
@@ -7371,12 +7282,12 @@
*/
@Override
public RemoteViews makeContentView(boolean increasedHeight) {
- if (mPicture == null || !mShowBigPictureWhenCollapsed) {
+ if (mPictureIcon == null || !mShowBigPictureWhenCollapsed) {
return super.makeContentView(increasedHeight);
}
Icon oldLargeIcon = mBuilder.mN.mLargeIcon;
- mBuilder.mN.mLargeIcon = Icon.createWithBitmap(mPicture);
+ mBuilder.mN.mLargeIcon = mPictureIcon;
// The legacy largeIcon might not allow us to clear the image, as it's taken in
// replacement if the other one is null. Because we're restoring these legacy icons
// for old listeners, this is in general non-null.
@@ -7401,12 +7312,12 @@
*/
@Override
public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
- if (mPicture == null || !mShowBigPictureWhenCollapsed) {
+ if (mPictureIcon == null || !mShowBigPictureWhenCollapsed) {
return super.makeHeadsUpContentView(increasedHeight);
}
Icon oldLargeIcon = mBuilder.mN.mLargeIcon;
- mBuilder.mN.mLargeIcon = Icon.createWithBitmap(mPicture);
+ mBuilder.mN.mLargeIcon = mPictureIcon;
// The legacy largeIcon might not allow us to clear the image, as it's taken in
// replacement if the other one is null. Because we're restoring these legacy icons
// for old listeners, this is in general non-null.
@@ -7463,7 +7374,7 @@
mBuilder.mN.largeIcon = largeIconLegacy;
}
- contentView.setImageViewBitmap(R.id.big_picture, mPicture);
+ contentView.setImageViewIcon(R.id.big_picture, mPictureIcon);
if (mPictureContentDescription != null) {
contentView.setContentDescription(R.id.big_picture, mPictureContentDescription);
@@ -7486,7 +7397,17 @@
mPictureContentDescription);
}
extras.putBoolean(EXTRA_SHOW_BIG_PICTURE_WHEN_COLLAPSED, mShowBigPictureWhenCollapsed);
- extras.putParcelable(EXTRA_PICTURE, mPicture);
+
+ // If the icon contains a bitmap, use the old extra so that listeners which look for
+ // that extra can still find the picture. Don't include the new extra in that case,
+ // to avoid duplicating data.
+ if (mPictureIcon != null && mPictureIcon.getType() == Icon.TYPE_BITMAP) {
+ extras.putParcelable(EXTRA_PICTURE, mPictureIcon.getBitmap());
+ extras.putParcelable(EXTRA_PICTURE_ICON, null);
+ } else {
+ extras.putParcelable(EXTRA_PICTURE, null);
+ extras.putParcelable(EXTRA_PICTURE_ICON, mPictureIcon);
+ }
}
/**
@@ -7507,7 +7428,16 @@
}
mShowBigPictureWhenCollapsed = extras.getBoolean(EXTRA_SHOW_BIG_PICTURE_WHEN_COLLAPSED);
- mPicture = extras.getParcelable(EXTRA_PICTURE);
+
+ // When this style adds a picture, we only add one of the keys. If both were added,
+ // it would most likely be a legacy app trying to override the picture in some way.
+ // Because of that case it's better to give precedence to the legacy field.
+ Bitmap bitmapPicture = extras.getParcelable(EXTRA_PICTURE);
+ if (bitmapPicture != null) {
+ mPictureIcon = Icon.createWithBitmap(bitmapPicture);
+ } else {
+ mPictureIcon = extras.getParcelable(EXTRA_PICTURE_ICON);
+ }
}
/**
@@ -7529,20 +7459,32 @@
return true;
}
BigPictureStyle otherS = (BigPictureStyle) other;
- return areBitmapsObviouslyDifferent(getBigPicture(), otherS.getBigPicture());
+ return areIconsObviouslyDifferent(getBigPicture(), otherS.getBigPicture());
}
- private static boolean areBitmapsObviouslyDifferent(Bitmap a, Bitmap b) {
+ private static boolean areIconsObviouslyDifferent(Icon a, Icon b) {
if (a == b) {
return false;
}
if (a == null || b == null) {
return true;
}
- return a.getWidth() != b.getWidth()
- || a.getHeight() != b.getHeight()
- || a.getConfig() != b.getConfig()
- || a.getGenerationId() != b.getGenerationId();
+ if (a.sameAs(b)) {
+ return false;
+ }
+ final int aType = a.getType();
+ if (aType != b.getType()) {
+ return true;
+ }
+ if (aType == Icon.TYPE_BITMAP || aType == Icon.TYPE_ADAPTIVE_BITMAP) {
+ final Bitmap aBitmap = a.getBitmap();
+ final Bitmap bBitmap = b.getBitmap();
+ return aBitmap.getWidth() != bBitmap.getWidth()
+ || aBitmap.getHeight() != bBitmap.getHeight()
+ || aBitmap.getConfig() != bBitmap.getConfig()
+ || aBitmap.getGenerationId() != bBitmap.getGenerationId();
+ }
+ return true;
}
}
@@ -8978,7 +8920,7 @@
*/
@Override
public RemoteViews makeContentView(boolean increasedHeight) {
- return makeMediaContentView();
+ return makeMediaContentView(null /* customContent */);
}
/**
@@ -8986,7 +8928,7 @@
*/
@Override
public RemoteViews makeBigContentView() {
- return makeMediaBigContentView();
+ return makeMediaBigContentView(null /* customContent */);
}
/**
@@ -8994,7 +8936,7 @@
*/
@Override
public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
- return makeMediaContentView();
+ return makeMediaContentView(null /* customContent */);
}
/** @hide */
@@ -9064,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.
*
@@ -9833,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);
}
/**
@@ -9843,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);
}
/**
@@ -9868,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);
}
/**
@@ -9886,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;
}
}
@@ -12190,6 +12103,8 @@
boolean mHeaderless;
boolean mHideAppName;
boolean mHideTitle;
+ boolean mHideSubText;
+ boolean mHideTime;
boolean mHideActions;
boolean mHideProgress;
boolean mHideSnoozeButton;
@@ -12212,6 +12127,8 @@
mHeaderless = false;
mHideAppName = false;
mHideTitle = false;
+ mHideSubText = false;
+ mHideTime = false;
mHideActions = false;
mHideProgress = false;
mHideSnoozeButton = false;
@@ -12225,6 +12142,7 @@
summaryText = null;
headerTextSecondary = null;
maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES;
+ hideLargeIcon = false;
allowColorization = true;
mReduceHighlights = false;
return this;
@@ -12249,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/WindowContext.java b/core/java/android/app/WindowContext.java
index cbe2995..d44918c 100644
--- a/core/java/android/app/WindowContext.java
+++ b/core/java/android/app/WindowContext.java
@@ -20,8 +20,11 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UiContext;
+import android.content.ComponentCallbacks;
+import android.content.ComponentCallbacksController;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
@@ -49,6 +52,8 @@
private final IWindowManager mWms;
private final WindowTokenClient mToken;
private boolean mListenerRegistered;
+ private final ComponentCallbacksController mCallbacksController =
+ new ComponentCallbacksController();
/**
* Default constructor. Will generate a {@link WindowTokenClient} and attach this context to
@@ -131,8 +136,24 @@
}
void destroy() {
+ mCallbacksController.clearCallbacks();
final ContextImpl impl = (ContextImpl) getBaseContext();
impl.scheduleFinalCleanup(getClass().getName(), "WindowContext");
Reference.reachabilityFence(this);
}
+
+ @Override
+ public void registerComponentCallbacks(@NonNull ComponentCallbacks callback) {
+ mCallbacksController.registerCallbacks(callback);
+ }
+
+ @Override
+ public void unregisterComponentCallbacks(@NonNull ComponentCallbacks callback) {
+ mCallbacksController.unregisterCallbacks(callback);
+ }
+
+ /** Dispatch {@link Configuration} to each {@link ComponentCallbacks}. */
+ void dispatchConfigurationChanged(@NonNull Configuration newConfig) {
+ mCallbacksController.dispatchConfigurationChanged(newConfig);
+ }
}
diff --git a/core/java/android/app/WindowTokenClient.java b/core/java/android/app/WindowTokenClient.java
index 2298e84..82cef07 100644
--- a/core/java/android/app/WindowTokenClient.java
+++ b/core/java/android/app/WindowTokenClient.java
@@ -61,7 +61,7 @@
@Override
public void onConfigurationChanged(Configuration newConfig, int newDisplayId) {
- final Context context = mContextRef.get();
+ final WindowContext context = mContextRef.get();
if (context == null) {
return;
}
@@ -72,6 +72,8 @@
if (displayChanged || configChanged) {
// TODO(ag/9789103): update resource manager logic to track non-activity tokens
mResourcesManager.updateResourcesForActivity(this, newConfig, newDisplayId);
+ ActivityThread.currentActivityThread().getHandler().post(
+ () -> context.dispatchConfigurationChanged(newConfig));
}
if (displayChanged) {
context.updateDisplay(newDisplayId);
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/apphibernation/AppHibernationManager.java b/core/java/android/apphibernation/AppHibernationManager.java
index 132cc40..de77848 100644
--- a/core/java/android/apphibernation/AppHibernationManager.java
+++ b/core/java/android/apphibernation/AppHibernationManager.java
@@ -24,6 +24,8 @@
import android.os.RemoteException;
import android.os.ServiceManager;
+import java.util.List;
+
/**
* This class provides an API surface for system apps to manipulate the app hibernation
* state of a package for the user provided in the context.
@@ -111,4 +113,20 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Get the hibernating packages for the user. This is equivalent to the list of packages for
+ * the user that return true for {@link #isHibernatingForUser}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(value = android.Manifest.permission.MANAGE_APP_HIBERNATION)
+ public @NonNull List<String> getHibernatingPackagesForUser() {
+ try {
+ return mIAppHibernationService.getHibernatingPackagesForUser(mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/apphibernation/IAppHibernationService.aidl b/core/java/android/apphibernation/IAppHibernationService.aidl
index 6a068ee..afdb3fe 100644
--- a/core/java/android/apphibernation/IAppHibernationService.aidl
+++ b/core/java/android/apphibernation/IAppHibernationService.aidl
@@ -25,4 +25,5 @@
void setHibernatingForUser(String packageName, int userId, boolean isHibernating);
boolean isHibernatingGlobally(String packageName);
void setHibernatingGlobally(String packageName, boolean isHibernating);
+ List<String> getHibernatingPackagesForUser(int userId);
}
\ No newline at end of file
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index a6b4b47..82da4fb 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -841,8 +841,8 @@
* Calling this method will trigger a full re-inflation of the App Widget.
*
* The color resources that can be overloaded are the ones whose name is prefixed with
- * {@code system_primary_}, {@code system_secondary_} or {@code system_neutral_}, for example
- * {@link android.R.color#system_primary_500}.
+ * {@code system_neutral} or {@code system_accent}, for example
+ * {@link android.R.color#system_neutral1_500}.
*/
public void setColorResources(@NonNull SparseIntArray colorMapping) {
mColorResources = RemoteViews.ColorResources.create(mContext, colorMapping);
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 8d41572..a3d19ca 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -52,6 +52,8 @@
import android.util.Log;
import android.util.Pair;
+import com.android.internal.util.Preconditions;
+
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -3152,6 +3154,25 @@
return true;
}
+ /**
+ * Determines whether a String Bluetooth address, such as "00:43:A8:23:10:F0"
+ * is a RANDOM STATIC address.
+ *
+ * RANDOM STATIC: (addr & 0b11) == 0b11
+ * RANDOM RESOLVABLE: (addr & 0b11) == 0b10
+ * RANDOM non-RESOLVABLE: (addr & 0b11) == 0b00
+ *
+ * @param address Bluetooth address as string
+ * @return true if the 2 Least Significant Bits of the address equals 0b11.
+ *
+ * @hide
+ */
+ public static boolean isAddressRandomStatic(@NonNull String address) {
+ Preconditions.checkNotNull(address);
+ return checkBluetoothAddress(address)
+ && (Integer.parseInt(address.split(":")[5], 16) & 0b11) == 0b11;
+ }
+
@UnsupportedAppUsage
/*package*/ IBluetoothManager getBluetoothManager() {
return mManagerService;
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 07dbdce..8908649 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1022,6 +1022,24 @@
public static final String EXTRA_MAS_INSTANCE =
"android.bluetooth.device.extra.MAS_INSTANCE";
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = { "ADDRESS_TYPE_" },
+ value = {
+ /** Hardware MAC Address */
+ ADDRESS_TYPE_PUBLIC,
+ /** Address is either resolvable, non-resolvable or static.*/
+ ADDRESS_TYPE_RANDOM,
+ }
+ )
+ public @interface AddressType {}
+
+ /** Hardware MAC Address of the device */
+ public static final int ADDRESS_TYPE_PUBLIC = 0;
+ /** Address is either resolvable, non-resolvable or static. */
+ public static final int ADDRESS_TYPE_RANDOM = 1;
+
/**
* Lazy initialization. Guaranteed final after first object constructed, or
* getService() called.
@@ -1030,6 +1048,7 @@
private static volatile IBluetooth sService;
private final String mAddress;
+ @AddressType private final int mAddressType;
/*package*/
@UnsupportedAppUsage
@@ -1084,6 +1103,7 @@
}
mAddress = address;
+ mAddressType = ADDRESS_TYPE_PUBLIC;
}
@Override
diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java
index 51f63f7..7459f87 100644
--- a/core/java/android/bluetooth/le/ScanFilter.java
+++ b/core/java/android/bluetooth/le/ScanFilter.java
@@ -16,15 +16,19 @@
package android.bluetooth.le;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothDevice.AddressType;
import android.os.Parcel;
import android.os.ParcelUuid;
import android.os.Parcelable;
import com.android.internal.util.BitUtils;
+import com.android.internal.util.Preconditions;
import java.util.Arrays;
import java.util.List;
@@ -53,6 +57,11 @@
@Nullable
private final String mDeviceAddress;
+ private final @AddressType int mAddressType;
+
+ @Nullable
+ private final byte[] mIrk;
+
@Nullable
private final ParcelUuid mServiceUuid;
@Nullable
@@ -79,12 +88,12 @@
/** @hide */
public static final ScanFilter EMPTY = new ScanFilter.Builder().build();
-
private ScanFilter(String name, String deviceAddress, ParcelUuid uuid,
ParcelUuid uuidMask, ParcelUuid solicitationUuid,
ParcelUuid solicitationUuidMask, ParcelUuid serviceDataUuid,
byte[] serviceData, byte[] serviceDataMask,
- int manufacturerId, byte[] manufacturerData, byte[] manufacturerDataMask) {
+ int manufacturerId, byte[] manufacturerData, byte[] manufacturerDataMask,
+ @AddressType int addressType, @Nullable byte[] irk) {
mDeviceName = name;
mServiceUuid = uuid;
mServiceUuidMask = uuidMask;
@@ -97,6 +106,8 @@
mManufacturerId = manufacturerId;
mManufacturerData = manufacturerData;
mManufacturerDataMask = manufacturerDataMask;
+ mAddressType = addressType;
+ mIrk = irk;
}
@Override
@@ -280,6 +291,23 @@
return mDeviceAddress;
}
+ /**
+ * @hide
+ */
+ @SystemApi
+ public @AddressType int getAddressType() {
+ return mAddressType;
+ }
+
+ /**
+ * @hide
+ */
+ @SystemApi
+ @Nullable
+ public byte[] getIrk() {
+ return mIrk;
+ }
+
@Nullable
public byte[] getServiceData() {
return mServiceData;
@@ -516,8 +544,16 @@
*/
public static final class Builder {
+ /**
+ * @hide
+ */
+ @SystemApi
+ public static final int LEN_IRK_OCTETS = 16;
+
private String mDeviceName;
private String mDeviceAddress;
+ private @AddressType int mAddressType = BluetoothDevice.ADDRESS_TYPE_PUBLIC;
+ private byte[] mIrk;
private ParcelUuid mServiceUuid;
private ParcelUuid mUuidMask;
@@ -546,14 +582,130 @@
*
* @param deviceAddress The device Bluetooth address for the filter. It needs to be in the
* format of "01:02:03:AB:CD:EF". The device address can be validated using {@link
- * BluetoothAdapter#checkBluetoothAddress}.
+ * BluetoothAdapter#checkBluetoothAddress}. The @AddressType is defaulted to {@link
+ * BluetoothDevice#ADDRESS_TYPE_PUBLIC}
* @throws IllegalArgumentException If the {@code deviceAddress} is invalid.
*/
public Builder setDeviceAddress(String deviceAddress) {
- if (deviceAddress != null && !BluetoothAdapter.checkBluetoothAddress(deviceAddress)) {
+ return setDeviceAddress(mDeviceAddress, BluetoothDevice.ADDRESS_TYPE_PUBLIC);
+ }
+
+ /**
+ * Set filter on Address with AddressType
+ *
+ * <p>This key is used to resolve a private address from a public address.
+ *
+ * @param deviceAddress The device Bluetooth address for the filter. It needs to be in the
+ * format of "01:02:03:AB:CD:EF". The device address can be validated using {@link
+ * BluetoothAdapter#checkBluetoothAddress}. May be any type of address.
+ * @param addressType indication of the type of address
+ * e.g. {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC}
+ * or {@link BluetoothDevice#ADDRESS_TYPE_RANDOM}
+ *
+ * @throws IllegalArgumentException If the {@code deviceAddress} is invalid.
+ * @throws IllegalArgumentException If the {@code addressType} is invalid length
+ * @throws NullPointerException if {@code deviceAddress} is null.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public Builder setDeviceAddress(@NonNull String deviceAddress,
+ @AddressType int addressType) {
+ return setDeviceAddressInternal(deviceAddress, addressType, null);
+ }
+
+ /**
+ * Set filter on Address with AddressType and the Identity Resolving Key (IRK).
+ *
+ * <p>The IRK is used to resolve a {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC} from
+ * a PRIVATE_ADDRESS type.
+ *
+ * @param deviceAddress The device Bluetooth address for the filter. It needs to be in the
+ * format of "01:02:03:AB:CD:EF". The device address can be validated using {@link
+ * BluetoothAdapter#checkBluetoothAddress}. This Address type must only be PUBLIC OR RANDOM
+ * STATIC.
+ * @param addressType indication of the type of address
+ * e.g. {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC}
+ * or {@link BluetoothDevice#ADDRESS_TYPE_RANDOM}
+ * @param irk non-null byte array representing the Identity Resolving Key
+ *
+ * @throws IllegalArgumentException If the {@code deviceAddress} is invalid.
+ * @throws IllegalArgumentException if the {@code irk} is invalid length.
+ * @throws IllegalArgumentException If the {@code addressType} is invalid length or is not
+ * PUBLIC or RANDOM STATIC when an IRK is present.
+ * @throws NullPointerException if {@code deviceAddress} or {@code irk} is null.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public Builder setDeviceAddress(@NonNull String deviceAddress,
+ @AddressType int addressType,
+ @NonNull byte[] irk) {
+ Preconditions.checkNotNull(irk);
+ if (irk.length != LEN_IRK_OCTETS) {
+ throw new IllegalArgumentException("'irk' is invalid length!");
+ }
+ return setDeviceAddressInternal(deviceAddress, addressType, irk);
+ }
+
+ /**
+ * Set filter on Address with AddressType and the Identity Resolving Key (IRK).
+ *
+ * <p>Internal setter for the device address
+ *
+ * @param deviceAddress The device Bluetooth address for the filter. It needs to be in the
+ * format of "01:02:03:AB:CD:EF". The device address can be validated using {@link
+ * BluetoothAdapter#checkBluetoothAddress}.
+ * @param addressType indication of the type of address
+ * e.g. {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC}
+ * @param irk non-null byte array representing the Identity Resolving Address; nullable
+ * internally.
+ *
+ * @throws IllegalArgumentException If the {@code deviceAddress} is invalid.
+ * @throws IllegalArgumentException If the {@code addressType} is invalid length.
+ * @throws NullPointerException if {@code deviceAddress} is null.
+ *
+ * @hide
+ */
+ @NonNull
+ private Builder setDeviceAddressInternal(@NonNull String deviceAddress,
+ @AddressType int addressType,
+ @Nullable byte[] irk) {
+
+ // Make sure our deviceAddress is valid!
+ Preconditions.checkNotNull(deviceAddress);
+ if (!BluetoothAdapter.checkBluetoothAddress(deviceAddress)) {
throw new IllegalArgumentException("invalid device address " + deviceAddress);
}
+
+ // Verify type range
+ if (addressType < BluetoothDevice.ADDRESS_TYPE_PUBLIC
+ || addressType > BluetoothDevice.ADDRESS_TYPE_RANDOM) {
+ throw new IllegalArgumentException("'addressType' is invalid!");
+ }
+
+ // IRK can only be used for a PUBLIC or RANDOM (STATIC) Address.
+ if (addressType == BluetoothDevice.ADDRESS_TYPE_RANDOM) {
+ // Don't want a bad combination of address and irk!
+ if (irk != null) {
+ // Since there are 3 possible RANDOM subtypes we must check to make sure
+ // the correct type of address is used.
+ if (!BluetoothAdapter.isAddressRandomStatic(deviceAddress)) {
+ throw new IllegalArgumentException(
+ "Invalid combination: IRK requires either a PUBLIC or "
+ + "RANDOM (STATIC) Address");
+ }
+ }
+ }
+
+ // PUBLIC doesn't require extra work
+ // Without an IRK any address may be accepted
+
mDeviceAddress = deviceAddress;
+ mAddressType = addressType;
+ mIrk = irk;
return this;
}
@@ -727,7 +879,8 @@
mServiceUuid, mUuidMask, mServiceSolicitationUuid,
mServiceSolicitationUuidMask,
mServiceDataUuid, mServiceData, mServiceDataMask,
- mManufacturerId, mManufacturerData, mManufacturerDataMask);
+ mManufacturerId, mManufacturerData, mManufacturerDataMask,
+ mAddressType, mIrk);
}
}
}
diff --git a/core/java/android/bluetooth/le/ScanSettings.java b/core/java/android/bluetooth/le/ScanSettings.java
index 504118e..368d1ee 100644
--- a/core/java/android/bluetooth/le/ScanSettings.java
+++ b/core/java/android/bluetooth/le/ScanSettings.java
@@ -52,6 +52,16 @@
public static final int SCAN_MODE_LOW_LATENCY = 2;
/**
+ * Perform Bluetooth LE scan in ambient discovery mode. This mode has lower duty cycle and more
+ * aggressive scan interval than balanced mode that provides a good trade-off between scan
+ * latency and power consumption.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int SCAN_MODE_AMBIENT_DISCOVERY = 3;
+
+ /**
* Trigger a callback for every Bluetooth advertisement found that matches the filter criteria.
* If no filter is active, all advertisement packets are reported.
*/
@@ -276,10 +286,17 @@
* @throws IllegalArgumentException If the {@code scanMode} is invalid.
*/
public Builder setScanMode(int scanMode) {
- if (scanMode < SCAN_MODE_OPPORTUNISTIC || scanMode > SCAN_MODE_LOW_LATENCY) {
- throw new IllegalArgumentException("invalid scan mode " + scanMode);
+ switch (scanMode) {
+ case SCAN_MODE_OPPORTUNISTIC:
+ case SCAN_MODE_LOW_POWER:
+ case SCAN_MODE_BALANCED:
+ case SCAN_MODE_LOW_LATENCY:
+ case SCAN_MODE_AMBIENT_DISCOVERY:
+ mScanMode = scanMode;
+ break;
+ default:
+ throw new IllegalArgumentException("invalid scan mode " + scanMode);
}
- mScanMode = scanMode;
return this;
}
diff --git a/core/java/android/content/ComponentCallbacksController.java b/core/java/android/content/ComponentCallbacksController.java
new file mode 100644
index 0000000..a81aaf7
--- /dev/null
+++ b/core/java/android/content/ComponentCallbacksController.java
@@ -0,0 +1,127 @@
+/*
+ * 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.content;
+
+import android.annotation.NonNull;
+import android.content.res.Configuration;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * A helper class to manage {@link ComponentCallbacks} and {@link ComponentCallbacks2}, such as
+ * registering ,unregistering {@link ComponentCallbacks} and sending callbacks to all registered
+ * {@link ComponentCallbacks}.
+ *
+ * @see Context#registerComponentCallbacks(ComponentCallbacks)
+ * @see Context#unregisterComponentCallbacks(ComponentCallbacks)
+ * @see ComponentCallbacks
+ * @see ComponentCallbacks2
+ *
+ * @hide
+ */
+public class ComponentCallbacksController {
+ @GuardedBy("mLock")
+ private List<ComponentCallbacks> mComponentCallbacks;
+
+ private final Object mLock = new Object();
+
+ /**
+ * Register the {@link ComponentCallbacks}.
+ *
+ * @see Context#registerComponentCallbacks(ComponentCallbacks)
+ */
+ public void registerCallbacks(@NonNull ComponentCallbacks callbacks) {
+ synchronized (mLock) {
+ if (mComponentCallbacks == null) {
+ mComponentCallbacks = new ArrayList<>();
+ }
+ mComponentCallbacks.add(callbacks);
+ }
+ }
+
+ /**
+ * Unregister the {@link ComponentCallbacks}.
+ *
+ * @see Context#unregisterComponentCallbacks(ComponentCallbacks)
+ */
+ public void unregisterCallbacks(@NonNull ComponentCallbacks callbacks) {
+ synchronized (mLock) {
+ if (mComponentCallbacks == null || mComponentCallbacks.isEmpty()) {
+ return;
+ }
+ mComponentCallbacks.remove(callbacks);
+ }
+ }
+
+ /**
+ * Clear all registered {@link ComponentCallbacks}.
+ * It is useful when the associated {@link Context} is going to be released.
+ */
+ public void clearCallbacks() {
+ synchronized (mLock) {
+ if (mComponentCallbacks != null) {
+ mComponentCallbacks.clear();
+ }
+ }
+ }
+
+ /**
+ * Sending {@link ComponentCallbacks#onConfigurationChanged(Configuration)} to all registered
+ * {@link ComponentCallbacks}.
+ */
+ public void dispatchConfigurationChanged(@NonNull Configuration newConfig) {
+ forAllComponentCallbacks(callbacks -> callbacks.onConfigurationChanged(newConfig));
+ }
+
+ /**
+ * Sending {@link ComponentCallbacks#onLowMemory()} to all registered
+ * {@link ComponentCallbacks}.
+ */
+ public void dispatchLowMemory() {
+ forAllComponentCallbacks(ComponentCallbacks::onLowMemory);
+ }
+
+ /**
+ * Sending {@link ComponentCallbacks2#onTrimMemory(int)} to all registered
+ * {@link ComponentCallbacks2}.
+ */
+ public void dispatchTrimMemory(int level) {
+ forAllComponentCallbacks(callbacks -> {
+ if (callbacks instanceof ComponentCallbacks2) {
+ ((ComponentCallbacks2) callbacks).onTrimMemory(level);
+ }
+ });
+ }
+
+ private void forAllComponentCallbacks(Consumer<ComponentCallbacks> callbacksConsumer) {
+ final ComponentCallbacks[] callbacksArray;
+ synchronized (mLock) {
+ if (mComponentCallbacks == null || mComponentCallbacks.isEmpty()) {
+ return;
+ }
+ callbacksArray = new ComponentCallbacks[mComponentCallbacks.size()];
+ mComponentCallbacks.toArray(callbacksArray);
+ }
+ for (ComponentCallbacks callbacks : callbacksArray) {
+ callbacksConsumer.accept(callbacks);
+ }
+ }
+}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 64ca92f..92ff640 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -655,12 +655,21 @@
/**
* Add a new {@link ComponentCallbacks} to the base application of the
* Context, which will be called at the same times as the ComponentCallbacks
- * methods of activities and other components are called. Note that you
+ * methods of activities and other components are called. Note that you
* <em>must</em> be sure to use {@link #unregisterComponentCallbacks} when
* appropriate in the future; this will not be removed for you.
+ * <p>
+ * After {@link Build.VERSION_CODES#S}, Registering the ComponentCallbacks to Context created
+ * via {@link #createWindowContext(int, Bundle)} or
+ * {@link #createWindowContext(Display, int, Bundle)} will receive
+ * {@link ComponentCallbacks#onConfigurationChanged(Configuration)} from Window Context rather
+ * than its base application. It is helpful if you want to handle UI components that
+ * associated with the Window Context when the Window Context has configuration changes.</p>
*
* @param callback The interface to call. This can be either a
* {@link ComponentCallbacks} or {@link ComponentCallbacks2} interface.
+ *
+ * @see Context#createWindowContext(int, Bundle)
*/
public void registerComponentCallbacks(ComponentCallbacks callback) {
getApplicationContext().registerComponentCallbacks(callback);
@@ -6358,6 +6367,16 @@
* windowContext.getSystemService(WindowManager.class).addView(overlayView, mParams);
* </pre>
* <p>
+ * After {@link Build.VERSION_CODES#S}, window context provides the capability to listen to its
+ * {@link Configuration} changes by calling
+ * {@link #registerComponentCallbacks(ComponentCallbacks)}, while other kinds of {@link Context}
+ * will register the {@link ComponentCallbacks} to {@link #getApplicationContext() its
+ * Application context}. Note that window context only propagate
+ * {@link ComponentCallbacks#onConfigurationChanged(Configuration)} callback.
+ * {@link ComponentCallbacks#onLowMemory()} or other callbacks in {@link ComponentCallbacks2}
+ * won't be invoked.
+ * </p>
+ * <p>
* Note that using {@link android.app.Application} or {@link android.app.Service} context for
* UI-related queries may result in layout or continuity issues on devices with variable screen
* sizes (e.g. foldables) or in multi-window modes, since these non-UI contexts may not reflect
diff --git a/core/java/android/content/TEST_MAPPING b/core/java/android/content/TEST_MAPPING
index a2880df..614143e 100644
--- a/core/java/android/content/TEST_MAPPING
+++ b/core/java/android/content/TEST_MAPPING
@@ -44,9 +44,12 @@
},
{
"include-filter": "android.content.ContextTest"
+ },
+ {
+ "include-filter": "android.content.ComponentCallbacksControllerTest"
}
],
- "file_patterns": ["(/|^)Context.java", "(/|^)ContextWrapper.java"]
+ "file_patterns": ["(/|^)Context.java", "(/|^)ContextWrapper.java", "(/|^)ComponentCallbacksController.java"]
}
]
}
\ No newline at end of file
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationManager.java b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
index 55e19f2..d187f60 100644
--- a/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
@@ -341,11 +341,11 @@
}
/**
- * Retrieve the user selection data for the given {@param packageName} and the current user.
+ * Retrieve the user state for the given package and the {@link Context}'s user.
*
* @param packageName The app to query state for.
- * @return the user selection verification data for the given package for the current user, or
- * null if the package does not declare any HTTP/HTTPS domains.
+ * @return The user selection verification data for the given package for the user, or null if
+ * the package does not declare any HTTP/HTTPS domains.
*/
@Nullable
public DomainVerificationUserState getDomainVerificationUserState(
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 3da1227..96e7d3b 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -326,6 +326,20 @@
* @hide
*/
public static final int ALLOWED_METERED_REASON_USER_EXEMPTED = 1 << 16;
+ /**
+ * Flag to indicate that app is exempt from certain metered network restrictions because of it
+ * being a system component.
+ *
+ * @hide
+ */
+ public static final int ALLOWED_METERED_REASON_SYSTEM = 1 << 17;
+ /**
+ * Flag to indicate that app is exempt from certain metered network restrictions because of it
+ * being in the foreground.
+ *
+ * @hide
+ */
+ public static final int ALLOWED_METERED_REASON_FOREGROUND = 1 << 18;
/** @hide */
public static final int ALLOWED_METERED_REASON_MASK = 0xffff0000;
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/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index e9bbcc79..7e40497 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -358,6 +358,38 @@
public static final String NAMESPACE_SETTINGS_STATS = "settings_stats";
/**
+ * Namespace for all statsd java features that can be applied immediately.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_STATSD_JAVA = "statsd_java";
+
+ /**
+ * Namespace for all statsd java features that are applied on boot.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_STATSD_JAVA_BOOT = "statsd_java_boot";
+
+ /**
+ * Namespace for all statsd native features that can be applied immediately.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_STATSD_NATIVE = "statsd_native";
+
+ /**
+ * Namespace for all statsd native features that are applied on boot.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_STATSD_NATIVE_BOOT = "statsd_native_boot";
+
+ /**
* Namespace for storage-related features.
*
* @deprecated Replace storage namespace with storage_native_boot.
@@ -456,7 +488,8 @@
*/
@NonNull
private static final List<String> PUBLIC_NAMESPACES =
- Arrays.asList(NAMESPACE_TEXTCLASSIFIER, NAMESPACE_RUNTIME);
+ Arrays.asList(NAMESPACE_TEXTCLASSIFIER, NAMESPACE_RUNTIME, NAMESPACE_STATSD_JAVA,
+ NAMESPACE_STATSD_JAVA_BOOT);
/**
* Privacy related properties definitions.
*
@@ -505,38 +538,6 @@
"connectivity_thermal_power_manager";
/**
- * Namespace for all statsd java features that can be applied immediately.
- *
- * @hide
- */
- @SystemApi
- public static final String NAMESPACE_STATSD_JAVA = "statsd_java";
-
- /**
- * Namespace for all statsd java features that are applied on boot.
- *
- * @hide
- */
- @SystemApi
- public static final String NAMESPACE_STATSD_JAVA_BOOT = "statsd_java_boot";
-
- /**
- * Namespace for all statsd native features that can be applied immediately.
- *
- * @hide
- */
- @SystemApi
- public static final String NAMESPACE_STATSD_NATIVE = "statsd_native";
-
- /**
- * Namespace for all statsd native features that are applied on boot.
- *
- * @hide
- */
- @SystemApi
- public static final String NAMESPACE_STATSD_NATIVE_BOOT = "statsd_native_boot";
-
- /**
* Namespace for configuration related features.
*
* @hide
@@ -646,7 +647,7 @@
* @param name The name of the property to look up.
* @param defaultValue The value to return if the property does not exist or has no non-null
* value.
- * @return the corresponding value, or defaultValue if none exists.
+ * @return the correspondfing value, or defaultValue if none exists.
* @hide
*/
@SystemApi
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index bf5f24d..09c3b2e 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -251,6 +251,10 @@
public static final int REASON_SNOOZED = 18;
/** Notification was canceled due to timeout */
public static final int REASON_TIMEOUT = 19;
+ /** Notification was canceled due to the backing channel being deleted */
+ public static final int REASON_CHANNEL_REMOVED = 20;
+ /** Notification was canceled due to the app's storage being cleared */
+ public static final int REASON_CLEAR_DATA = 21;
/**
* @hide
diff --git a/core/java/android/service/notification/ScheduleCalendar.java b/core/java/android/service/notification/ScheduleCalendar.java
index 6ed966e..314c97d 100644
--- a/core/java/android/service/notification/ScheduleCalendar.java
+++ b/core/java/android/service/notification/ScheduleCalendar.java
@@ -57,25 +57,18 @@
}
/**
- * Sets next alarm of the schedule if the saved next alarm has passed or is further
- * in the future than given nextAlarm
+ * Sets next alarm of the schedule
* @param now current time in milliseconds
* @param nextAlarm time of next alarm in milliseconds
*/
public void maybeSetNextAlarm(long now, long nextAlarm) {
if (mSchedule != null && mSchedule.exitAtAlarm) {
- // alarm canceled
if (nextAlarm == 0) {
+ // alarm canceled
mSchedule.nextAlarm = 0;
- }
- // only allow alarms in the future
- if (nextAlarm > now) {
- if (mSchedule.nextAlarm == 0 || mSchedule.nextAlarm < now) {
- mSchedule.nextAlarm = nextAlarm;
- } else {
- // store earliest alarm
- mSchedule.nextAlarm = Math.min(mSchedule.nextAlarm, nextAlarm);
- }
+ } else if (nextAlarm > now) {
+ // only allow alarms in the future
+ mSchedule.nextAlarm = nextAlarm;
} else if (mSchedule.nextAlarm < now) {
if (DEBUG) {
Log.d(TAG, "All alarms are in the past " + mSchedule.nextAlarm);
diff --git a/core/java/android/util/Slog.java b/core/java/android/util/Slog.java
index d3eaeae..e5106e2 100644
--- a/core/java/android/util/Slog.java
+++ b/core/java/android/util/Slog.java
@@ -26,6 +26,10 @@
import java.util.Locale;
/**
+ * API for sending log output to the {@link Log#LOG_ID_SYSTEM} buffer.
+ *
+ * <p>Should be used by system components. Use {@code adb logcat --buffer=system} to fetch the logs.
+ *
* @hide
*/
public final class Slog {
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/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index f4d5a7b..98b7dbf 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -50,10 +50,6 @@
super(ITYPE_IME, state, transactionSupplier, controller);
}
- public void applyImeVisibility(boolean setVisible) {
- mController.applyImeVisibility(setVisible);
- }
-
@Override
public void onWindowFocusGained() {
super.onWindowFocusGained();
diff --git a/core/java/android/view/InputEventAssigner.java b/core/java/android/view/InputEventAssigner.java
index c159a12..7fac6c5 100644
--- a/core/java/android/view/InputEventAssigner.java
+++ b/core/java/android/view/InputEventAssigner.java
@@ -45,13 +45,13 @@
public class InputEventAssigner {
private static final String TAG = "InputEventAssigner";
private boolean mHasUnprocessedDown = false;
- private int mEventId = INVALID_INPUT_EVENT_ID;
+ private int mDownEventId = INVALID_INPUT_EVENT_ID;
/**
- * Notify InputEventAssigner that the Choreographer callback has been processed. This will reset
- * the 'down' state to assign the latest input event to the current frame.
+ * Notify InputEventAssigner that a frame has been processed. We no longer need to keep track of
+ * the DOWN event because a frame has already been produced for it.
*/
- public void onChoreographerCallback() {
+ public void notifyFrameProcessed() {
// Mark completion of this frame. Use newest input event from now on.
mHasUnprocessedDown = false;
}
@@ -62,31 +62,22 @@
* @return the id of the input event to use for the current frame
*/
public int processEvent(InputEvent event) {
- if (event instanceof KeyEvent) {
- // We will not do any special handling for key events
- return event.getId();
- }
-
if (event instanceof MotionEvent) {
MotionEvent motionEvent = (MotionEvent) event;
- final int action = motionEvent.getActionMasked();
-
- if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
- mHasUnprocessedDown = false;
+ if (motionEvent.isFromSource(SOURCE_TOUCHSCREEN)) {
+ final int action = motionEvent.getActionMasked();
+ if (action == MotionEvent.ACTION_DOWN) {
+ mHasUnprocessedDown = true;
+ mDownEventId = event.getId();
+ }
+ if (mHasUnprocessedDown && action == MotionEvent.ACTION_MOVE) {
+ return mDownEventId;
+ }
+ if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
+ mHasUnprocessedDown = false;
+ }
}
- if (motionEvent.isFromSource(SOURCE_TOUCHSCREEN) && action == MotionEvent.ACTION_DOWN) {
- mHasUnprocessedDown = true;
- mEventId = event.getId();
- // This will remain 'true' even if we receive a MOVE event, as long as choreographer
- // hasn't invoked the 'CALLBACK_INPUT' callback.
- }
- // Don't update the event id if we haven't processed DOWN yet.
- if (!mHasUnprocessedDown) {
- mEventId = event.getId();
- }
- return mEventId;
}
-
- throw new IllegalArgumentException("Received unexpected " + event);
+ return event.getId();
}
}
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 7a61df8..a67ec10 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -269,7 +269,7 @@
}
mShownOnFinish = shown;
mFinished = true;
- setInsetsAndAlpha(shown ? mShownInsets : mHiddenInsets, 1f /* alpha */, 1f /* fraction */,
+ setInsetsAndAlpha(shown ? mShownInsets : mHiddenInsets, mPendingAlpha, 1f /* fraction */,
true /* allowWhenFinished */);
if (DEBUG) Log.d(TAG, "notify control request finished for types: " + mTypes);
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 8bf78db3..a68f528 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -189,16 +189,34 @@
}
private static final String TAG = "InsetsController";
- private static final int ANIMATION_DURATION_SHOW_MS = 275;
- private static final int ANIMATION_DURATION_HIDE_MS = 340;
+ private static final int ANIMATION_DURATION_MOVE_IN_MS = 275;
+ private static final int ANIMATION_DURATION_MOVE_OUT_MS = 340;
+ private static final int ANIMATION_DURATION_FADE_IN_MS = 500;
+ private static final int ANIMATION_DURATION_FADE_OUT_MS = 1500;
+
+ private static final int ANIMATION_DELAY_DIM_MS = 500;
private static final int ANIMATION_DURATION_SYNC_IME_MS = 285;
private static final int ANIMATION_DURATION_UNSYNC_IME_MS = 200;
private static final int PENDING_CONTROL_TIMEOUT_MS = 2000;
- public static final Interpolator SYSTEM_BARS_INTERPOLATOR =
+ private static final Interpolator SYSTEM_BARS_INSETS_INTERPOLATOR =
new PathInterpolator(0.4f, 0f, 0.2f, 1f);
+ private static final Interpolator SYSTEM_BARS_ALPHA_INTERPOLATOR =
+ new PathInterpolator(0.3f, 0f, 1f, 1f);
+ private static final Interpolator SYSTEM_BARS_DIM_INTERPOLATOR = alphaFraction -> {
+ // While playing dim animation, alphaFraction is changed from 1f to 0f. Here changes it to
+ // time-based fraction for computing delay and interpolation.
+ float fraction = 1 - alphaFraction;
+ final float fractionDelay = (float) ANIMATION_DELAY_DIM_MS / ANIMATION_DURATION_FADE_OUT_MS;
+ if (fraction <= fractionDelay) {
+ return 1f;
+ } else {
+ float innerFraction = (fraction - fractionDelay) / (1f - fractionDelay);
+ return 1f - SYSTEM_BARS_ALPHA_INTERPOLATOR.getInterpolation(innerFraction);
+ }
+ };
private static final Interpolator SYNC_IME_INTERPOLATOR =
new PathInterpolator(0.2f, 0f, 0f, 1f);
private static final Interpolator LINEAR_OUT_SLOW_IN_INTERPOLATOR =
@@ -206,6 +224,9 @@
private static final Interpolator FAST_OUT_LINEAR_IN_INTERPOLATOR =
new PathInterpolator(0.4f, 0f, 1f, 1f);
+ /** The amount IME will move up/down when animating in floating mode. */
+ private static final int FLOATING_IME_BOTTOM_INSET_DP = -80;
+
static final boolean DEBUG = false;
static final boolean WARN = false;
@@ -278,14 +299,12 @@
public static class InternalAnimationControlListener
implements WindowInsetsAnimationControlListener {
- /** The amount IME will move up/down when animating in floating mode. */
- protected static final int FLOATING_IME_BOTTOM_INSET = -80;
-
private WindowInsetsAnimationController mController;
private ValueAnimator mAnimator;
private final boolean mShow;
private final boolean mHasAnimationCallbacks;
private final @InsetsType int mRequestedTypes;
+ private final @Behavior int mBehavior;
private final long mDurationMs;
private final boolean mDisable;
private final int mFloatingImeBottomInset;
@@ -301,10 +320,12 @@
};
public InternalAnimationControlListener(boolean show, boolean hasAnimationCallbacks,
- int requestedTypes, boolean disable, int floatingImeBottomInset) {
+ @InsetsType int requestedTypes, @Behavior int behavior, boolean disable,
+ int floatingImeBottomInset) {
mShow = show;
mHasAnimationCallbacks = hasAnimationCallbacks;
mRequestedTypes = requestedTypes;
+ mBehavior = behavior;
mDurationMs = calculateDurationMs();
mDisable = disable;
mFloatingImeBottomInset = floatingImeBottomInset;
@@ -335,7 +356,7 @@
Insets end = mShow
? controller.getShownStateInsets()
: hiddenInsets;
- Interpolator insetsInterpolator = getInterpolator();
+ Interpolator insetsInterpolator = getInsetsInterpolator();
Interpolator alphaInterpolator = getAlphaInterpolator();
mAnimator.addUpdateListener(animation -> {
float rawFraction = animation.getAnimatedFraction();
@@ -379,7 +400,7 @@
+ mRequestedTypes);
}
- Interpolator getInterpolator() {
+ protected Interpolator getInsetsInterpolator() {
if ((mRequestedTypes & ime()) != 0) {
if (mHasAnimationCallbacks) {
return SYNC_IME_INTERPOLATOR;
@@ -389,7 +410,12 @@
return FAST_OUT_LINEAR_IN_INTERPOLATOR;
}
} else {
- return SYSTEM_BARS_INTERPOLATOR;
+ if (mBehavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE) {
+ return SYSTEM_BARS_INSETS_INTERPOLATOR;
+ } else {
+ // Makes insets stay at the shown position.
+ return input -> mShow ? 1f : 0f;
+ }
}
}
@@ -405,7 +431,15 @@
return FAST_OUT_LINEAR_IN_INTERPOLATOR;
}
} else {
- return input -> 1f;
+ if (mBehavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE) {
+ return input -> 1f;
+ } else {
+ if (mShow) {
+ return SYSTEM_BARS_ALPHA_INTERPOLATOR;
+ } else {
+ return SYSTEM_BARS_DIM_INTERPOLATOR;
+ }
+ }
}
}
@@ -429,7 +463,11 @@
return ANIMATION_DURATION_UNSYNC_IME_MS;
}
} else {
- return mShow ? ANIMATION_DURATION_SHOW_MS : ANIMATION_DURATION_HIDE_MS;
+ if (mBehavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE) {
+ return mShow ? ANIMATION_DURATION_MOVE_IN_MS : ANIMATION_DURATION_MOVE_OUT_MS;
+ } else {
+ return mShow ? ANIMATION_DURATION_FADE_IN_MS : ANIMATION_DURATION_FADE_OUT_MS;
+ }
}
}
}
@@ -882,7 +920,8 @@
hide(types, false /* fromIme */);
}
- void hide(@InsetsType int types, boolean fromIme) {
+ @VisibleForTesting
+ public void hide(@InsetsType int types, boolean fromIme) {
if (fromIme) {
ImeTracing.getInstance().triggerClientDump("InsetsController#hide",
mHost.getInputMethodManager(), null /* icProto */);
@@ -1274,19 +1313,6 @@
getSourceConsumer(ITYPE_IME).onWindowFocusLost();
}
- /**
- * Used by {@link ImeInsetsSourceConsumer} when IME decides to be shown/hidden.
- * @hide
- */
- @VisibleForTesting
- public void applyImeVisibility(boolean setVisible) {
- if (setVisible) {
- show(Type.IME, true /* fromIme */);
- } else {
- hide(Type.IME);
- }
- }
-
@VisibleForTesting
public @AnimationType int getAnimationType(@InternalInsetsType int type) {
for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
@@ -1358,14 +1384,14 @@
boolean hasAnimationCallbacks = mHost.hasAnimationCallbacks();
final InternalAnimationControlListener listener = new InternalAnimationControlListener(
- show, hasAnimationCallbacks, types, skipAnim || mAnimationsDisabled,
- mHost.dipToPx(InternalAnimationControlListener.FLOATING_IME_BOTTOM_INSET));
+ show, hasAnimationCallbacks, types, mHost.getSystemBarsBehavior(),
+ skipAnim || mAnimationsDisabled, mHost.dipToPx(FLOATING_IME_BOTTOM_INSET_DP));
// We are about to playing the default animation (show/hide). Passing a null frame indicates
// the controlled types should be animated regardless of the frame.
controlAnimationUnchecked(
types, null /* cancellationSignal */, listener, null /* frame */, fromIme,
- listener.getDurationMs(), listener.getInterpolator(),
+ listener.getDurationMs(), listener.getInsetsInterpolator(),
show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE,
show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN,
!hasAnimationCallbacks /* useInsetsAnimationThread */);
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/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 3ffe0c6..f1eef9f 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1296,7 +1296,7 @@
mBlastBufferQueue.destroy();
}
mBlastBufferQueue = new BLASTBufferQueue(name, mBlastSurfaceControl, mSurfaceWidth,
- mSurfaceHeight, mFormat, true /* TODO */);
+ mSurfaceHeight, mFormat);
}
private void onDrawFinished() {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e4fb611..dbccf10 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -330,7 +330,6 @@
private boolean mUseBLASTAdapter;
private boolean mForceDisableBLAST;
- private boolean mEnableTripleBuffering;
private boolean mFastScrollSoundEffectsEnabled;
@@ -474,6 +473,7 @@
FrameInfo frameInfo = mChoreographer.mFrameInfo;
mViewFrameInfo.populateFrameInfo(frameInfo);
mViewFrameInfo.reset();
+ mInputEventAssigner.notifyFrameProcessed();
return frameInfo;
}
@@ -1179,9 +1179,6 @@
if ((res & WindowManagerGlobal.ADD_FLAG_USE_BLAST) != 0) {
mUseBLASTAdapter = true;
}
- if ((res & WindowManagerGlobal.ADD_FLAG_USE_TRIPLE_BUFFERING) != 0) {
- mEnableTripleBuffering = true;
- }
if (view instanceof RootViewSurfaceTaker) {
mInputQueueCallback =
@@ -1373,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();
}
@@ -1907,7 +1897,7 @@
Surface ret = null;
if (mBlastBufferQueue == null) {
mBlastBufferQueue = new BLASTBufferQueue(mTag, mSurfaceControl, width, height,
- format, mEnableTripleBuffering);
+ format);
// We only return the Surface the first time, as otherwise
// it hasn't changed and there is no need to update.
ret = mBlastBufferQueue.createSurface();
@@ -8502,11 +8492,6 @@
consumedBatches = false;
}
doProcessInputEvents();
- if (consumedBatches) {
- // Must be done after we processed the input events, to mark the completion of the frame
- // from the input point of view
- mInputEventAssigner.onChoreographerCallback();
- }
return consumedBatches;
}
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/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 47ac1ee..18013e8 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -118,7 +118,6 @@
public static final int ADD_FLAG_IN_TOUCH_MODE = 0x1;
public static final int ADD_FLAG_APP_VISIBLE = 0x2;
- public static final int ADD_FLAG_USE_TRIPLE_BUFFERING = 0x4;
public static final int ADD_FLAG_USE_BLAST = 0x8;
/**
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/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 9872dc0..3cd3902 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -511,7 +511,6 @@
static final int MSG_TIMEOUT_INPUT_EVENT = 6;
static final int MSG_FLUSH_INPUT_EVENT = 7;
static final int MSG_REPORT_FULLSCREEN_MODE = 10;
- static final int MSG_APPLY_IME_VISIBILITY = 20;
static final int MSG_UPDATE_ACTIVITY_VIEW_TO_SCREEN_MATRIX = 30;
private static boolean isAutofillUIShowing(View servedView) {
@@ -985,17 +984,6 @@
}
return;
}
- case MSG_APPLY_IME_VISIBILITY: {
- synchronized (mH) {
- if (mImeInsetsConsumer != null) {
- ImeTracing.getInstance().triggerClientDump(
- "ImeInsetsSourceConsumer#applyImeVisibility",
- InputMethodManager.this, null /* icProto */);
- mImeInsetsConsumer.applyImeVisibility(msg.arg1 != 0);
- }
- }
- return;
- }
case MSG_UPDATE_ACTIVITY_VIEW_TO_SCREEN_MATRIX: {
final float[] matrixValues = (float[]) msg.obj;
final int bindSequence = msg.arg1;
@@ -1090,12 +1078,6 @@
}
@Override
- public void applyImeVisibility(boolean setVisible) {
- mH.obtainMessage(MSG_APPLY_IME_VISIBILITY, setVisible ? 1 : 0, 0)
- .sendToTarget();
- }
-
- @Override
public void updateActivityViewToScreenMatrix(int bindSequence, float[] matrixValues) {
mH.obtainMessage(MSG_UPDATE_ACTIVITY_VIEW_TO_SCREEN_MATRIX, bindSequence, 0,
matrixValues).sendToTarget();
diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java
index 58dfc0d..34ad659 100644
--- a/core/java/android/widget/EdgeEffect.java
+++ b/core/java/android/widget/EdgeEffect.java
@@ -640,6 +640,12 @@
mWidth,
mHeight
);
+ } else {
+ // This is TYPE_STRETCH and drawing into a Canvas that isn't a Recording Canvas,
+ // so no effect can be shown. Just end the effect.
+ mState = STATE_IDLE;
+ mDistance = 0;
+ mVelocity = 0;
}
boolean oneLastFrame = false;
@@ -771,8 +777,9 @@
* considered at rest or false if it is still animating.
*/
private boolean isAtEquilibrium() {
- double displacement = mDistance * mHeight; // in pixels
- return Math.abs(mVelocity) < VELOCITY_THRESHOLD
+ double displacement = mDistance * mHeight * LINEAR_STRETCH_INTENSITY; // in pixels
+ double velocity = mVelocity * LINEAR_STRETCH_INTENSITY;
+ return Math.abs(velocity) < VELOCITY_THRESHOLD
&& Math.abs(displacement) < VALUE_THRESHOLD;
}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 319e788..cb2bba1 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -5476,14 +5476,14 @@
/**
* Object allowing the modification of a context to overload the system's dynamic colors.
*
- * Only colors from {@link android.R.color#system_primary_0} to
- * {@link android.R.color#system_neutral_1000} can be overloaded.
+ * Only colors from {@link android.R.color#system_accent1_0} to
+ * {@link android.R.color#system_neutral2_1000} can be overloaded.
* @hide
*/
public static final class ColorResources {
// Set of valid colors resources.
- private static final int FIRST_RESOURCE_COLOR_ID = android.R.color.system_primary_0;
- private static final int LAST_RESOURCE_COLOR_ID = android.R.color.system_neutral_1000;
+ private static final int FIRST_RESOURCE_COLOR_ID = android.R.color.system_neutral1_0;
+ private static final int LAST_RESOURCE_COLOR_ID = android.R.color.system_accent3_1000;
// Size, in bytes, of an entry in the array of colors in an ARSC file.
private static final int ARSC_ENTRY_SIZE = 16;
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/app/AppPredictionServiceResolverComparator.java b/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
index 26af615..b76ef0f 100644
--- a/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
+++ b/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
@@ -28,11 +28,9 @@
import android.content.pm.ResolveInfo;
import android.os.Message;
import android.os.UserHandle;
-import android.provider.DeviceConfig;
import android.util.Log;
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import java.util.ArrayList;
import java.util.HashMap;
@@ -62,11 +60,6 @@
// back to using the ResolverRankerService.
private ResolverRankerServiceResolverComparator mResolverRankerService;
- private boolean mAppendDirectShareEnabled = DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.APPEND_DIRECT_SHARE_ENABLED,
- true);
-
AppPredictionServiceResolverComparator(
Context context,
Intent intent,
@@ -183,9 +176,6 @@
if (mResolverRankerService != null) {
return mResolverRankerService.getScore(name);
}
- if (mAppendDirectShareEnabled && !mTargetScores.isEmpty()) {
- return mTargetScores.get(name);
- }
Integer rank = mTargetRanks.get(name);
if (rank == null) {
Log.w(TAG, "Score requested for unknown component. Did you call compute yet?");
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index d4d8536..862c900 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -256,15 +256,6 @@
SystemUiDeviceConfigFlags.HASH_SALT_MAX_DAYS,
DEFAULT_SALT_EXPIRATION_DAYS);
- private boolean mAppendDirectShareEnabled = DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.APPEND_DIRECT_SHARE_ENABLED,
- true);
- private boolean mChooserTargetRankingEnabled = DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.CHOOSER_TARGET_RANKING_ENABLED,
- true);
-
private Bundle mReplacementExtras;
private IntentSender mChosenComponentSender;
private IntentSender mRefinementIntentSender;
@@ -472,16 +463,10 @@
private static final int SHORTCUT_MANAGER_SHARE_TARGET_RESULT = 4;
private static final int SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED = 5;
private static final int LIST_VIEW_UPDATE_MESSAGE = 6;
- private static final int CHOOSER_TARGET_RANKING_SCORE = 7;
private static final int WATCHDOG_TIMEOUT_MAX_MILLIS = 10000;
private static final int WATCHDOG_TIMEOUT_MIN_MILLIS = 3000;
- private static final int DEFAULT_DIRECT_SHARE_TIMEOUT_MILLIS = 1500;
- private int mDirectShareTimeout = DeviceConfig.getInt(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.SHARE_SHEET_DIRECT_SHARE_TIMEOUT,
- DEFAULT_DIRECT_SHARE_TIMEOUT_MILLIS);
-
private boolean mMinTimeoutPassed = false;
private void removeAllMessages() {
@@ -491,7 +476,6 @@
removeMessages(CHOOSER_TARGET_SERVICE_RESULT);
removeMessages(SHORTCUT_MANAGER_SHARE_TARGET_RESULT);
removeMessages(SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED);
- removeMessages(CHOOSER_TARGET_RANKING_SCORE);
}
private void restartServiceRequestTimer() {
@@ -501,14 +485,13 @@
if (DEBUG) {
Log.d(TAG, "queryTargets setting watchdog timer for "
- + mDirectShareTimeout + "-"
+ WATCHDOG_TIMEOUT_MAX_MILLIS + "ms");
}
sendEmptyMessageDelayed(CHOOSER_TARGET_SERVICE_WATCHDOG_MIN_TIMEOUT,
WATCHDOG_TIMEOUT_MIN_MILLIS);
sendEmptyMessageDelayed(CHOOSER_TARGET_SERVICE_WATCHDOG_MAX_TIMEOUT,
- mAppendDirectShareEnabled ? mDirectShareTimeout : WATCHDOG_TIMEOUT_MAX_MILLIS);
+ WATCHDOG_TIMEOUT_MAX_MILLIS);
}
private void maybeStopServiceRequestTimer() {
@@ -608,17 +591,6 @@
getChooserActivityLogger().logSharesheetDirectLoadComplete();
break;
- case CHOOSER_TARGET_RANKING_SCORE:
- if (DEBUG) Log.d(TAG, "CHOOSER_TARGET_RANKING_SCORE");
- final ChooserTargetRankingInfo scoreInfo = (ChooserTargetRankingInfo) msg.obj;
- ChooserListAdapter adapterForUserHandle =
- mChooserMultiProfilePagerAdapter.getListAdapterForUserHandle(
- scoreInfo.userHandle);
- if (adapterForUserHandle != null) {
- adapterForUserHandle.addChooserTargetRankingScore(scoreInfo.scores);
- }
- break;
-
default:
super.handleMessage(msg);
}
@@ -878,24 +850,14 @@
final List<ShortcutManager.ShareShortcutInfo> shareShortcutInfos =
new ArrayList<>();
- // Separate ChooserTargets ranking scores and ranked Shortcuts.
List<AppTarget> shortcutResults = new ArrayList<>();
- List<AppTarget> chooserTargetScores = new ArrayList<>();
for (AppTarget appTarget : resultList) {
if (appTarget.getShortcutInfo() == null) {
continue;
}
- if (appTarget.getShortcutInfo().getId().equals(CHOOSER_TARGET)) {
- chooserTargetScores.add(appTarget);
- } else {
- shortcutResults.add(appTarget);
- }
+ shortcutResults.add(appTarget);
}
resultList = shortcutResults;
- if (mChooserTargetRankingEnabled) {
- sendChooserTargetRankingScore(chooserTargetScores,
- chooserListAdapter.getUserHandle());
- }
for (AppTarget appTarget : resultList) {
shareShortcutInfos.add(new ShortcutManager.ShareShortcutInfo(
appTarget.getShortcutInfo(),
@@ -2148,14 +2110,6 @@
return true;
}
- private void sendChooserTargetRankingScore(List<AppTarget> chooserTargetScores,
- UserHandle userHandle) {
- final Message msg = Message.obtain();
- msg.what = ChooserHandler.CHOOSER_TARGET_RANKING_SCORE;
- msg.obj = new ChooserTargetRankingInfo(chooserTargetScores, userHandle);
- mChooserHandler.sendMessage(msg);
- }
-
private void sendShareShortcutInfoList(
List<ShortcutManager.ShareShortcutInfo> resultList,
ChooserListAdapter chooserListAdapter,
@@ -2376,9 +2330,6 @@
}
private void sendImpressionToAppPredictor(TargetInfo targetInfo, ChooserListAdapter adapter) {
- if (!mChooserTargetRankingEnabled) {
- return;
- }
AppPredictor directShareAppPredictor = getAppPredictorForDirectShareIfEnabled(
mChooserMultiProfilePagerAdapter.getCurrentUserHandle());
if (directShareAppPredictor == null) {
@@ -2399,11 +2350,6 @@
targetIds.add(new AppTargetId(
String.format("%s/%s/%s", shortcutId, componentName.flattenToString(),
SHORTCUT_TARGET)));
- } else {
- String titleHash = ChooserUtil.md5(chooserTarget.getTitle().toString());
- targetIds.add(new AppTargetId(
- String.format("%s/%s/%s", titleHash, componentName.flattenToString(),
- CHOOSER_TARGET)));
}
}
directShareAppPredictor.notifyLaunchLocationShown(LAUNCH_LOCATION_DIRECT_SHARE, targetIds);
@@ -2423,29 +2369,6 @@
if (mDirectShareAppTargetCache != null) {
appTarget = mDirectShareAppTargetCache.get(chooserTarget);
}
- if (mChooserTargetRankingEnabled && appTarget == null) {
- // Send ChooserTarget sharing info to AppPredictor.
- ComponentName componentName = mChooserTargetComponentNameCache.getOrDefault(
- chooserTarget.getComponentName(), chooserTarget.getComponentName());
- try {
- appTarget = new AppTarget.Builder(
- new AppTargetId(componentName.flattenToString()),
- new ShortcutInfo.Builder(
- createPackageContextAsUser(
- componentName.getPackageName(),
- 0 /* flags */,
- getUser()),
- CHOOSER_TARGET)
- .setActivity(componentName)
- .setShortLabel(ChooserUtil.md5(chooserTarget.getTitle().toString()))
- .build())
- .setClassName(componentName.getClassName())
- .build();
- } catch (NameNotFoundException e) {
- Log.e(TAG, "Could not look up service " + componentName
- + "; component name not found");
- }
- }
// This is a direct share click that was provided by the APS
if (appTarget != null) {
directShareAppPredictor.notifyAppTargetEvent(
@@ -3971,9 +3894,7 @@
// until they start to scroll
ChooserListAdapter adapter =
mChooserMultiProfilePagerAdapter.getActiveListAdapter();
- int validTargets =
- mAppendDirectShareEnabled ? adapter.getNumServiceTargetsForExpand()
- : adapter.getSelectableServiceTargetCount();
+ int validTargets = adapter.getSelectableServiceTargetCount();
if (validTargets <= maxTargetsPerRow) {
mHideDirectShareExpansion = true;
return;
diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java
index 5700668..cc2b12a 100644
--- a/core/java/com/android/internal/app/ChooserListAdapter.java
+++ b/core/java/com/android/internal/app/ChooserListAdapter.java
@@ -21,7 +21,6 @@
import android.app.ActivityManager;
import android.app.prediction.AppPredictor;
-import android.app.prediction.AppTarget;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -37,7 +36,6 @@
import android.provider.DeviceConfig;
import android.service.chooser.ChooserTarget;
import android.util.Log;
-import android.util.Pair;
import android.view.View;
import android.view.ViewGroup;
@@ -53,21 +51,13 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
public class ChooserListAdapter extends ResolverListAdapter {
private static final String TAG = "ChooserListAdapter";
private static final boolean DEBUG = false;
- private boolean mAppendDirectShareEnabled = DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.APPEND_DIRECT_SHARE_ENABLED,
- true);
-
private boolean mEnableStackedApps = true;
public static final int NO_POSITION = -1;
@@ -79,8 +69,6 @@
private static final int MAX_SUGGESTED_APP_TARGETS = 4;
private static final int MAX_CHOOSER_TARGETS_PER_APP = 2;
- private static final int MAX_SERVICE_TARGET_APP = 8;
- private static final int DEFAULT_DIRECT_SHARE_RANKING_SCORE = 1000;
/** {@link #getBaseScore} */
public static final float CALLER_TARGET_SCORE_BOOST = 900.f;
@@ -94,17 +82,11 @@
private int mNumShortcutResults = 0;
private Map<DisplayResolveInfo, LoadIconTask> mIconLoaders = new HashMap<>();
+ private boolean mApplySharingAppLimits;
// Reserve spots for incoming direct share targets by adding placeholders
private ChooserTargetInfo
mPlaceHolderTargetInfo = new ChooserActivity.PlaceHolderTargetInfo();
- private int mValidServiceTargetsNum = 0;
- private int mAvailableServiceTargetsNum = 0;
- private final Map<ComponentName, Pair<List<ChooserTargetInfo>, Integer>>
- mParkingDirectShareTargets = new HashMap<>();
- private final Map<ComponentName, Map<String, Integer>> mChooserTargetScores = new HashMap<>();
- private Set<ComponentName> mPendingChooserTargetService = new HashSet<>();
- private Set<ComponentName> mShortcutComponents = new HashSet<>();
private final List<ChooserTargetInfo> mServiceTargets = new ArrayList<>();
private final List<DisplayResolveInfo> mCallerTargets = new ArrayList<>();
@@ -183,6 +165,10 @@
if (mCallerTargets.size() == MAX_SUGGESTED_APP_TARGETS) break;
}
}
+ mApplySharingAppLimits = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.APPLY_SHARING_APP_LIMITS_IN_SYSUI,
+ true);
}
AppPredictor getAppPredictor() {
@@ -209,9 +195,6 @@
void refreshListView() {
if (mListViewDataChanged) {
- if (mAppendDirectShareEnabled) {
- appendServiceTargetsWithQuota();
- }
super.notifyDataSetChanged();
}
mListViewDataChanged = false;
@@ -221,10 +204,6 @@
private void createPlaceHolders() {
mNumShortcutResults = 0;
mServiceTargets.clear();
- mValidServiceTargetsNum = 0;
- mParkingDirectShareTargets.clear();
- mPendingChooserTargetService.clear();
- mShortcutComponents.clear();
for (int i = 0; i < mChooserListCommunicator.getMaxRankedTargets(); i++) {
mServiceTargets.add(mPlaceHolderTargetInfo);
}
@@ -517,33 +496,30 @@
+ targets.size()
+ " targets");
}
- if (mAppendDirectShareEnabled) {
- parkTargetIntoMemory(origTarget, targets, targetType, directShareToShortcutInfos,
- pendingChooserTargetServiceConnections);
- return;
- }
if (targets.size() == 0) {
return;
}
-
final float baseScore = getBaseScore(origTarget, targetType);
Collections.sort(targets, mBaseTargetComparator);
-
final boolean isShortcutResult =
(targetType == TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER
|| targetType == TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE);
final int maxTargets = isShortcutResult ? mMaxShortcutTargetsPerApp
: MAX_CHOOSER_TARGETS_PER_APP;
+ final int targetsLimit = mApplySharingAppLimits ? Math.min(targets.size(), maxTargets)
+ : targets.size();
float lastScore = 0;
boolean shouldNotify = false;
- for (int i = 0, count = Math.min(targets.size(), maxTargets); i < count; i++) {
+ for (int i = 0, count = targetsLimit; i < count; i++) {
final ChooserTarget target = targets.get(i);
float targetScore = target.getScore();
- targetScore *= baseScore;
- if (i > 0 && targetScore >= lastScore) {
- // Apply a decay so that the top app can't crowd out everything else.
- // This incents ChooserTargetServices to define what's truly better.
- targetScore = lastScore * 0.95f;
+ if (mApplySharingAppLimits) {
+ targetScore *= baseScore;
+ if (i > 0 && targetScore >= lastScore) {
+ // Apply a decay so that the top app can't crowd out everything else.
+ // This incents ChooserTargetServices to define what's truly better.
+ targetScore = lastScore * 0.95f;
+ }
}
UserHandle userHandle = getUserHandle();
Context contextAsUser = mContext.createContextAsUser(userHandle, 0 /* flags */);
@@ -561,7 +537,8 @@
Log.d(TAG, " => " + target.toString() + " score=" + targetScore
+ " base=" + target.getScore()
+ " lastScore=" + lastScore
- + " baseScore=" + baseScore);
+ + " baseScore=" + baseScore
+ + " applyAppLimit=" + mApplySharingAppLimits);
}
lastScore = targetScore;
@@ -573,217 +550,13 @@
}
/**
- * Store ChooserTarget ranking scores info wrapped in {@code targets}.
- */
- public void addChooserTargetRankingScore(List<AppTarget> targets) {
- Log.i(TAG, "addChooserTargetRankingScore " + targets.size() + " targets score.");
- for (AppTarget target : targets) {
- if (target.getShortcutInfo() == null) {
- continue;
- }
- ShortcutInfo shortcutInfo = target.getShortcutInfo();
- if (!shortcutInfo.getId().equals(ChooserActivity.CHOOSER_TARGET)
- || shortcutInfo.getActivity() == null) {
- continue;
- }
- ComponentName componentName = shortcutInfo.getActivity();
- if (!mChooserTargetScores.containsKey(componentName)) {
- mChooserTargetScores.put(componentName, new HashMap<>());
- }
- mChooserTargetScores.get(componentName).put(shortcutInfo.getShortLabel().toString(),
- target.getRank());
- }
- mChooserTargetScores.keySet().forEach(key -> rankTargetsWithinComponent(key));
- }
-
- /**
- * Rank chooserTargets of the given {@code componentName} in mParkingDirectShareTargets as per
- * available scores stored in mChooserTargetScores.
- */
- private void rankTargetsWithinComponent(ComponentName componentName) {
- if (!mParkingDirectShareTargets.containsKey(componentName)
- || !mChooserTargetScores.containsKey(componentName)) {
- return;
- }
- Map<String, Integer> scores = mChooserTargetScores.get(componentName);
- Collections.sort(mParkingDirectShareTargets.get(componentName).first, (o1, o2) -> {
- // The score has been normalized between 0 and 2000, the default is 1000.
- int score1 = scores.getOrDefault(
- ChooserUtil.md5(o1.getChooserTarget().getTitle().toString()),
- DEFAULT_DIRECT_SHARE_RANKING_SCORE);
- int score2 = scores.getOrDefault(
- ChooserUtil.md5(o2.getChooserTarget().getTitle().toString()),
- DEFAULT_DIRECT_SHARE_RANKING_SCORE);
- return score2 - score1;
- });
- }
-
- /**
- * Park {@code targets} into memory for the moment to surface them later when view is refreshed.
- * Components pending on ChooserTargetService query are also recorded.
- */
- private void parkTargetIntoMemory(DisplayResolveInfo origTarget, List<ChooserTarget> targets,
- @ChooserActivity.ShareTargetType int targetType,
- Map<ChooserTarget, ShortcutInfo> directShareToShortcutInfos,
- List<ChooserActivity.ChooserTargetServiceConnection>
- pendingChooserTargetServiceConnections) {
- ComponentName origComponentName = origTarget != null ? origTarget.getResolvedComponentName()
- : !targets.isEmpty() ? targets.get(0).getComponentName() : null;
- Log.i(TAG,
- "parkTargetIntoMemory " + origComponentName + ", " + targets.size() + " targets");
- mPendingChooserTargetService = pendingChooserTargetServiceConnections.stream()
- .map(ChooserActivity.ChooserTargetServiceConnection::getComponentName)
- .filter(componentName -> !componentName.equals(origComponentName))
- .collect(Collectors.toSet());
- // Park targets in memory
- if (!targets.isEmpty()) {
- final boolean isShortcutResult =
- (targetType == TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER
- || targetType == TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE);
- Context contextAsUser = mContext.createContextAsUser(getUserHandle(),
- 0 /* flags */);
- List<ChooserTargetInfo> parkingTargetInfos = targets.stream()
- .map(target ->
- new SelectableTargetInfo(
- contextAsUser, origTarget, target, target.getScore(),
- mSelectableTargetInfoCommunicator,
- (isShortcutResult ? directShareToShortcutInfos.get(target)
- : null))
- )
- .collect(Collectors.toList());
- Pair<List<ChooserTargetInfo>, Integer> parkingTargetInfoPair =
- mParkingDirectShareTargets.getOrDefault(origComponentName,
- new Pair<>(new ArrayList<>(), 0));
- for (ChooserTargetInfo target : parkingTargetInfos) {
- if (!checkDuplicateTarget(target, parkingTargetInfoPair.first)
- && !checkDuplicateTarget(target, mServiceTargets)) {
- parkingTargetInfoPair.first.add(target);
- mAvailableServiceTargetsNum++;
- }
- }
- mParkingDirectShareTargets.put(origComponentName, parkingTargetInfoPair);
- rankTargetsWithinComponent(origComponentName);
- if (isShortcutResult) {
- mShortcutComponents.add(origComponentName);
- }
- }
- notifyDataSetChanged();
- }
-
- /**
- * Append targets of top ranked share app into direct share row with quota limit. Remove
- * appended ones from memory.
- */
- private void appendServiceTargetsWithQuota() {
- int maxRankedTargets = mChooserListCommunicator.getMaxRankedTargets();
- List<ComponentName> topComponentNames = getTopComponentNames(maxRankedTargets);
- float totalScore = 0f;
- for (ComponentName component : topComponentNames) {
- if (!mPendingChooserTargetService.contains(component)
- && !mParkingDirectShareTargets.containsKey(component)) {
- continue;
- }
- totalScore += super.getScore(component);
- }
- boolean shouldWaitPendingService = false;
- for (ComponentName component : topComponentNames) {
- if (!mPendingChooserTargetService.contains(component)
- && !mParkingDirectShareTargets.containsKey(component)) {
- continue;
- }
- float score = super.getScore(component);
- int quota = Math.round(maxRankedTargets * score / totalScore);
- if (mPendingChooserTargetService.contains(component) && quota >= 1) {
- shouldWaitPendingService = true;
- }
- if (!mParkingDirectShareTargets.containsKey(component)) {
- continue;
- }
- // Append targets into direct share row as per quota.
- Pair<List<ChooserTargetInfo>, Integer> parkingTargetsItem =
- mParkingDirectShareTargets.get(component);
- List<ChooserTargetInfo> parkingTargets = parkingTargetsItem.first;
- int insertedNum = parkingTargetsItem.second;
- while (insertedNum < quota && !parkingTargets.isEmpty()) {
- if (!checkDuplicateTarget(parkingTargets.get(0), mServiceTargets)) {
- mServiceTargets.add(mValidServiceTargetsNum, parkingTargets.get(0));
- mValidServiceTargetsNum++;
- insertedNum++;
- }
- parkingTargets.remove(0);
- }
- Log.i(TAG, " appendServiceTargetsWithQuota component=" + component
- + " appendNum=" + (insertedNum - parkingTargetsItem.second));
- if (DEBUG) {
- Log.d(TAG, " appendServiceTargetsWithQuota component=" + component
- + " score=" + score
- + " totalScore=" + totalScore
- + " quota=" + quota);
- }
- mParkingDirectShareTargets.put(component, new Pair<>(parkingTargets, insertedNum));
- }
- if (!shouldWaitPendingService) {
- fillAllServiceTargets();
- }
- }
-
- /**
- * Append all remaining targets (parking in memory) into direct share row as per their ranking.
- */
- private void fillAllServiceTargets() {
- if (mParkingDirectShareTargets.isEmpty()) {
- return;
- }
- Log.i(TAG, " fillAllServiceTargets");
- List<ComponentName> topComponentNames = getTopComponentNames(MAX_SERVICE_TARGET_APP);
- // Append all remaining targets of top recommended components into direct share row.
- for (ComponentName component : topComponentNames) {
- if (!mParkingDirectShareTargets.containsKey(component)) {
- continue;
- }
- mParkingDirectShareTargets.get(component).first.stream()
- .filter(target -> !checkDuplicateTarget(target, mServiceTargets))
- .forEach(target -> {
- mServiceTargets.add(mValidServiceTargetsNum, target);
- mValidServiceTargetsNum++;
- });
- mParkingDirectShareTargets.remove(component);
- }
- // Append all remaining shortcuts targets into direct share row.
- mParkingDirectShareTargets.entrySet().stream()
- .filter(entry -> mShortcutComponents.contains(entry.getKey()))
- .map(entry -> entry.getValue())
- .map(pair -> pair.first)
- .forEach(targets -> {
- for (ChooserTargetInfo target : targets) {
- if (!checkDuplicateTarget(target, mServiceTargets)) {
- mServiceTargets.add(mValidServiceTargetsNum, target);
- mValidServiceTargetsNum++;
- }
- }
- });
- mParkingDirectShareTargets.clear();
- }
-
- private boolean checkDuplicateTarget(ChooserTargetInfo target,
- List<ChooserTargetInfo> destination) {
- // Check for duplicates and abort if found
- for (ChooserTargetInfo otherTargetInfo : destination) {
- if (target.isSimilar(otherTargetInfo)) {
- return true;
- }
- }
- return false;
- }
-
- /**
* The return number have to exceed a minimum limit to make direct share area expandable. When
* append direct share targets is enabled, return count of all available targets parking in the
* memory; otherwise, it is shortcuts count which will help reduce the amount of visible
* shuffling due to older-style direct share targets.
*/
int getNumServiceTargetsForExpand() {
- return mAppendDirectShareEnabled ? mAvailableServiceTargetsNum : mNumShortcutResults;
+ return mNumShortcutResults;
}
/**
@@ -801,16 +574,11 @@
if (target == null) {
return CALLER_TARGET_SCORE_BOOST;
}
-
- if (targetType == TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE) {
- return SHORTCUT_TARGET_SCORE_BOOST;
- }
-
float score = super.getScore(target);
- if (targetType == TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER) {
+ if (targetType == TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER
+ || targetType == TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE) {
return score * SHORTCUT_TARGET_SCORE_BOOST;
}
-
return score;
}
@@ -820,9 +588,6 @@
*/
public void completeServiceTargetLoading() {
mServiceTargets.removeIf(o -> o instanceof ChooserActivity.PlaceHolderTargetInfo);
- if (mAppendDirectShareEnabled) {
- fillAllServiceTargets();
- }
if (mServiceTargets.isEmpty()) {
mServiceTargets.add(new ChooserActivity.EmptyTargetInfo());
}
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 52801fa..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";
@@ -415,6 +409,12 @@
public static final String CHOOSER_TARGET_RANKING_ENABLED = "chooser_target_ranking_enabled";
/**
+ * (boolean) Whether dark launch of remote prediction service is enabled.
+ */
+ public static final String DARK_LAUNCH_REMOTE_PREDICTION_SERVICE_ENABLED =
+ "dark_launch_remote_prediction_service_enabled";
+
+ /**
* (boolean) Whether to enable pinch resizing for PIP.
*/
public static final String PIP_PINCH_RESIZE = "pip_pinch_resize";
@@ -473,6 +473,14 @@
*/
public static final String SHARE_USE_SERVICE_TARGETS = "share_use_service_targets";
+ /**
+ * (boolean) If true, SysUI provides guardrails for app usage of Direct Share by enforcing
+ * limits on number of targets per app & adjusting scores for apps providing many targets. If
+ * false, this step is skipped. This should be true unless the ranking provider configured by
+ * [some other flag] is expected to manage these incentives.
+ */
+ public static final String APPLY_SHARING_APP_LIMITS_IN_SYSUI =
+ "apply_sharing_app_limits_in_sysui";
/*
* (long) The duration that the home button must be pressed before triggering Assist
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/view/IInputMethodClient.aidl b/core/java/com/android/internal/view/IInputMethodClient.aidl
index ec9a0a2..49dbbaa 100644
--- a/core/java/com/android/internal/view/IInputMethodClient.aidl
+++ b/core/java/com/android/internal/view/IInputMethodClient.aidl
@@ -28,7 +28,6 @@
void setActive(boolean active, boolean fullscreen, boolean reportToImeController);
void scheduleStartInputIfNecessary(boolean fullscreen);
void reportFullscreenMode(boolean fullscreen);
- void applyImeVisibility(boolean setVisible);
void updateActivityViewToScreenMatrix(int bindSequence, in float[] matrixValues);
void setImeTraceEnabled(boolean enabled);
}
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/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
index b790056..b46b5a2 100644
--- a/core/jni/android_graphics_BLASTBufferQueue.cpp
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -68,7 +68,7 @@
};
static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring jName, jlong surfaceControl,
- jlong width, jlong height, jint format, jboolean enableTripleBuffering) {
+ jlong width, jlong height, jint format) {
String8 str8;
if (jName) {
const jchar* str16 = env->GetStringCritical(jName, nullptr);
@@ -81,7 +81,7 @@
std::string name = str8.string();
sp<BLASTBufferQueue> queue =
new BLASTBufferQueue(name, reinterpret_cast<SurfaceControl*>(surfaceControl), width,
- height, format, enableTripleBuffering);
+ height, format);
queue->incStrong((void*)nativeCreate);
return reinterpret_cast<jlong>(queue.get());
}
@@ -140,7 +140,7 @@
static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
// clang-format off
- {"nativeCreate", "(Ljava/lang/String;JJJIZ)J", (void*)nativeCreate},
+ {"nativeCreate", "(Ljava/lang/String;JJJI)J", (void*)nativeCreate},
{"nativeGetSurface", "(JZ)Landroid/view/Surface;", (void*)nativeGetSurface},
{"nativeDestroy", "(J)V", (void*)nativeDestroy},
{"nativeSetNextTransaction", "(JJ)V", (void*)nativeSetNextTransaction},
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index d4b5c2b..dd62bb1 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -52,6 +52,7 @@
#include <string.h>
#include <sys/epoll.h>
#include <sys/errno.h>
+#include <sys/pidfd.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/syscall.h>
@@ -1316,14 +1317,8 @@
return removeAllProcessGroups();
}
-// Wrapper function to the syscall pidfd_open, which creates a file
-// descriptor that refers to the process whose PID is specified in pid.
-static inline int sys_pidfd_open(pid_t pid, unsigned int flags) {
- return syscall(__NR_pidfd_open, pid, flags);
-}
-
static jint android_os_Process_nativePidFdOpen(JNIEnv* env, jobject, jint pid, jint flags) {
- int fd = sys_pidfd_open(pid, flags);
+ int fd = pidfd_open(pid, flags);
if (fd < 0) {
jniThrowErrnoException(env, "nativePidFdOpen", errno);
return -1;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 45fd96a..8d7f542 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2659,10 +2659,6 @@
<permission android:name="android.permission.CREATE_USERS"
android:protectionLevel="signature" />
- <!-- @TestApi @hide Allows an application to query user info for all users on the device. -->
- <permission android:name="android.permission.QUERY_USERS"
- android:protectionLevel="signature" />
-
<!-- @hide Allows an application to set the profile owners and the device owner.
This permission is not available to third party applications.-->
<permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"
@@ -3447,6 +3443,14 @@
<permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"
android:protectionLevel="signature" />
+ <!-- Allows an application to avoid all toast rate limiting restrictions.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.UNLIMITED_TOASTS"
+ android:protectionLevel="signature" />
+ <uses-permission android:name="android.permission.UNLIMITED_TOASTS" />
+
<!-- @SystemApi Allows an application to use
{@link android.view.WindowManager.LayoutsParams#SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS}
to hide non-system-overlay windows.
@@ -3550,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.
@@ -4484,7 +4488,7 @@
<permission android:name="android.permission.FACTORY_TEST"
android:protectionLevel="signature" />
- <!-- @hide @TestApi Allows an application to broadcast the intent {@link
+ <!-- @hide @TestApi @SystemApi Allows an application to broadcast the intent {@link
android.content.Intent#ACTION_CLOSE_SYSTEM_DIALOGS}.
<p>Not for use by third-party applications.
-->
diff --git a/core/res/remote_color_resources_res/values/colors.xml b/core/res/remote_color_resources_res/values/colors.xml
index 295f16e..e4bcae43 100644
--- a/core/res/remote_color_resources_res/values/colors.xml
+++ b/core/res/remote_color_resources_res/values/colors.xml
@@ -1,40 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Note: the values of the colors doesn't really matter (they will always be overwritten before used), but they help a lot debugging, to find out which color is where in the ARSC file. -->
- <color name="system_primary_0">#01010101</color>
- <color name="system_primary_50">#02020202</color>
- <color name="system_primary_100">#03030303</color>
- <color name="system_primary_200">#04040404</color>
- <color name="system_primary_300">#05050505</color>
- <color name="system_primary_400">#06060606</color>
- <color name="system_primary_500">#07070707</color>
- <color name="system_primary_600">#08080808</color>
- <color name="system_primary_700">#09090909</color>
- <color name="system_primary_800">#0a0a0a0a</color>
- <color name="system_primary_900">#0b0b0b0b</color>
- <color name="system_primary_1000">#0c0c0c0c</color>
- <color name="system_secondary_0">#10101010</color>
- <color name="system_secondary_50">#20202020</color>
- <color name="system_secondary_100">#30303030</color>
- <color name="system_secondary_200">#40404040</color>
- <color name="system_secondary_300">#50505050</color>
- <color name="system_secondary_400">#60606060</color>
- <color name="system_secondary_500">#70707070</color>
- <color name="system_secondary_600">#80808080</color>
- <color name="system_secondary_700">#90909090</color>
- <color name="system_secondary_800">#a0a0a0a0</color>
- <color name="system_secondary_900">#b0b0b0b0</color>
- <color name="system_secondary_1000">#c0c0c0c0</color>
- <color name="system_neutral_0">#1f1f1f1f</color>
- <color name="system_neutral_50">#2f2f2f2f</color>
- <color name="system_neutral_100">#3f3f3f3f</color>
- <color name="system_neutral_200">#4f4f4f4f</color>
- <color name="system_neutral_300">#5f5f5f5f</color>
- <color name="system_neutral_400">#6f6f6f6f</color>
- <color name="system_neutral_500">#7f7f7f7f</color>
- <color name="system_neutral_600">#8f8f8f8f</color>
- <color name="system_neutral_700">#9f9f9f9f</color>
- <color name="system_neutral_800">#afafafaf</color>
- <color name="system_neutral_900">#bfbfbfbf</color>
- <color name="system_neutral_1000">#cfcfcfcf</color>
+ <color name="system_accent1_0">#ffffff</color>
+ <color name="system_accent1_50">#91fff4</color>
+ <color name="system_accent1_100">#83f6e5</color>
+ <color name="system_accent1_200">#65d9c9</color>
+ <color name="system_accent1_300">#45bdae</color>
+ <color name="system_accent1_400">#1fa293</color>
+ <color name="system_accent1_500">#008377</color>
+ <color name="system_accent1_600">#006d61</color>
+ <color name="system_accent1_700">#005449</color>
+ <color name="system_accent1_800">#003c33</color>
+ <color name="system_accent1_900">#00271e</color>
+ <color name="system_accent1_1000">#000000</color>
+ <color name="system_accent2_0">#ffffff</color>
+ <color name="system_accent2_50">#91fff4</color>
+ <color name="system_accent2_100">#83f6e5</color>
+ <color name="system_accent2_200">#65d9c9</color>
+ <color name="system_accent2_300">#45bdae</color>
+ <color name="system_accent2_400">#1fa293</color>
+ <color name="system_accent2_500">#008377</color>
+ <color name="system_accent2_600">#006d61</color>
+ <color name="system_accent2_700">#005449</color>
+ <color name="system_accent2_800">#003c33</color>
+ <color name="system_accent2_900">#00271e</color>
+ <color name="system_accent2_1000">#000000</color>
+ <color name="system_accent3_0">#ffffff</color>
+ <color name="system_accent3_50">#91fff4</color>
+ <color name="system_accent3_100">#83f6e5</color>
+ <color name="system_accent3_200">#65d9c9</color>
+ <color name="system_accent3_300">#45bdae</color>
+ <color name="system_accent3_400">#1fa293</color>
+ <color name="system_accent3_500">#008377</color>
+ <color name="system_accent3_600">#006d61</color>
+ <color name="system_accent3_700">#005449</color>
+ <color name="system_accent3_800">#003c33</color>
+ <color name="system_accent3_900">#00271e</color>
+ <color name="system_accent3_1000">#000000</color>
+ <color name="system_neutral1_0">#ffffff</color>
+ <color name="system_neutral1_50">#f0f0f0</color>
+ <color name="system_neutral1_100">#e2e2e2</color>
+ <color name="system_neutral1_200">#c6c6c6</color>
+ <color name="system_neutral1_300">#ababab</color>
+ <color name="system_neutral1_400">#909090</color>
+ <color name="system_neutral1_500">#757575</color>
+ <color name="system_neutral1_600">#5e5e5e</color>
+ <color name="system_neutral1_700">#464646</color>
+ <color name="system_neutral1_800">#303030</color>
+ <color name="system_neutral1_900">#1b1b1b</color>
+ <color name="system_neutral1_1000">#000000</color>
+ <color name="system_neutral2_0">#ffffff</color>
+ <color name="system_neutral2_50">#f0f0f0</color>
+ <color name="system_neutral2_100">#e2e2e2</color>
+ <color name="system_neutral2_200">#c6c6c6</color>
+ <color name="system_neutral2_300">#ababab</color>
+ <color name="system_neutral2_400">#909090</color>
+ <color name="system_neutral2_500">#757575</color>
+ <color name="system_neutral2_600">#5e5e5e</color>
+ <color name="system_neutral2_700">#464646</color>
+ <color name="system_neutral2_800">#303030</color>
+ <color name="system_neutral2_900">#1b1b1b</color>
+ <color name="system_neutral2_1000">#000000</color>
</resources>
diff --git a/core/res/remote_color_resources_res/values/public.xml b/core/res/remote_color_resources_res/values/public.xml
index e628f09..9616628 100644
--- a/core/res/remote_color_resources_res/values/public.xml
+++ b/core/res/remote_color_resources_res/values/public.xml
@@ -1,41 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<public-group type="color" first-id="0x0106001d">
- <public name="system_primary_0" />
- <public name="system_primary_50" />
- <public name="system_primary_100" />
- <public name="system_primary_200" />
- <public name="system_primary_300" />
- <public name="system_primary_400" />
- <public name="system_primary_500" />
- <public name="system_primary_600" />
- <public name="system_primary_700" />
- <public name="system_primary_800" />
- <public name="system_primary_900" />
- <public name="system_primary_1000" />
- <public name="system_secondary_0" />
- <public name="system_secondary_50" />
- <public name="system_secondary_100" />
- <public name="system_secondary_200" />
- <public name="system_secondary_300" />
- <public name="system_secondary_400" />
- <public name="system_secondary_500" />
- <public name="system_secondary_600" />
- <public name="system_secondary_700" />
- <public name="system_secondary_800" />
- <public name="system_secondary_900" />
- <public name="system_secondary_1000" />
- <public name="system_neutral_0" />
- <public name="system_neutral_50" />
- <public name="system_neutral_100" />
- <public name="system_neutral_200" />
- <public name="system_neutral_300" />
- <public name="system_neutral_400" />
- <public name="system_neutral_500" />
- <public name="system_neutral_600" />
- <public name="system_neutral_700" />
- <public name="system_neutral_800" />
- <public name="system_neutral_900" />
- <public name="system_neutral_1000" />
+ <public name="system_accent1_0" />
+ <public name="system_accent1_50" />
+ <public name="system_accent1_100" />
+ <public name="system_accent1_200" />
+ <public name="system_accent1_300" />
+ <public name="system_accent1_400" />
+ <public name="system_accent1_500" />
+ <public name="system_accent1_600" />
+ <public name="system_accent1_700" />
+ <public name="system_accent1_800" />
+ <public name="system_accent1_900" />
+ <public name="system_accent1_1000" />
+ <public name="system_accent2_0" />
+ <public name="system_accent2_50" />
+ <public name="system_accent2_100" />
+ <public name="system_accent2_200" />
+ <public name="system_accent2_300" />
+ <public name="system_accent2_400" />
+ <public name="system_accent2_500" />
+ <public name="system_accent2_600" />
+ <public name="system_accent2_700" />
+ <public name="system_accent2_800" />
+ <public name="system_accent2_900" />
+ <public name="system_accent2_1000" />
+ <public name="system_accent3_0" />
+ <public name="system_accent3_50" />
+ <public name="system_accent3_100" />
+ <public name="system_accent3_200" />
+ <public name="system_accent3_300" />
+ <public name="system_accent3_400" />
+ <public name="system_accent3_500" />
+ <public name="system_accent3_600" />
+ <public name="system_accent3_700" />
+ <public name="system_accent3_800" />
+ <public name="system_accent3_900" />
+ <public name="system_accent3_1000" />
+ <public name="system_neutral1_0" />
+ <public name="system_neutral1_50" />
+ <public name="system_neutral1_100" />
+ <public name="system_neutral1_200" />
+ <public name="system_neutral1_300" />
+ <public name="system_neutral1_400" />
+ <public name="system_neutral1_500" />
+ <public name="system_neutral1_600" />
+ <public name="system_neutral1_700" />
+ <public name="system_neutral1_800" />
+ <public name="system_neutral1_900" />
+ <public name="system_neutral1_1000" />
+ <public name="system_neutral2_0" />
+ <public name="system_neutral2_50" />
+ <public name="system_neutral2_100" />
+ <public name="system_neutral2_200" />
+ <public name="system_neutral2_300" />
+ <public name="system_neutral2_400" />
+ <public name="system_neutral2_500" />
+ <public name="system_neutral2_600" />
+ <public name="system_neutral2_700" />
+ <public name="system_neutral2_800" />
+ <public name="system_neutral2_900" />
+ <public name="system_neutral2_1000" />
</public-group>
</resources>
diff --git a/core/res/res/color/text_color_primary_device_default_dark.xml b/core/res/res/color/text_color_primary_device_default_dark.xml
index 90d6b07..5926fde 100644
--- a/core/res/res/color/text_color_primary_device_default_dark.xml
+++ b/core/res/res/color/text_color_primary_device_default_dark.xml
@@ -17,7 +17,6 @@
<!-- Please see primary_text_material_dark.xml -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
- android:alpha="?attr/disabledAlpha"
- android:color="@color/system_primary_50"/>
- <item android:color="@color/system_primary_50"/>
+ android:color="@color/system_neutral1_500"/>
+ <item android:color="@color/system_neutral1_50"/>
</selector>
diff --git a/core/res/res/color/text_color_primary_device_default_light.xml b/core/res/res/color/text_color_primary_device_default_light.xml
index bdc4fa9..1379523 100644
--- a/core/res/res/color/text_color_primary_device_default_light.xml
+++ b/core/res/res/color/text_color_primary_device_default_light.xml
@@ -17,7 +17,6 @@
<!-- Please see primary_text_material_light.xml -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
- android:alpha="?attr/disabledAlpha"
- android:color="@color/system_primary_900"/>
- <item android:color="@color/system_primary_900"/>
+ android:color="@color/system_neutral1_400"/>
+ <item android:color="@color/system_neutral1_900"/>
</selector>
diff --git a/core/res/res/color/text_color_secondary_device_default_dark.xml b/core/res/res/color/text_color_secondary_device_default_dark.xml
index 799636ad..f79fd62 100644
--- a/core/res/res/color/text_color_secondary_device_default_dark.xml
+++ b/core/res/res/color/text_color_secondary_device_default_dark.xml
@@ -18,6 +18,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:alpha="?attr/disabledAlpha"
- android:color="@color/system_primary_200"/>
- <item android:color="@color/system_primary_200"/>
+ android:color="@color/system_neutral2_200"/>
+ <item android:color="@color/system_neutral2_200"/>
</selector>
diff --git a/core/res/res/color/text_color_secondary_device_default_light.xml b/core/res/res/color/text_color_secondary_device_default_light.xml
index 4793bb8..c58f565 100644
--- a/core/res/res/color/text_color_secondary_device_default_light.xml
+++ b/core/res/res/color/text_color_secondary_device_default_light.xml
@@ -18,6 +18,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:alpha="?attr/disabledAlpha"
- android:color="@color/system_primary_700"/>
- <item android:color="@color/system_primary_700"/>
+ android:color="@color/system_neutral2_700"/>
+ <item android:color="@color/system_neutral2_700"/>
</selector>
diff --git a/core/res/res/color/text_color_tertiary_device_default_dark.xml b/core/res/res/color/text_color_tertiary_device_default_dark.xml
index c828631..63fdc81 100644
--- a/core/res/res/color/text_color_tertiary_device_default_dark.xml
+++ b/core/res/res/color/text_color_tertiary_device_default_dark.xml
@@ -18,6 +18,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:alpha="?attr/disabledAlpha"
- android:color="@color/system_primary_400"/>
- <item android:color="@color/system_primary_400"/>
+ android:color="@color/system_neutral2_400"/>
+ <item android:color="@color/system_neutral2_400"/>
</selector>
diff --git a/core/res/res/color/text_color_tertiary_device_default_light.xml b/core/res/res/color/text_color_tertiary_device_default_light.xml
index 82c420a..1ad6f6a 100644
--- a/core/res/res/color/text_color_tertiary_device_default_light.xml
+++ b/core/res/res/color/text_color_tertiary_device_default_light.xml
@@ -18,6 +18,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:alpha="?attr/disabledAlpha"
- android:color="@color/system_primary_500"/>
- <item android:color="@color/system_primary_500"/>
+ android:color="@color/system_neutral2_500"/>
+ <item android:color="@color/system_neutral2_500"/>
</selector>
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/colors.xml b/core/res/res/values/colors.xml
index 22467e4..91896fe 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -244,114 +244,188 @@
<color name="conversation_important_highlight">#F9AB00</color>
- <!-- Lightest shade of the primary color used by the system. White.
+ <!-- Lightest shade of the accent color used by the system. White.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_primary_0">#ffffff</color>
- <!-- Shade of the primary system color at 95% lightness.
+ <color name="system_accent1_0">#ffffff</color>
+ <!-- Shade of the accent system color at 95% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_primary_50">#f2f2f2</color>
- <!-- Shade of the primary system color at 90% lightness.
+ <color name="system_accent1_50">#91fff4</color>
+ <!-- Shade of the accent system color at 90% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_primary_100">#e3e3e3</color>
- <!-- Shade of the primary system color at 80% lightness.
+ <color name="system_accent1_100">#83f6e5</color>
+ <!-- Shade of the accent system color at 80% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_primary_200">#c7c7c7</color>
- <!-- Shade of the primary system color at 70% lightness.
+ <color name="system_accent1_200">#65d9c9</color>
+ <!-- Shade of the accent system color at 70% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_primary_300">#ababab</color>
- <!-- Shade of the primary system color at 60% lightness.
+ <color name="system_accent1_300">#45bdae</color>
+ <!-- Shade of the accent system color at 60% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_primary_400">#8f8f8f</color>
- <!-- Shade of the primary system color at 50% lightness.
+ <color name="system_accent1_400">#1fa293</color>
+ <!-- Shade of the accent system color at 49% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_primary_500">#757575</color>
- <!-- Shade of the primary system color at 40% lightness.
+ <color name="system_accent1_500">#008377</color>
+ <!-- Shade of the accent system color at 40% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_primary_600">#5e5e5e</color>
- <!-- Shade of the primary system color at 30% lightness.
+ <color name="system_accent1_600">#006d61</color>
+ <!-- Shade of the accent system color at 30% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_primary_700">#474747</color>
- <!-- Shade of the primary system color at 20% lightness.
+ <color name="system_accent1_700">#005449</color>
+ <!-- Shade of the accent system color at 20% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_primary_800">#303030</color>
- <!-- Shade of the primary system color at 10% lightness.
+ <color name="system_accent1_800">#003c33</color>
+ <!-- Shade of the accent system color at 10% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_primary_900">#1f1f1f</color>
- <!-- Darkest shade of the primary color used by the system. Black.
+ <color name="system_accent1_900">#00271e</color>
+ <!-- Darkest shade of the accent color used by the system. Black.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_primary_1000">#000000</color>
+ <color name="system_accent1_1000">#000000</color>
- <!-- Lightest shade of the secondary color used by the system. White.
+ <!-- Lightest shade of the secondary accent color used by the system. White.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_secondary_0">#ffffff</color>
- <!-- Shade of the secondary system color at 95% lightness.
+ <color name="system_accent2_0">#ffffff</color>
+ <!-- Shade of the secondary accent system color at 95% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_secondary_50">#91fff4</color>
- <!-- Shade of the secondary system color at 90% lightness.
+ <color name="system_accent2_50">#91fff4</color>
+ <!-- Shade of the secondary accent system color at 90% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_secondary_100">#83f6e5</color>
- <!-- Shade of the secondary system color at 80% lightness.
+ <color name="system_accent2_100">#83f6e5</color>
+ <!-- Shade of the secondary accent system color at 80% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_secondary_200">#65d9c9</color>
- <!-- Shade of the secondary system color at 70% lightness.
+ <color name="system_accent2_200">#65d9c9</color>
+ <!-- Shade of the secondary accent system color at 70% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_secondary_300">#45bdae</color>
- <!-- Shade of the secondary system color at 60% lightness.
+ <color name="system_accent2_300">#45bdae</color>
+ <!-- Shade of the secondary accent system color at 60% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_secondary_400">#1fa293</color>
- <!-- Shade of the secondary system color at 50% lightness.
+ <color name="system_accent2_400">#1fa293</color>
+ <!-- Shade of the secondary accent system color at 49% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_secondary_500">#008377</color>
- <!-- Shade of the secondary system color at 40% lightness.
+ <color name="system_accent2_500">#008377</color>
+ <!-- Shade of the secondary accent system color at 40% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_secondary_600">#006d61</color>
- <!-- Shade of the secondary system color at 30% lightness.
+ <color name="system_accent2_600">#006d61</color>
+ <!-- Shade of the secondary accent system color at 30% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_secondary_700">#005449</color>
- <!-- Shade of the secondary system color at 20% lightness.
+ <color name="system_accent2_700">#005449</color>
+ <!-- Shade of the secondary accent system color at 20% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_secondary_800">#003c33</color>
- <!-- Shade of the secondary system color at 10% lightness.
+ <color name="system_accent2_800">#003c33</color>
+ <!-- Shade of the secondary accent system color at 10% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_secondary_900">#00271e</color>
- <!-- Darkest shade of the secondary color used by the system. Black.
+ <color name="system_accent2_900">#00271e</color>
+ <!-- Darkest shade of the secondary accent color used by the system. Black.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_secondary_1000">#000000</color>
+ <color name="system_accent2_1000">#000000</color>
+
+ <!-- Lightest shade of the tertiary accent color used by the system. White.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_accent3_0">#ffffff</color>
+ <!-- Shade of the tertiary accent system color at 95% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_accent3_50">#91fff4</color>
+ <!-- Shade of the tertiary accent system color at 90% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_accent3_100">#83f6e5</color>
+ <!-- Shade of the tertiary accent system color at 80% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_accent3_200">#65d9c9</color>
+ <!-- Shade of the tertiary accent system color at 70% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_accent3_300">#45bdae</color>
+ <!-- Shade of the tertiary accent system color at 60% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_accent3_400">#1fa293</color>
+ <!-- Shade of the tertiary accent system color at 49% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_accent3_500">#008377</color>
+ <!-- Shade of the tertiary accent system color at 40% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_accent3_600">#006d61</color>
+ <!-- Shade of the tertiary accent system color at 30% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_accent3_700">#005449</color>
+ <!-- Shade of the tertiary accent system color at 20% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_accent3_800">#003c33</color>
+ <!-- Shade of the tertiary accent system color at 10% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_accent3_900">#00271e</color>
+ <!-- Darkest shade of the tertiary accent color used by the system. Black.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_accent3_1000">#000000</color>
<!-- Lightest shade of the neutral color used by the system. White.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral_0">#ffffff</color>
+ <color name="system_neutral1_0">#ffffff</color>
<!-- Shade of the neutral system color at 95% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral_50">#f0f0f0</color>
+ <color name="system_neutral1_50">#f0f0f0</color>
<!-- Shade of the neutral system color at 90% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral_100">#e2e2e2</color>
+ <color name="system_neutral1_100">#e2e2e2</color>
<!-- Shade of the neutral system color at 80% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral_200">#c6c6c6</color>
+ <color name="system_neutral1_200">#c6c6c6</color>
<!-- Shade of the neutral system color at 70% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral_300">#ababab</color>
+ <color name="system_neutral1_300">#ababab</color>
<!-- Shade of the neutral system color at 60% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral_400">#909090</color>
- <!-- Shade of the neutral system color at 50% lightness.
+ <color name="system_neutral1_400">#909090</color>
+ <!-- Shade of the neutral system color at 49% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral_500">#757575</color>
+ <color name="system_neutral1_500">#757575</color>
<!-- Shade of the neutral system color at 40% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral_600">#5e5e5e</color>
+ <color name="system_neutral1_600">#5e5e5e</color>
<!-- Shade of the neutral system color at 30% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral_700">#464646</color>
+ <color name="system_neutral1_700">#464646</color>
<!-- Shade of the neutral system color at 20% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral_800">#303030</color>
+ <color name="system_neutral1_800">#303030</color>
<!-- Shade of the neutral system color at 10% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral_900">#1b1b1b</color>
+ <color name="system_neutral1_900">#1b1b1b</color>
<!-- Darkest shade of the neutral color used by the system. Black.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral_1000">#000000</color>
+ <color name="system_neutral1_1000">#000000</color>
+
+ <!-- Lightest shade of the secondary neutral color used by the system. White.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_neutral2_0">#ffffff</color>
+ <!-- Shade of the secondary neutral system color at 95% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_neutral2_50">#f0f0f0</color>
+ <!-- Shade of the secondary neutral system color at 90% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_neutral2_100">#e2e2e2</color>
+ <!-- Shade of the secondary neutral system color at 80% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_neutral2_200">#c6c6c6</color>
+ <!-- Shade of the secondary neutral system color at 70% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_neutral2_300">#ababab</color>
+ <!-- Shade of the secondary neutral system color at 60% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_neutral2_400">#909090</color>
+ <!-- Shade of the secondary neutral system color at 49% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_neutral2_500">#757575</color>
+ <!-- Shade of the secondary neutral system color at 40% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_neutral2_600">#5e5e5e</color>
+ <!-- Shade of the secondary neutral system color at 30% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_neutral2_700">#464646</color>
+ <!-- Shade of the secondary neutral system color at 20% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_neutral2_800">#303030</color>
+ <!-- Shade of the secondary neutral system color at 10% lightness.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_neutral2_900">#1b1b1b</color>
+ <!-- Darkest shade of the secondary neutral color used by the system. Black.
+ This value can be overlaid at runtime by OverlayManager RROs. -->
+ <color name="system_neutral2_1000">#000000</color>
</resources>
diff --git a/core/res/res/values/colors_device_defaults.xml b/core/res/res/values/colors_device_defaults.xml
index 9b56321..3fbd7ca 100644
--- a/core/res/res/values/colors_device_defaults.xml
+++ b/core/res/res/values/colors_device_defaults.xml
@@ -17,9 +17,9 @@
<!-- Colors specific to DeviceDefault themes. These are mostly pass-throughs to enable
overlaying new theme colors. -->
<resources>
- <color name="primary_device_default_dark">@color/system_primary_800</color>
- <color name="primary_device_default_light">@color/system_primary_50</color>
- <color name="primary_device_default_settings">@color/system_primary_800</color>
+ <color name="primary_device_default_dark">@color/system_neutral1_800</color>
+ <color name="primary_device_default_light">@color/system_neutral1_50</color>
+ <color name="primary_device_default_settings">@color/system_neutral1_800</color>
<color name="primary_device_default_settings_light">@color/primary_device_default_light</color>
<color name="primary_dark_device_default_dark">@color/primary_device_default_dark</color>
<color name="primary_dark_device_default_light">@color/primary_device_default_light</color>
@@ -33,14 +33,14 @@
<color name="tertiary_device_default_settings">@color/tertiary_material_settings</color>
<color name="quaternary_device_default_settings">@color/quaternary_material_settings</color>
- <color name="accent_device_default_light">@color/system_secondary_600</color>
- <color name="accent_device_default_dark">@color/system_secondary_200</color>
+ <color name="accent_device_default_light">@color/system_accent1_600</color>
+ <color name="accent_device_default_dark">@color/system_accent1_200</color>
<color name="accent_device_default">@color/accent_device_default_light</color>
- <color name="background_device_default_dark">@color/system_primary_800</color>
- <color name="background_device_default_light">@color/system_primary_50</color>
- <color name="background_floating_device_default_dark">@color/system_primary_900</color>
- <color name="background_floating_device_default_light">@color/system_primary_100</color>
+ <color name="background_device_default_dark">@color/system_neutral1_800</color>
+ <color name="background_device_default_light">@color/system_neutral1_50</color>
+ <color name="background_floating_device_default_dark">@color/system_neutral1_900</color>
+ <color name="background_floating_device_default_light">@color/system_neutral1_100</color>
<!-- Please refer to text_color_[primary]_device_default_[light].xml for text colors-->
<color name="foreground_device_default_light">@color/text_color_primary_device_default_light</color>
@@ -50,8 +50,8 @@
<color name="error_color_device_default_dark">@color/error_color_material_dark</color>
<color name="error_color_device_default_light">@color/error_color_material_light</color>
- <color name="list_divider_color_light">@color/system_primary_500</color>
- <color name="list_divider_color_dark">@color/system_primary_400</color>
+ <color name="list_divider_color_light">@color/system_neutral1_200</color>
+ <color name="list_divider_color_dark">@color/system_neutral1_700</color>
<color name="list_divider_opacity_device_default_light">@android:color/white</color>
<color name="list_divider_opacity_device_default_dark">@android:color/white</color>
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/public.xml b/core/res/res/values/public.xml
index 3e5fad8..7694faf 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3102,45 +3102,68 @@
<!-- color definitions go here -->
<!-- Material design dynamic system palette:-->
- <!-- Primary color -->
- <public name="system_primary_0" />
- <public name="system_primary_50" />
- <public name="system_primary_100" />
- <public name="system_primary_200" />
- <public name="system_primary_300" />
- <public name="system_primary_400" />
- <public name="system_primary_500" />
- <public name="system_primary_600" />
- <public name="system_primary_700" />
- <public name="system_primary_800" />
- <public name="system_primary_900" />
- <public name="system_primary_1000" />
- <!-- Secondary color -->
- <public name="system_secondary_0" />
- <public name="system_secondary_50" />
- <public name="system_secondary_100" />
- <public name="system_secondary_200" />
- <public name="system_secondary_300" />
- <public name="system_secondary_400" />
- <public name="system_secondary_500" />
- <public name="system_secondary_600" />
- <public name="system_secondary_700" />
- <public name="system_secondary_800" />
- <public name="system_secondary_900" />
- <public name="system_secondary_1000" />
- <!-- Neutral color -->
- <public name="system_neutral_0" />
- <public name="system_neutral_50" />
- <public name="system_neutral_100" />
- <public name="system_neutral_200" />
- <public name="system_neutral_300" />
- <public name="system_neutral_400" />
- <public name="system_neutral_500" />
- <public name="system_neutral_600" />
- <public name="system_neutral_700" />
- <public name="system_neutral_800" />
- <public name="system_neutral_900" />
- <public name="system_neutral_1000" />
+ <!-- Neutral colors for background and text -->
+ <public name="system_neutral1_0" />
+ <public name="system_neutral1_50" />
+ <public name="system_neutral1_100" />
+ <public name="system_neutral1_200" />
+ <public name="system_neutral1_300" />
+ <public name="system_neutral1_400" />
+ <public name="system_neutral1_500" />
+ <public name="system_neutral1_600" />
+ <public name="system_neutral1_700" />
+ <public name="system_neutral1_800" />
+ <public name="system_neutral1_900" />
+ <public name="system_neutral1_1000" />
+ <public name="system_neutral2_0" />
+ <public name="system_neutral2_50" />
+ <public name="system_neutral2_100" />
+ <public name="system_neutral2_200" />
+ <public name="system_neutral2_300" />
+ <public name="system_neutral2_400" />
+ <public name="system_neutral2_500" />
+ <public name="system_neutral2_600" />
+ <public name="system_neutral2_700" />
+ <public name="system_neutral2_800" />
+ <public name="system_neutral2_900" />
+ <public name="system_neutral2_1000" />
+ <!-- Accent colors, for buttons and UI decorations -->
+ <public name="system_accent1_0" />
+ <public name="system_accent1_50" />
+ <public name="system_accent1_100" />
+ <public name="system_accent1_200" />
+ <public name="system_accent1_300" />
+ <public name="system_accent1_400" />
+ <public name="system_accent1_500" />
+ <public name="system_accent1_600" />
+ <public name="system_accent1_700" />
+ <public name="system_accent1_800" />
+ <public name="system_accent1_900" />
+ <public name="system_accent1_1000" />
+ <public name="system_accent2_0" />
+ <public name="system_accent2_50" />
+ <public name="system_accent2_100" />
+ <public name="system_accent2_200" />
+ <public name="system_accent2_300" />
+ <public name="system_accent2_400" />
+ <public name="system_accent2_500" />
+ <public name="system_accent2_600" />
+ <public name="system_accent2_700" />
+ <public name="system_accent2_800" />
+ <public name="system_accent2_900" />
+ <public name="system_accent2_1000" />
+ <public name="system_accent3_0" />
+ <public name="system_accent3_50" />
+ <public name="system_accent3_100" />
+ <public name="system_accent3_200" />
+ <public name="system_accent3_300" />
+ <public name="system_accent3_400" />
+ <public name="system_accent3_500" />
+ <public name="system_accent3_600" />
+ <public name="system_accent3_700" />
+ <public name="system_accent3_800" />
+ <public name="system_accent3_900" />
+ <public name="system_accent3_1000" />
</public-group>
<public-group type="dimen" first-id="0x01050008">
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/content/ComponentCallbacksControllerTest.java b/core/tests/coretests/src/android/content/ComponentCallbacksControllerTest.java
new file mode 100644
index 0000000..09985a8
--- /dev/null
+++ b/core/tests/coretests/src/android/content/ComponentCallbacksControllerTest.java
@@ -0,0 +1,140 @@
+/*
+ * 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.content;
+
+import static android.content.ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.WindowConfiguration;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+
+import androidx.annotation.NonNull;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:ComponentCallbacksControllerTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ComponentCallbacksControllerTest {
+ private ComponentCallbacksController mController;
+
+ @Before
+ public void setUp() {
+ mController = new ComponentCallbacksController();
+ }
+
+ @Test
+ public void testUnregisterCallbackWithoutRegistrationNoCrash() {
+ mController.unregisterCallbacks(new FakeComponentCallbacks());
+ }
+
+ @Test
+ public void testDispatchWithEmptyCallbacksNoCrash() {
+ mController.dispatchConfigurationChanged(new Configuration());
+ mController.dispatchLowMemory();
+ mController.dispatchTrimMemory(TRIM_MEMORY_BACKGROUND);
+ }
+
+ @Test
+ public void testClearCallbacksNoCrash() {
+ mController.clearCallbacks();
+ }
+
+ @Test
+ public void testDispatchTrimMemoryWithoutComponentCallbacks2NoCrash() {
+ // Register a ComponentCallbacks instead of ComponentCallbacks2
+ mController.registerCallbacks(new FakeComponentCallbacks());
+
+ mController.dispatchTrimMemory(TRIM_MEMORY_BACKGROUND);
+ }
+
+ @Test
+ public void testDispatchConfigurationChanged() throws Exception {
+ final TestComponentCallbacks2 callback = new TestComponentCallbacks2();
+ mController.registerCallbacks(callback);
+
+ final Configuration config = new Configuration();
+ config.windowConfiguration.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
+ config.windowConfiguration.setBounds(new Rect(0, 0, 100, 100));
+
+ mController.dispatchConfigurationChanged(config);
+
+ assertThat(callback.mConfiguration).isEqualTo(config);
+
+ mController.dispatchConfigurationChanged(Configuration.EMPTY);
+
+ assertThat(callback.mConfiguration).isEqualTo(Configuration.EMPTY);
+ }
+
+ @Test
+ public void testDispatchLowMemory() {
+ final TestComponentCallbacks2 callback = new TestComponentCallbacks2();
+ mController.registerCallbacks(callback);
+
+ mController.dispatchLowMemory();
+
+ assertThat(callback.mLowMemoryCalled).isTrue();
+ }
+
+ @Test
+ public void testDispatchTrimMemory() {
+ final TestComponentCallbacks2 callback = new TestComponentCallbacks2();
+ mController.registerCallbacks(callback);
+
+ mController.dispatchTrimMemory(TRIM_MEMORY_BACKGROUND);
+
+ assertThat(callback.mLevel).isEqualTo(TRIM_MEMORY_BACKGROUND);
+ }
+
+ private static class FakeComponentCallbacks implements ComponentCallbacks {
+ @Override
+ public void onConfigurationChanged(@NonNull Configuration newConfig) {}
+
+ @Override
+ public void onLowMemory() {}
+ }
+
+ private static class TestComponentCallbacks2 implements ComponentCallbacks2 {
+ private Configuration mConfiguration;
+ private boolean mLowMemoryCalled;
+ private int mLevel;
+
+ @Override
+ public void onConfigurationChanged(@NonNull Configuration newConfig) {
+ mConfiguration = newConfig;
+ }
+
+ @Override
+ public void onLowMemory() {
+ mLowMemoryCalled = true;
+ }
+
+ @Override
+ public void onTrimMemory(int level) {
+ mLevel = level;
+ }
+ }
+}
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/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
index 5de55d7..47556c3 100644
--- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
@@ -98,12 +98,12 @@
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
// test if setVisibility can show IME
mImeConsumer.onWindowFocusGained();
- mImeConsumer.applyImeVisibility(true);
+ mController.show(WindowInsets.Type.ime(), true /* fromIme */);
mController.cancelExistingAnimations();
assertTrue(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
// test if setVisibility can hide IME
- mImeConsumer.applyImeVisibility(false);
+ mController.hide(WindowInsets.Type.ime(), true /* fromIme */);
mController.cancelExistingAnimations();
assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
});
@@ -117,7 +117,7 @@
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
// Request IME visible before control is available.
mImeConsumer.onWindowFocusGained();
- mImeConsumer.applyImeVisibility(true /* setVisible */);
+ mController.show(WindowInsets.Type.ime(), true /* fromIme */);
// set control and verify visibility is applied.
InsetsSourceControl control =
@@ -136,7 +136,7 @@
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
// Request IME visible before control is available.
mImeConsumer.onWindowFocusGained();
- mImeConsumer.applyImeVisibility(true /* setVisible */);
+ mController.show(WindowInsets.Type.ime(), true /* fromIme */);
// set control and verify visibility is applied.
InsetsSourceControl control = Mockito.spy(
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index ff505c4..4390546 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -236,7 +236,7 @@
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mController.getSourceConsumer(ITYPE_IME).onWindowFocusGained();
// since there is no focused view, forcefully make IME visible.
- mController.applyImeVisibility(true /* setVisible */);
+ mController.show(Type.ime(), true /* fromIme */);
mController.show(Type.all());
// quickly jump to final state by cancelling it.
mController.cancelExistingAnimations();
@@ -244,7 +244,7 @@
assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
assertTrue(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
- mController.applyImeVisibility(false /* setVisible */);
+ mController.hide(Type.ime(), true /* fromIme */);
mController.hide(Type.all());
mController.cancelExistingAnimations();
assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
@@ -261,10 +261,10 @@
mController.onControlsChanged(new InsetsSourceControl[] { ime });
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mController.getSourceConsumer(ITYPE_IME).onWindowFocusGained();
- mController.applyImeVisibility(true);
+ mController.show(Type.ime(), true /* fromIme */);
mController.cancelExistingAnimations();
assertTrue(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
- mController.applyImeVisibility(false);
+ mController.hide(Type.ime(), true /* fromIme */);
mController.cancelExistingAnimations();
assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
mController.getSourceConsumer(ITYPE_IME).onWindowFocusLost();
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index d9012f64..4c58ad3 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -154,8 +154,8 @@
sOverrides.reset();
sOverrides.createPackageManager = mPackageManagerOverride;
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.APPEND_DIRECT_SHARE_ENABLED,
- Boolean.toString(false),
+ SystemUiDeviceConfigFlags.APPLY_SHARING_APP_LIMITS_IN_SYSUI,
+ Boolean.toString(true),
true /* makeDefault*/);
}
@@ -1017,7 +1017,7 @@
assertThat(adapter.getBaseScore(testDri, TARGET_TYPE_DEFAULT), is(testBaseScore));
assertThat(adapter.getBaseScore(testDri, TARGET_TYPE_CHOOSER_TARGET), is(testBaseScore));
assertThat(adapter.getBaseScore(testDri, TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE),
- is(SHORTCUT_TARGET_SCORE_BOOST));
+ is(testBaseScore * SHORTCUT_TARGET_SCORE_BOOST));
assertThat(adapter.getBaseScore(testDri, TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER),
is(testBaseScore * SHORTCUT_TARGET_SCORE_BOOST));
}
@@ -1262,6 +1262,126 @@
.getAllValues().get(2).getTaggedData(MetricsEvent.FIELD_RANKED_POSITION), is(0));
}
+ @Test
+ public void testShortcutTargetWithApplyAppLimits() throws InterruptedException {
+ // Set up resources
+ sOverrides.resources = Mockito.spy(
+ InstrumentationRegistry.getInstrumentation().getContext().getResources());
+ when(sOverrides.resources.getInteger(R.integer.config_maxShortcutTargetsPerApp))
+ .thenReturn(1);
+ Intent sendIntent = createSendTextIntent();
+ // We need app targets for direct targets to get displayed
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ // Create direct share target
+ List<ChooserTarget> serviceTargets = createDirectShareTargets(2,
+ resolvedComponentInfos.get(0).getResolveInfoAt(0).activityInfo.packageName);
+ ResolveInfo ri = ResolverDataProvider.createResolveInfo(3, 0);
+
+ // Start activity
+ final ChooserWrapperActivity activity = mActivityRule
+ .launchActivity(Intent.createChooser(sendIntent, null));
+
+ // Insert the direct share target
+ Map<ChooserTarget, ShortcutInfo> directShareToShortcutInfos = new HashMap<>();
+ List<ShareShortcutInfo> shortcutInfos = createShortcuts(activity);
+ directShareToShortcutInfos.put(serviceTargets.get(0),
+ shortcutInfos.get(0).getShortcutInfo());
+ directShareToShortcutInfos.put(serviceTargets.get(1),
+ shortcutInfos.get(1).getShortcutInfo());
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(
+ () -> activity.getAdapter().addServiceResults(
+ activity.createTestDisplayResolveInfo(sendIntent,
+ ri,
+ "testLabel",
+ "testInfo",
+ sendIntent,
+ /* resolveInfoPresentationGetter */ null),
+ serviceTargets,
+ TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE,
+ directShareToShortcutInfos,
+ List.of())
+ );
+ // Thread.sleep shouldn't be a thing in an integration test but it's
+ // necessary here because of the way the code is structured
+ // TODO: restructure the tests b/129870719
+ Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
+
+ assertThat("Chooser should have 3 targets (2 apps, 1 direct)",
+ activity.getAdapter().getCount(), is(3));
+ assertThat("Chooser should have exactly one selectable direct target",
+ activity.getAdapter().getSelectableServiceTargetCount(), is(1));
+ assertThat("The resolver info must match the resolver info used to create the target",
+ activity.getAdapter().getItem(0).getResolveInfo(), is(ri));
+ assertThat("The display label must match",
+ activity.getAdapter().getItem(0).getDisplayLabel(), is("testTitle0"));
+ }
+
+ @Test
+ public void testShortcutTargetWithoutApplyAppLimits() throws InterruptedException {
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.APPLY_SHARING_APP_LIMITS_IN_SYSUI,
+ Boolean.toString(false),
+ true /* makeDefault*/);
+ // Set up resources
+ sOverrides.resources = Mockito.spy(
+ InstrumentationRegistry.getInstrumentation().getContext().getResources());
+ when(sOverrides.resources.getInteger(R.integer.config_maxShortcutTargetsPerApp))
+ .thenReturn(1);
+ Intent sendIntent = createSendTextIntent();
+ // We need app targets for direct targets to get displayed
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ // Create direct share target
+ List<ChooserTarget> serviceTargets = createDirectShareTargets(2,
+ resolvedComponentInfos.get(0).getResolveInfoAt(0).activityInfo.packageName);
+ ResolveInfo ri = ResolverDataProvider.createResolveInfo(3, 0);
+
+ // Start activity
+ final ChooserWrapperActivity activity = mActivityRule
+ .launchActivity(Intent.createChooser(sendIntent, null));
+
+ // Insert the direct share target
+ Map<ChooserTarget, ShortcutInfo> directShareToShortcutInfos = new HashMap<>();
+ List<ShareShortcutInfo> shortcutInfos = createShortcuts(activity);
+ directShareToShortcutInfos.put(serviceTargets.get(0),
+ shortcutInfos.get(0).getShortcutInfo());
+ directShareToShortcutInfos.put(serviceTargets.get(1),
+ shortcutInfos.get(1).getShortcutInfo());
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(
+ () -> activity.getAdapter().addServiceResults(
+ activity.createTestDisplayResolveInfo(sendIntent,
+ ri,
+ "testLabel",
+ "testInfo",
+ sendIntent,
+ /* resolveInfoPresentationGetter */ null),
+ serviceTargets,
+ TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE,
+ directShareToShortcutInfos,
+ List.of())
+ );
+ // Thread.sleep shouldn't be a thing in an integration test but it's
+ // necessary here because of the way the code is structured
+ // TODO: restructure the tests b/129870719
+ Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
+
+ assertThat("Chooser should have 4 targets (2 apps, 2 direct)",
+ activity.getAdapter().getCount(), is(4));
+ assertThat("Chooser should have exactly two selectable direct target",
+ activity.getAdapter().getSelectableServiceTargetCount(), is(2));
+ assertThat("The resolver info must match the resolver info used to create the target",
+ activity.getAdapter().getItem(0).getResolveInfo(), is(ri));
+ assertThat("The display label must match",
+ activity.getAdapter().getItem(0).getDisplayLabel(), is("testTitle0"));
+ assertThat("The display label must match",
+ activity.getAdapter().getItem(1).getDisplayLabel(), is("testTitle1"));
+ }
+
// This test is too long and too slow and should not be taken as an example for future tests.
@Test
public void testDirectTargetLoggingWithAppTargetNotRankedPortrait()
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/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
index 3aaf11c..4534d36 100644
--- a/graphics/java/android/graphics/BLASTBufferQueue.java
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -28,7 +28,7 @@
public long mNativeObject; // BLASTBufferQueue*
private static native long nativeCreate(String name, long surfaceControl, long width,
- long height, int format, boolean tripleBufferingEnabled);
+ long height, int format);
private static native void nativeDestroy(long ptr);
private static native Surface nativeGetSurface(long ptr, boolean includeSurfaceControlHandle);
private static native void nativeSetNextTransaction(long ptr, long transactionPtr);
@@ -53,9 +53,8 @@
/** Create a new connection with the surface flinger. */
public BLASTBufferQueue(String name, SurfaceControl sc, int width, int height,
- @PixelFormat.Format int format, boolean tripleBufferingEnabled) {
- mNativeObject = nativeCreate(name, sc.mNativeObject, width, height, format,
- tripleBufferingEnabled);
+ @PixelFormat.Format int format) {
+ mNativeObject = nativeCreate(name, sc.mNativeObject, width, height, format);
}
public void destroy() {
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/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index b71bb07..1455269 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -120,12 +120,6 @@
int imgHeight = image->height();
sk_sp<GrDirectContext> grContext = sk_ref_sp(mRenderThread.getGrContext());
- if (bitmap->colorType() == kRGBA_F16_SkColorType &&
- !grContext->colorTypeSupportedAsSurface(bitmap->colorType())) {
- ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported");
- return CopyResult::DestinationInvalid;
- }
-
CopyResult copyResult = CopyResult::UnknownError;
int displayedWidth = imgWidth, displayedHeight = imgHeight;
@@ -159,12 +153,10 @@
bool Readback::copyLayerInto(Layer* layer, const SkRect* srcRect, const SkRect* dstRect,
SkBitmap* bitmap) {
- /* This intermediate surface is present to work around a bug in SwiftShader that
- * prevents us from reading the contents of the layer's texture directly. The
- * workaround involves first rendering that texture into an intermediate buffer and
- * then reading from the intermediate buffer into the bitmap.
- * Another reason to render in an offscreen buffer is to scale and to avoid an issue b/62262733
- * with reading incorrect data from EGLImage backed SkImage (likely a driver bug).
+ /* This intermediate surface is present to work around limitations that LayerDrawable expects
+ * to render into a GPU backed canvas. Additionally, the offscreen buffer solution works around
+ * a scaling issue (b/62262733) that was encountered when sampling from an EGLImage into a
+ * software buffer.
*/
sk_sp<SkSurface> tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(),
SkBudgeted::kYes, bitmap->info(), 0,
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/AudioProfile.java b/media/java/android/media/AudioProfile.java
index 3cd615b..9774e80 100644
--- a/media/java/android/media/AudioProfile.java
+++ b/media/java/android/media/AudioProfile.java
@@ -18,6 +18,9 @@
import android.annotation.NonNull;
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
/**
* An AudioProfile is specific to an audio format and lists supported sampling rates and
* channel masks for that format. An {@link AudioDeviceInfo} has a list of supported AudioProfiles.
@@ -63,4 +66,29 @@
public @NonNull int[] getSampleRates() {
return mSamplingRates;
}
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("{");
+ sb.append(AudioFormat.toLogFriendlyEncoding(mFormat));
+ if (mSamplingRates != null && mSamplingRates.length > 0) {
+ sb.append(", sampling rates=").append(Arrays.toString(mSamplingRates));
+ }
+ if (mChannelMasks != null && mChannelMasks.length > 0) {
+ sb.append(", channel masks=").append(toHexString(mChannelMasks));
+ }
+ if (mChannelIndexMasks != null && mChannelIndexMasks.length > 0) {
+ sb.append(", channel index masks=").append(Arrays.toString(mChannelIndexMasks));
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+
+ private static String toHexString(int[] ints) {
+ if (ints == null || ints.length == 0) {
+ return "";
+ }
+ return Arrays.stream(ints).mapToObj(anInt -> String.format("0x%02X, ", anInt))
+ .collect(Collectors.joining());
+ }
}
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/media/java/android/media/metrics/Event.java b/media/java/android/media/metrics/Event.java
index 96b61d2..17218f0 100644
--- a/media/java/android/media/metrics/Event.java
+++ b/media/java/android/media/metrics/Event.java
@@ -17,6 +17,7 @@
package android.media.metrics;
import android.annotation.IntRange;
+import android.annotation.NonNull;
import android.os.Bundle;
/**
@@ -24,7 +25,7 @@
*/
public abstract class Event {
final long mTimeSinceCreatedMillis;
- Bundle mExtras;
+ Bundle mMetricsBundle = new Bundle();
// hide default constructor
/* package */ Event() {
@@ -38,7 +39,7 @@
/* package */ Event(long timeSinceCreatedMillis, Bundle extras) {
mTimeSinceCreatedMillis = timeSinceCreatedMillis;
- mExtras = extras;
+ mMetricsBundle = extras;
}
/**
@@ -50,8 +51,12 @@
return mTimeSinceCreatedMillis;
}
- /** @hide */
- public Bundle getExtras() {
- return mExtras;
+ /**
+ * Gets metrics-related information that is not supported by dedicated methods.
+ * <p>It is intended to be used for backwards compatibility by the metrics infrastructure.
+ */
+ @NonNull
+ public Bundle getMetricsBundle() {
+ return mMetricsBundle;
}
}
diff --git a/media/java/android/media/metrics/LogSessionId.java b/media/java/android/media/metrics/LogSessionId.java
index 7ddb259..f68ef4b 100644
--- a/media/java/android/media/metrics/LogSessionId.java
+++ b/media/java/android/media/metrics/LogSessionId.java
@@ -16,19 +16,28 @@
package android.media.metrics;
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+
/**
* An instances of this class represents the ID of a log session.
- * @hide
*/
-public class LogSessionId {
+public final class LogSessionId {
private final String mSessionId;
- /* package */ LogSessionId(String id) {
+ /* package */ LogSessionId(@NonNull String id) {
mSessionId = id;
}
/** @hide */
+ @TestApi
+ @NonNull
public String getStringId() {
return mSessionId;
}
+
+ @Override
+ public String toString() {
+ return mSessionId;
+ }
}
diff --git a/media/java/android/media/metrics/MediaMetricsManager.java b/media/java/android/media/metrics/MediaMetricsManager.java
index 9710e88..b4a74a3 100644
--- a/media/java/android/media/metrics/MediaMetricsManager.java
+++ b/media/java/android/media/metrics/MediaMetricsManager.java
@@ -104,7 +104,6 @@
/**
* Creates a recording session.
- * @hide
*/
@NonNull
public RecordingSession createRecordingSession() {
diff --git a/media/java/android/media/metrics/NetworkEvent.java b/media/java/android/media/metrics/NetworkEvent.java
index 18660682..0e80543 100644
--- a/media/java/android/media/metrics/NetworkEvent.java
+++ b/media/java/android/media/metrics/NetworkEvent.java
@@ -32,10 +32,7 @@
* Media network event.
*/
public final class NetworkEvent extends Event implements Parcelable {
- /** Network type is not specified. Default type. */
- public static final int NETWORK_TYPE_NONE = 0;
- // TODO: replace NONE with UNKNOWN
- /** @hide */
+ /** Network type is not known. Default type. */
public static final int NETWORK_TYPE_UNKNOWN = 0;
/** Other network type */
public static final int NETWORK_TYPE_OTHER = 1;
@@ -54,7 +51,6 @@
/** 5G SA network */
public static final int NETWORK_TYPE_5G_SA = 8;
/** Not network connected */
- /** @hide */
public static final int NETWORK_TYPE_OFFLINE = 9;
private final int mNetworkType;
@@ -62,7 +58,6 @@
/** @hide */
@IntDef(prefix = "NETWORK_TYPE_", value = {
- NETWORK_TYPE_NONE,
NETWORK_TYPE_UNKNOWN,
NETWORK_TYPE_OTHER,
NETWORK_TYPE_WIFI,
@@ -83,8 +78,8 @@
*/
public static String networkTypeToString(@NetworkType int value) {
switch (value) {
- case NETWORK_TYPE_NONE:
- return "NETWORK_TYPE_NONE";
+ case NETWORK_TYPE_UNKNOWN:
+ return "NETWORK_TYPE_UNKNOWN";
case NETWORK_TYPE_OTHER:
return "NETWORK_TYPE_OTHER";
case NETWORK_TYPE_WIFI:
@@ -114,10 +109,10 @@
* @hide
*/
public NetworkEvent(@NetworkType int type, long timeSinceCreatedMillis,
- @Nullable Bundle extras) {
+ @NonNull Bundle extras) {
this.mNetworkType = type;
this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
- this.mExtras = extras == null ? null : extras.deepCopy();
+ this.mMetricsBundle = extras == null ? null : extras.deepCopy();
}
/**
@@ -138,6 +133,16 @@
return mTimeSinceCreatedMillis;
}
+ /**
+ * Gets metrics-related information that is not supported by dedicated methods.
+ * <p>It is intended to be used for backwards compatibility by the metrics infrastructure.
+ */
+ @Override
+ @NonNull
+ public Bundle getMetricsBundle() {
+ return mMetricsBundle;
+ }
+
@Override
public String toString() {
return "NetworkEvent { "
@@ -162,12 +167,9 @@
@Override
public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
- byte flg = 0;
- if (mExtras != null) flg |= 0x1;
- dest.writeByte(flg);
dest.writeInt(mNetworkType);
dest.writeLong(mTimeSinceCreatedMillis);
- if (mExtras != null) dest.writeBundle(mExtras);
+ dest.writeBundle(mMetricsBundle);
}
@Override
@@ -177,14 +179,13 @@
/** @hide */
/* package-private */ NetworkEvent(@NonNull android.os.Parcel in) {
- byte flg = in.readByte();
int type = in.readInt();
long timeSinceCreatedMillis = in.readLong();
- Bundle extras = (flg & 0x2) == 0 ? null : in.readBundle();
+ Bundle extras = in.readBundle();
this.mNetworkType = type;
this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
- this.mExtras = extras;
+ this.mMetricsBundle = extras;
}
/**
@@ -207,9 +208,9 @@
* A builder for {@link NetworkEvent}
*/
public static final class Builder {
- private int mNetworkType = NETWORK_TYPE_NONE;
+ private int mNetworkType = NETWORK_TYPE_UNKNOWN;
private long mTimeSinceCreatedMillis = -1;
- private Bundle mExtras;
+ private Bundle mMetricsBundle = new Bundle();
/**
* Creates a new Builder.
@@ -236,18 +237,20 @@
}
/**
- * Set extras for compatibility.
- * <p>Should be used by support library only.
- * @hide
+ * Sets metrics-related information that is not supported by dedicated
+ * methods.
+ * <p>It is intended to be used for backwards compatibility by the
+ * metrics infrastructure.
*/
- public @NonNull Builder setExtras(@NonNull Bundle extras) {
- mExtras = extras;
+ public @NonNull Builder setMetricsBundle(@NonNull Bundle metricsBundle) {
+ mMetricsBundle = metricsBundle;
return this;
}
/** Builds the instance. */
public @NonNull NetworkEvent build() {
- NetworkEvent o = new NetworkEvent(mNetworkType, mTimeSinceCreatedMillis, mExtras);
+ NetworkEvent o =
+ new NetworkEvent(mNetworkType, mTimeSinceCreatedMillis, mMetricsBundle);
return o;
}
}
diff --git a/media/java/android/media/metrics/PlaybackErrorEvent.java b/media/java/android/media/metrics/PlaybackErrorEvent.java
index ccf848b..f36c04e 100644
--- a/media/java/android/media/metrics/PlaybackErrorEvent.java
+++ b/media/java/android/media/metrics/PlaybackErrorEvent.java
@@ -33,11 +33,77 @@
*/
public final class PlaybackErrorEvent extends Event implements Parcelable {
/** Unknown error code. */
- public static final int ERROR_CODE_UNKNOWN = 0;
+ public static final int ERROR_UNKNOWN = 0;
/** Error code for other errors */
- public static final int ERROR_CODE_OTHER = 1;
+ public static final int ERROR_OTHER = 1;
/** Error code for runtime errors */
- public static final int ERROR_CODE_RUNTIME = 2;
+ public static final int ERROR_RUNTIME = 2;
+
+ /** No network */
+ public static final int ERROR_NETWORK_OFFLINE = 3;
+ /** Connection opening error */
+ public static final int ERROR_NETWORK_CONNECT = 4;
+ /** Bad HTTP status code */
+ public static final int ERROR_NETWORK_BAD_STATUS = 5;
+ /** DNS resolution error */
+ public static final int ERROR_NETWORK_DNS = 6;
+ /** Network socket timeout */
+ public static final int ERROR_NETWORK_TIMEOUT = 7;
+ /** Connection closed */
+ public static final int ERROR_NETWORK_CLOSED = 8;
+ /** Other network errors */
+ public static final int ERROR_NETWORK_OTHER = 9;
+
+ /** Manifest parsing error */
+ public static final int ERROR_MEDIA_MANIFEST = 10;
+ /**
+ * Media bitstream (audio, video, text, metadata) parsing error, either malformed or
+ * unsupported.
+ */
+ public static final int ERROR_MEDIA_PARSER = 11;
+ /** Other media errors */
+ public static final int ERROR_MEDIA_OTHER = 12;
+
+ /** Codec initialization failed */
+ public static final int ERROR_DECODER_INIT = 13;
+ /** Decoding failed */
+ public static final int ERROR_DECODER_DECODE = 14;
+ /** Out of memory */
+ public static final int ERROR_DECODER_OOM = 15;
+ /** Other decoder errors */
+ public static final int ERROR_DECODER_OTHER = 16;
+
+ /** AudioTrack initialization failed */
+ public static final int ERROR_AUDIOTRACK_INIT = 17;
+ /** AudioTrack writing failed */
+ public static final int ERROR_AUDIOTRACK_WRITE = 18;
+ /** Other AudioTrack errors */
+ public static final int ERROR_AUDIOTRACK_OTHER = 19;
+
+ /** Exception in remote controller or player */
+ public static final int ERROR_PLAYER_REMOTE = 20;
+ /** Error when a Live playback falls behind the Live DVR window. */
+ public static final int ERROR_PLAYER_BEHIND_LIVE_WINDOW = 21;
+ /** Other player errors */
+ public static final int ERROR_PLAYER_OTHER = 22;
+
+ /** Scheme unsupported by device */
+ public static final int ERROR_DRM_UNAVAILABLE = 23;
+ /** Provisioning failed */
+ public static final int ERROR_DRM_PROVISIONING_FAILED = 24;
+ /** Failed to acquire license */
+ public static final int ERROR_DRM_LICENSE_ERROR = 25;
+ /** Operation prevented by license policy */
+ public static final int ERROR_DRM_DISALLOWED = 26;
+ /** Failure in the DRM system */
+ public static final int ERROR_DRM_SYSTEM_ERROR = 27;
+ /** Incompatible content */
+ public static final int ERROR_DRM_CONTENT_ERROR = 28;
+ /** Device has been revoked */
+ public static final int ERROR_DRM_REVOKED = 29;
+ /** Other drm errors */
+ public static final int ERROR_DRM_OTHER = 30;
+
private final @Nullable String mExceptionStack;
private final int mErrorCode;
@@ -46,11 +112,38 @@
/** @hide */
- // TODO: more error types
- @IntDef(prefix = "ERROR_CODE_", value = {
- ERROR_CODE_UNKNOWN,
- ERROR_CODE_OTHER,
- ERROR_CODE_RUNTIME
+ @IntDef(prefix = "ERROR_", value = {
+ ERROR_UNKNOWN,
+ ERROR_OTHER,
+ ERROR_RUNTIME,
+ ERROR_NETWORK_OFFLINE,
+ ERROR_NETWORK_CONNECT,
+ ERROR_NETWORK_BAD_STATUS,
+ ERROR_NETWORK_DNS,
+ ERROR_NETWORK_TIMEOUT,
+ ERROR_NETWORK_CLOSED,
+ ERROR_NETWORK_OTHER,
+ ERROR_MEDIA_MANIFEST,
+ ERROR_MEDIA_PARSER,
+ ERROR_MEDIA_OTHER,
+ ERROR_DECODER_INIT,
+ ERROR_DECODER_DECODE,
+ ERROR_DECODER_OOM,
+ ERROR_DECODER_OTHER,
+ ERROR_AUDIOTRACK_INIT,
+ ERROR_AUDIOTRACK_WRITE,
+ ERROR_AUDIOTRACK_OTHER,
+ ERROR_PLAYER_REMOTE,
+ ERROR_PLAYER_BEHIND_LIVE_WINDOW,
+ ERROR_PLAYER_OTHER,
+ ERROR_DRM_UNAVAILABLE,
+ ERROR_DRM_PROVISIONING_FAILED,
+ ERROR_DRM_LICENSE_ERROR,
+ ERROR_DRM_DISALLOWED,
+ ERROR_DRM_SYSTEM_ERROR,
+ ERROR_DRM_CONTENT_ERROR,
+ ERROR_DRM_REVOKED,
+ ERROR_DRM_OTHER,
})
@Retention(java.lang.annotation.RetentionPolicy.SOURCE)
public @interface ErrorCode {}
@@ -65,12 +158,12 @@
int errorCode,
int subErrorCode,
long timeSinceCreatedMillis,
- @Nullable Bundle extras) {
+ @NonNull Bundle extras) {
this.mExceptionStack = exceptionStack;
this.mErrorCode = errorCode;
this.mSubErrorCode = subErrorCode;
this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
- this.mExtras = extras == null ? null : extras.deepCopy();
+ this.mMetricsBundle = extras.deepCopy();
}
/** @hide */
@@ -107,6 +200,16 @@
return mTimeSinceCreatedMillis;
}
+ /**
+ * Gets metrics-related information that is not supported by dedicated methods.
+ * <p>It is intended to be used for backwards compatibility by the metrics infrastructure.
+ */
+ @Override
+ @NonNull
+ public Bundle getMetricsBundle() {
+ return mMetricsBundle;
+ }
+
@Override
public String toString() {
return "PlaybackErrorEvent { "
@@ -138,13 +241,12 @@
public void writeToParcel(@NonNull Parcel dest, int flags) {
byte flg = 0;
if (mExceptionStack != null) flg |= 0x1;
- if (mExtras != null) flg |= 0x2;
dest.writeByte(flg);
if (mExceptionStack != null) dest.writeString(mExceptionStack);
dest.writeInt(mErrorCode);
dest.writeInt(mSubErrorCode);
dest.writeLong(mTimeSinceCreatedMillis);
- if (mExtras != null) dest.writeBundle(mExtras);
+ dest.writeBundle(mMetricsBundle);
}
@Override
@@ -159,13 +261,13 @@
int errorCode = in.readInt();
int subErrorCode = in.readInt();
long timeSinceCreatedMillis = in.readLong();
- Bundle extras = (flg & 0x2) == 0 ? null : in.readBundle();
+ Bundle extras = in.readBundle();
this.mExceptionStack = exceptionStack;
this.mErrorCode = errorCode;
this.mSubErrorCode = subErrorCode;
this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
- this.mExtras = extras;
+ this.mMetricsBundle = extras;
}
@@ -190,7 +292,7 @@
private int mErrorCode;
private int mSubErrorCode;
private long mTimeSinceCreatedMillis = -1;
- private Bundle mExtras;
+ private Bundle mMetricsBundle = new Bundle();
/**
* Creates a new Builder.
@@ -235,12 +337,13 @@
}
/**
- * Set extras for compatibility.
- * <p>Should be used by support library only.
- * @hide
+ * Sets metrics-related information that is not supported by dedicated
+ * methods.
+ * <p>It is intended to be used for backwards compatibility by the
+ * metrics infrastructure.
*/
- public @NonNull Builder setExtras(@NonNull Bundle extras) {
- mExtras = extras;
+ public @NonNull Builder setMetricsBundle(@NonNull Bundle metricsBundle) {
+ mMetricsBundle = metricsBundle;
return this;
}
@@ -260,7 +363,7 @@
mErrorCode,
mSubErrorCode,
mTimeSinceCreatedMillis,
- mExtras);
+ mMetricsBundle);
return o;
}
}
diff --git a/media/java/android/media/metrics/PlaybackMetrics.java b/media/java/android/media/metrics/PlaybackMetrics.java
index 3ffd10f..5f606a0 100644
--- a/media/java/android/media/metrics/PlaybackMetrics.java
+++ b/media/java/android/media/metrics/PlaybackMetrics.java
@@ -60,15 +60,13 @@
public static final int STREAM_TYPE_SS = 5;
/** Unknown playback type. */
- // TODO: change the PLAYBACK_TYPE_ values
- /** @hide */
public static final int PLAYBACK_TYPE_UNKNOWN = 0;
/** VOD (Video on Demand) playback type. */
- public static final int PLAYBACK_TYPE_VOD = 0;
+ public static final int PLAYBACK_TYPE_VOD = 1;
/** Live playback type. */
- public static final int PLAYBACK_TYPE_LIVE = 1;
+ public static final int PLAYBACK_TYPE_LIVE = 2;
/** Other playback type. */
- public static final int PLAYBACK_TYPE_OTHER = 2;
+ public static final int PLAYBACK_TYPE_OTHER = 3;
/** DRM is not used. */
public static final int DRM_TYPE_NONE = 0;
@@ -86,15 +84,13 @@
public static final int DRM_TYPE_CLEARKEY = 6;
/** Unknown content type. */
- // TODO: change the CONTENT_TYPE_ values
- /** @hide */
public static final int CONTENT_TYPE_UNKNOWN = 0;
/** Main contents. */
- public static final int CONTENT_TYPE_MAIN = 0;
+ public static final int CONTENT_TYPE_MAIN = 1;
/** Advertisement contents. */
- public static final int CONTENT_TYPE_AD = 1;
+ public static final int CONTENT_TYPE_AD = 2;
/** Other contents. */
- public static final int CONTENT_TYPE_OTHER = 2;
+ public static final int CONTENT_TYPE_OTHER = 3;
/** @hide */
@@ -170,7 +166,7 @@
private final long mLocalBytesRead;
private final long mNetworkTransferDurationMillis;
private final byte[] mDrmSessionId;
- private final Bundle mExtras;
+ private final @NonNull Bundle mMetricsBundle;
/**
* Creates a new PlaybackMetrics.
@@ -194,7 +190,7 @@
long localBytesRead,
long networkTransferDurationMillis,
byte[] drmSessionId,
- @Nullable Bundle extras) {
+ @NonNull Bundle extras) {
this.mMediaDurationMillis = mediaDurationMillis;
this.mStreamSource = streamSource;
this.mStreamType = streamType;
@@ -212,7 +208,7 @@
this.mLocalBytesRead = localBytesRead;
this.mNetworkTransferDurationMillis = networkTransferDurationMillis;
this.mDrmSessionId = drmSessionId;
- this.mExtras = extras == null ? null : extras.deepCopy();
+ this.mMetricsBundle = extras.deepCopy();
}
/**
@@ -338,12 +334,23 @@
return mNetworkTransferDurationMillis;
}
- /** @hide */
+ /**
+ * Gets DRM session ID.
+ */
@NonNull
public byte[] getDrmSessionId() {
return mDrmSessionId;
}
+ /**
+ * Gets metrics-related information that is not supported by dedicated methods.
+ * <p>It is intended to be used for backwards compatibility by the metrics infrastructure.
+ */
+ @NonNull
+ public Bundle getMetricsBundle() {
+ return mMetricsBundle;
+ }
+
@Override
public String toString() {
return "PlaybackMetrics { "
@@ -402,7 +409,6 @@
long flg = 0;
if (mPlayerName != null) flg |= 0x80;
if (mPlayerVersion != null) flg |= 0x100;
- if (mExtras != null) flg |= 0x200;
dest.writeLong(flg);
dest.writeLong(mMediaDurationMillis);
dest.writeInt(mStreamSource);
@@ -412,7 +418,6 @@
dest.writeInt(mContentType);
if (mPlayerName != null) dest.writeString(mPlayerName);
if (mPlayerVersion != null) dest.writeString(mPlayerVersion);
- if (mExtras != null) dest.writeBundle(mExtras);
dest.writeLongArray(mExperimentIds);
dest.writeInt(mVideoFramesPlayed);
dest.writeInt(mVideoFramesDropped);
@@ -422,6 +427,7 @@
dest.writeLong(mNetworkTransferDurationMillis);
dest.writeInt(mDrmSessionId.length);
dest.writeByteArray(mDrmSessionId);
+ dest.writeBundle(mMetricsBundle);
}
@Override
@@ -440,7 +446,6 @@
int contentType = in.readInt();
String playerName = (flg & 0x80) == 0 ? null : in.readString();
String playerVersion = (flg & 0x100) == 0 ? null : in.readString();
- Bundle extras = (flg & 0x200) == 0 ? null : in.readBundle();
long[] experimentIds = in.createLongArray();
int videoFramesPlayed = in.readInt();
int videoFramesDropped = in.readInt();
@@ -451,6 +456,7 @@
int drmSessionIdLen = in.readInt();
byte[] drmSessionId = new byte[drmSessionIdLen];
in.readByteArray(drmSessionId);
+ Bundle extras = in.readBundle();
this.mMediaDurationMillis = mediaDurationMillis;
this.mStreamSource = streamSource;
@@ -469,7 +475,7 @@
this.mLocalBytesRead = localBytesRead;
this.mNetworkTransferDurationMillis = networkTransferDurationMillis;
this.mDrmSessionId = drmSessionId;
- this.mExtras = extras;
+ this.mMetricsBundle = extras;
}
public static final @NonNull Parcelable.Creator<PlaybackMetrics> CREATOR =
@@ -506,7 +512,7 @@
private long mLocalBytesRead = -1;
private long mNetworkTransferDurationMillis = -1;
private byte[] mDrmSessionId = new byte[0];
- private Bundle mExtras;
+ private Bundle mMetricsBundle = new Bundle();
/**
* Creates a new Builder.
@@ -646,7 +652,7 @@
}
/**
- * @hide
+ * Sets DRM session ID.
*/
public @NonNull Builder setDrmSessionId(@NonNull byte[] drmSessionId) {
mDrmSessionId = drmSessionId;
@@ -654,12 +660,13 @@
}
/**
- * Set extras for compatibility.
- * <p>Should be used by support library only.
- * @hide
+ * Sets metrics-related information that is not supported by dedicated
+ * methods.
+ * <p>It is intended to be used for backwards compatibility by the
+ * metrics infrastructure.
*/
- public @NonNull Builder setExtras(@NonNull Bundle extras) {
- mExtras = extras;
+ public @NonNull Builder setMetricsBundle(@NonNull Bundle metricsBundle) {
+ mMetricsBundle = metricsBundle;
return this;
}
@@ -683,7 +690,7 @@
mLocalBytesRead,
mNetworkTransferDurationMillis,
mDrmSessionId,
- mExtras);
+ mMetricsBundle);
return o;
}
diff --git a/media/java/android/media/metrics/PlaybackSession.java b/media/java/android/media/metrics/PlaybackSession.java
index 272fd9b..aad510e 100644
--- a/media/java/android/media/metrics/PlaybackSession.java
+++ b/media/java/android/media/metrics/PlaybackSession.java
@@ -80,14 +80,7 @@
mManager.reportTrackChangeEvent(mId, event);
}
- public @NonNull String getId() {
- // TODO: remove this method and use getSessionId();
- return mId;
- }
-
- /** @hide */
public @NonNull LogSessionId getSessionId() {
- // TODO: remove getId() and use this method;
return mLogSessionId;
}
diff --git a/media/java/android/media/metrics/PlaybackStateEvent.java b/media/java/android/media/metrics/PlaybackStateEvent.java
index 2bab5c9..449abe9 100644
--- a/media/java/android/media/metrics/PlaybackStateEvent.java
+++ b/media/java/android/media/metrics/PlaybackStateEvent.java
@@ -138,10 +138,10 @@
public PlaybackStateEvent(
int state,
long timeSinceCreatedMillis,
- @Nullable Bundle extras) {
+ @NonNull Bundle extras) {
this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
this.mState = state;
- this.mExtras = extras == null ? null : extras.deepCopy();
+ this.mMetricsBundle = extras.deepCopy();
}
/**
@@ -161,6 +161,16 @@
return mTimeSinceCreatedMillis;
}
+ /**
+ * Gets metrics-related information that is not supported by dedicated methods.
+ * <p>It is intended to be used for backwards compatibility by the metrics infrastructure.
+ */
+ @Override
+ @NonNull
+ public Bundle getMetricsBundle() {
+ return mMetricsBundle;
+ }
+
@Override
public boolean equals(@Nullable Object o) {
if (this == o) return true;
@@ -177,12 +187,9 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- byte flg = 0;
- if (mExtras != null) flg |= 0x1;
- dest.writeByte(flg);
dest.writeInt(mState);
dest.writeLong(mTimeSinceCreatedMillis);
- if (mExtras != null) dest.writeBundle(mExtras);
+ dest.writeBundle(mMetricsBundle);
}
@Override
@@ -192,14 +199,13 @@
/** @hide */
/* package-private */ PlaybackStateEvent(@NonNull Parcel in) {
- byte flg = in.readByte();
int state = in.readInt();
long timeSinceCreatedMillis = in.readLong();
- Bundle extras = (flg & 0x1) == 0 ? null : in.readBundle();
+ Bundle extras = in.readBundle();
this.mState = state;
this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
- this.mExtras = extras;
+ this.mMetricsBundle = extras;
}
public static final @NonNull Parcelable.Creator<PlaybackStateEvent> CREATOR =
@@ -221,7 +227,7 @@
public static final class Builder {
private int mState = STATE_NOT_STARTED;
private long mTimeSinceCreatedMillis = -1;
- private Bundle mExtras;
+ private Bundle mMetricsBundle = new Bundle();
/**
* Creates a new Builder.
@@ -248,12 +254,13 @@
}
/**
- * Set extras for compatibility.
- * <p>Should be used by support library only.
- * @hide
+ * Sets metrics-related information that is not supported by dedicated
+ * methods.
+ * <p>It is intended to be used for backwards compatibility by the
+ * metrics infrastructure.
*/
- public @NonNull Builder setExtras(@NonNull Bundle extras) {
- mExtras = extras;
+ public @NonNull Builder setMetricsBundle(@NonNull Bundle metricsBundle) {
+ mMetricsBundle = metricsBundle;
return this;
}
@@ -262,7 +269,7 @@
PlaybackStateEvent o = new PlaybackStateEvent(
mState,
mTimeSinceCreatedMillis,
- mExtras);
+ mMetricsBundle);
return o;
}
}
diff --git a/media/java/android/media/metrics/RecordingSession.java b/media/java/android/media/metrics/RecordingSession.java
index 541d129..d388351 100644
--- a/media/java/android/media/metrics/RecordingSession.java
+++ b/media/java/android/media/metrics/RecordingSession.java
@@ -25,7 +25,6 @@
/**
* An instances of this class represents a session of media recording.
- * @hide
*/
public final class RecordingSession implements AutoCloseable {
private final @NonNull String mId;
@@ -42,6 +41,10 @@
mLogSessionId = new LogSessionId(mId);
}
+ public @NonNull LogSessionId getSessionId() {
+ return mLogSessionId;
+ }
+
@Override
public boolean equals(@Nullable Object o) {
if (this == o) return true;
diff --git a/media/java/android/media/metrics/TrackChangeEvent.java b/media/java/android/media/metrics/TrackChangeEvent.java
index a3eb4ad..c3670269 100644
--- a/media/java/android/media/metrics/TrackChangeEvent.java
+++ b/media/java/android/media/metrics/TrackChangeEvent.java
@@ -102,39 +102,6 @@
@Retention(RetentionPolicy.SOURCE)
public @interface TrackType {}
- // TODO: remove this constructor. Use the private one below.
- public TrackChangeEvent(
- int state,
- int reason,
- @Nullable String containerMimeType,
- @Nullable String sampleMimeType,
- @Nullable String codecName,
- int bitrate,
- long timeSinceCreatedMillis,
- int type,
- @Nullable String language,
- @Nullable String languageRegion,
- int channelCount,
- int sampleRate,
- int width,
- int height) {
- this.mState = state;
- this.mReason = reason;
- this.mContainerMimeType = containerMimeType;
- this.mSampleMimeType = sampleMimeType;
- this.mCodecName = codecName;
- this.mBitrate = bitrate;
- this.mTimeSinceCreatedMillis = timeSinceCreatedMillis;
- this.mType = type;
- this.mLanguage = language;
- this.mLanguageRegion = languageRegion;
- this.mChannelCount = channelCount;
- this.mAudioSampleRate = sampleRate;
- this.mWidth = width;
- this.mHeight = height;
- this.mVideoFrameRate = -1;
- }
-
private TrackChangeEvent(
int state,
int reason,
@@ -151,7 +118,7 @@
int width,
int height,
float videoFrameRate,
- @Nullable Bundle extras) {
+ @NonNull Bundle extras) {
this.mState = state;
this.mReason = reason;
this.mContainerMimeType = containerMimeType;
@@ -167,7 +134,7 @@
this.mWidth = width;
this.mHeight = height;
this.mVideoFrameRate = videoFrameRate;
- this.mExtras = extras == null ? null : extras.deepCopy();
+ this.mMetricsBundle = extras.deepCopy();
}
/**
@@ -258,11 +225,11 @@
}
/**
- * Gets sample rate.
+ * Gets audio sample rate.
* @return the sample rate, or -1 if unknown.
*/
@IntRange(from = -1, to = Integer.MAX_VALUE)
- public int getSampleRate() {
+ public int getAudioSampleRate() {
return mAudioSampleRate;
}
@@ -287,13 +254,22 @@
/**
* Gets video frame rate.
* @return the video frame rate, or -1 if unknown.
- * @hide
*/
@FloatRange(from = -1, to = Float.MAX_VALUE)
public float getVideoFrameRate() {
return mVideoFrameRate;
}
+ /**
+ * Gets metrics-related information that is not supported by dedicated methods.
+ * <p>It is intended to be used for backwards compatibility by the metrics infrastructure.
+ */
+ @Override
+ @NonNull
+ public Bundle getMetricsBundle() {
+ return mMetricsBundle;
+ }
+
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
int flg = 0;
@@ -302,7 +278,6 @@
if (mCodecName != null) flg |= 0x10;
if (mLanguage != null) flg |= 0x100;
if (mLanguageRegion != null) flg |= 0x200;
- if (mExtras != null) flg |= 0x400;
dest.writeInt(flg);
dest.writeInt(mState);
dest.writeInt(mReason);
@@ -319,7 +294,7 @@
dest.writeInt(mWidth);
dest.writeInt(mHeight);
dest.writeFloat(mVideoFrameRate);
- if (mExtras != null) dest.writeBundle(mExtras);
+ dest.writeBundle(mMetricsBundle);
}
@Override
@@ -345,7 +320,7 @@
int width = in.readInt();
int height = in.readInt();
float videoFrameRate = in.readFloat();
- Bundle extras = (flg & 0x400) == 0 ? null : in.readBundle();
+ Bundle extras = in.readBundle();
this.mState = state;
this.mReason = reason;
@@ -362,7 +337,7 @@
this.mWidth = width;
this.mHeight = height;
this.mVideoFrameRate = videoFrameRate;
- this.mExtras = extras;
+ this.mMetricsBundle = extras;
}
public static final @NonNull Parcelable.Creator<TrackChangeEvent> CREATOR =
@@ -448,7 +423,7 @@
private int mWidth = -1;
private int mHeight = -1;
private float mVideoFrameRate = -1;
- private Bundle mExtras;
+ private Bundle mMetricsBundle = new Bundle();
private long mBuilderFieldsSet = 0L;
@@ -571,9 +546,8 @@
* Sets sample rate.
* @param value the sample rate. -1 indicates the value is unknown.
*/
- public @NonNull Builder setSampleRate(
+ public @NonNull Builder setAudioSampleRate(
@IntRange(from = -1, to = Integer.MAX_VALUE) int value) {
- // TODO: rename it to setAudioSampleRate
checkNotUsed();
mBuilderFieldsSet |= 0x800;
mAudioSampleRate = value;
@@ -605,7 +579,6 @@
/**
* Sets video frame rate.
* @param value the video frame rate. -1 indicates the value is unknown.
- * @hide
*/
public @NonNull Builder setVideoFrameRate(
@FloatRange(from = -1, to = Float.MAX_VALUE) float value) {
@@ -615,12 +588,13 @@
}
/**
- * Set extras for compatibility.
- * <p>Should be used by support library only.
- * @hide
+ * Sets metrics-related information that is not supported by dedicated
+ * methods.
+ * <p>It is intended to be used for backwards compatibility by the
+ * metrics infrastructure.
*/
- public @NonNull Builder setExtras(@NonNull Bundle extras) {
- mExtras = extras;
+ public @NonNull Builder setMetricsBundle(@NonNull Bundle metricsBundle) {
+ mMetricsBundle = metricsBundle;
return this;
}
@@ -645,7 +619,7 @@
mWidth,
mHeight,
mVideoFrameRate,
- mExtras);
+ mMetricsBundle);
return o;
}
diff --git a/packages/Connectivity/TEST_MAPPING b/packages/Connectivity/TEST_MAPPING
new file mode 100644
index 0000000..94f9232
--- /dev/null
+++ b/packages/Connectivity/TEST_MAPPING
@@ -0,0 +1,19 @@
+{
+ "imports": [
+ {
+ "path": "frameworks/base/core/java/android/net"
+ },
+ {
+ "path": "packages/modules/NetworkStack"
+ },
+ {
+ "path": "packages/modules/CaptivePortalLogin"
+ },
+ {
+ "path": "packages/modules/Connectivity"
+ },
+ {
+ "path": "packages/modules/Connectivity/Tethering"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/Connectivity/framework/api/current.txt b/packages/Connectivity/framework/api/current.txt
index e415e01..ad44b27 100644
--- a/packages/Connectivity/framework/api/current.txt
+++ b/packages/Connectivity/framework/api/current.txt
@@ -396,6 +396,7 @@
public static class NetworkRequest.Builder {
ctor public NetworkRequest.Builder();
+ ctor public NetworkRequest.Builder(@NonNull android.net.NetworkRequest);
method public android.net.NetworkRequest.Builder addCapability(int);
method public android.net.NetworkRequest.Builder addTransportType(int);
method public android.net.NetworkRequest build();
diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt
index 5bd01a6..b179c6d 100644
--- a/packages/Connectivity/framework/api/module-lib-current.txt
+++ b/packages/Connectivity/framework/api/module-lib-current.txt
@@ -13,11 +13,14 @@
method @NonNull public static String getPrivateDnsMode(@NonNull android.content.Context);
method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
+ method @Deprecated public boolean requestRouteToHostAddress(int, java.net.InetAddress);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void setAcceptPartialConnectivity(@NonNull android.net.Network, boolean, boolean);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void setAcceptUnvalidated(@NonNull android.net.Network, boolean, boolean);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void setAvoidUnvalidated(@NonNull android.net.Network);
method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void setGlobalProxy(@Nullable android.net.ProxyInfo);
+ method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void setLegacyLockdownVpnEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void setProfileNetworkPreference(@NonNull android.os.UserHandle, int, @Nullable java.util.concurrent.Executor, @Nullable Runnable);
+ method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void setRequireVpnForUids(boolean, @NonNull java.util.Collection<android.util.Range<java.lang.Integer>>);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void startCaptivePortalApp(@NonNull android.net.Network);
method public void systemReady();
@@ -40,7 +43,14 @@
}
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
+ field public static final long REDACT_FOR_NETWORK_SETTINGS = 4L; // 0x4L
+ field public static final long REDACT_NONE = 0L; // 0x0L
field public static final int TRANSPORT_TEST = 7; // 0x7
}
@@ -48,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>>);
}
@@ -92,6 +108,11 @@
field @NonNull public static final android.os.Parcelable.Creator<android.net.TestNetworkSpecifier> CREATOR;
}
+ public interface TransportInfo {
+ method public default long getApplicableRedactions();
+ method @NonNull public default android.net.TransportInfo makeCopy(long);
+ }
+
public final class VpnTransportInfo implements android.os.Parcelable android.net.TransportInfo {
ctor public VpnTransportInfo(int);
method public int describeContents();
diff --git a/packages/Connectivity/framework/api/system-current.txt b/packages/Connectivity/framework/api/system-current.txt
index 8845225..c19fcdd 100644
--- a/packages/Connectivity/framework/api/system-current.txt
+++ b/packages/Connectivity/framework/api/system-current.txt
@@ -52,7 +52,7 @@
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEntitlementResultListener);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) public int registerNetworkProvider(@NonNull android.net.NetworkProvider);
- method public void registerQosCallback(@NonNull android.net.QosSocketInfo, @NonNull android.net.QosCallback, @NonNull java.util.concurrent.Executor);
+ method public void registerQosCallback(@NonNull android.net.QosSocketInfo, @NonNull java.util.concurrent.Executor, @NonNull android.net.QosCallback);
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void requestNetwork(@NonNull android.net.NetworkRequest, int, int, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_AIRPLANE_MODE, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean);
@@ -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);
@@ -261,7 +268,6 @@
}
public final class NetworkCapabilities implements android.os.Parcelable {
- ctor public NetworkCapabilities(@Nullable android.net.NetworkCapabilities, boolean);
method @NonNull public int[] getAdministratorUids();
method @Nullable public String getSsid();
method @NonNull public int[] getTransportTypes();
@@ -389,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
}
@@ -435,11 +442,6 @@
field public final int tcpWindowScale;
}
- public interface TransportInfo {
- method public default boolean hasLocationSensitiveFields();
- method @NonNull public default android.net.TransportInfo makeCopy(boolean);
- }
-
}
package android.net.apf {
diff --git a/packages/Connectivity/framework/jarjar-rules.txt b/packages/Connectivity/framework/jarjar-rules.txt
index 0959840..7474c24 100644
--- a/packages/Connectivity/framework/jarjar-rules.txt
+++ b/packages/Connectivity/framework/jarjar-rules.txt
@@ -1,4 +1,5 @@
rule com.android.net.module.util.** android.net.connectivity.framework.util.@1
+rule android.net.NetworkFactory* android.net.connectivity.framework.NetworkFactory@1
# TODO (b/149403767): remove the annotations from net-utils-device-common instead of here
zap android.annotation.**
diff --git a/packages/Connectivity/framework/jni/android_net_NetworkUtils.cpp b/packages/Connectivity/framework/jni/android_net_NetworkUtils.cpp
index 19ffe77..c5b1ff8 100644
--- a/packages/Connectivity/framework/jni/android_net_NetworkUtils.cpp
+++ b/packages/Connectivity/framework/jni/android_net_NetworkUtils.cpp
@@ -30,6 +30,7 @@
#include <DnsProxydProtocol.h> // NETID_USE_LOCAL_NAMESERVERS
#include <cutils/properties.h>
+#include <nativehelper/JNIHelp.h>
#include <nativehelper/JNIPlatformHelp.h>
#include <nativehelper/ScopedLocalRef.h>
#include <utils/Log.h>
@@ -57,14 +58,6 @@
return clazz;
}
-static inline jmethodID GetMethodIDOrDie(JNIEnv* env, jclass clazz, const char* method_name,
- const char* method_signature) {
- jmethodID res = env->GetMethodID(clazz, method_name, method_signature);
- LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find method %s with signature %s", method_name,
- method_signature);
- return res;
-}
-
template <typename T>
static inline T MakeGlobalRefOrDie(JNIEnv* env, T in) {
jobject res = env->NewGlobalRef(in);
@@ -72,27 +65,6 @@
return static_cast<T>(res);
}
-static void throwErrnoException(JNIEnv* env, const char* functionName, int error) {
- ScopedLocalRef<jstring> detailMessage(env, env->NewStringUTF(functionName));
- if (detailMessage.get() == NULL) {
- // Not really much we can do here. We're probably dead in the water,
- // but let's try to stumble on...
- env->ExceptionClear();
- }
- static jclass errnoExceptionClass =
- MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/system/ErrnoException"));
-
- static jmethodID errnoExceptionCtor =
- GetMethodIDOrDie(env, errnoExceptionClass,
- "<init>", "(Ljava/lang/String;I)V");
-
- jobject exception = env->NewObject(errnoExceptionClass,
- errnoExceptionCtor,
- detailMessage.get(),
- error);
- env->Throw(reinterpret_cast<jthrowable>(exception));
-}
-
static void android_net_utils_attachDropAllBPFFilter(JNIEnv *env, jobject clazz, jobject javaFd)
{
struct sock_filter filter_code[] = {
@@ -170,7 +142,7 @@
int fd = resNetworkQuery(netId, queryname.data(), ns_class, ns_type, flags);
if (fd < 0) {
- throwErrnoException(env, "resNetworkQuery", -fd);
+ jniThrowErrnoException(env, "resNetworkQuery", -fd);
return nullptr;
}
@@ -185,7 +157,7 @@
int fd = resNetworkSend(netId, data, msgLen, flags);
if (fd < 0) {
- throwErrnoException(env, "resNetworkSend", -fd);
+ jniThrowErrnoException(env, "resNetworkSend", -fd);
return nullptr;
}
@@ -200,13 +172,13 @@
int res = resNetworkResult(fd, &rcode, buf.data(), MAXPACKETSIZE);
jniSetFileDescriptorOfFD(env, javaFd, -1);
if (res < 0) {
- throwErrnoException(env, "resNetworkResult", -res);
+ jniThrowErrnoException(env, "resNetworkResult", -res);
return nullptr;
}
jbyteArray answer = env->NewByteArray(res);
if (answer == nullptr) {
- throwErrnoException(env, "resNetworkResult", ENOMEM);
+ jniThrowErrnoException(env, "resNetworkResult", ENOMEM);
return nullptr;
} else {
env->SetByteArrayRegion(answer, 0, res,
@@ -228,7 +200,7 @@
static jobject android_net_utils_getDnsNetwork(JNIEnv *env, jobject thiz) {
unsigned dnsNetId = 0;
if (int res = getNetworkForDns(&dnsNetId) < 0) {
- throwErrnoException(env, "getDnsNetId", -res);
+ jniThrowErrnoException(env, "getDnsNetId", -res);
return nullptr;
}
bool privateDnsBypass = dnsNetId & NETID_USE_LOCAL_NAMESERVERS;
@@ -253,7 +225,7 @@
// Obtain the parameters of the TCP repair window.
int rc = getsockopt(fd, IPPROTO_TCP, TCP_REPAIR_WINDOW, &trw, &size);
if (rc == -1) {
- throwErrnoException(env, "getsockopt : TCP_REPAIR_WINDOW", errno);
+ jniThrowErrnoException(env, "getsockopt : TCP_REPAIR_WINDOW", errno);
return NULL;
}
@@ -264,7 +236,7 @@
// should be applied to the window size.
rc = getsockopt(fd, IPPROTO_TCP, TCP_INFO, &tcpinfo, &tcpinfo_size);
if (rc == -1) {
- throwErrnoException(env, "getsockopt : TCP_INFO", errno);
+ jniThrowErrnoException(env, "getsockopt : TCP_INFO", errno);
return NULL;
}
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index f8a0e4e..f207830 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -1125,12 +1125,13 @@
* @param ranges the UID ranges to restrict
* @param requireVpn whether the specified UID ranges must use a VPN
*
- * TODO: expose as @SystemApi.
* @hide
*/
@RequiresPermission(anyOf = {
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
- android.Manifest.permission.NETWORK_STACK})
+ android.Manifest.permission.NETWORK_STACK,
+ android.Manifest.permission.NETWORK_SETTINGS})
+ @SystemApi(client = MODULE_LIBRARIES)
public void setRequireVpnForUids(boolean requireVpn,
@NonNull Collection<Range<Integer>> ranges) {
Objects.requireNonNull(ranges);
@@ -1174,13 +1175,13 @@
*
* @param enabled whether legacy lockdown VPN is enabled or disabled
*
- * TODO: @SystemApi(client = MODULE_LIBRARIES)
- *
* @hide
*/
@RequiresPermission(anyOf = {
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_STACK,
android.Manifest.permission.NETWORK_SETTINGS})
+ @SystemApi(client = MODULE_LIBRARIES)
public void setLegacyLockdownVpnEnabled(boolean enabled) {
try {
mService.setLegacyLockdownVpnEnabled(enabled);
@@ -2127,6 +2128,7 @@
*/
@Deprecated
@UnsupportedAppUsage
+ @SystemApi(client = MODULE_LIBRARIES)
public boolean requestRouteToHostAddress(int networkType, InetAddress hostAddress) {
checkLegacyRoutingApiAccess();
try {
@@ -4942,20 +4944,20 @@
* {@link QosCallback#onError(QosCallbackException)}. see: {@link QosCallbackException}.
*
* @param socketInfo the socket information used to match QoS events
- * @param callback receives qos events that satisfy socketInfo
* @param executor The executor on which the callback will be invoked. The provided
* {@link Executor} must run callback sequentially, otherwise the order of
- * callbacks cannot be guaranteed.
+ * callbacks cannot be guaranteed.onQosCallbackRegistered
+ * @param callback receives qos events that satisfy socketInfo
*
* @hide
*/
@SystemApi
public void registerQosCallback(@NonNull final QosSocketInfo socketInfo,
- @NonNull final QosCallback callback,
- @CallbackExecutor @NonNull final Executor executor) {
+ @CallbackExecutor @NonNull final Executor executor,
+ @NonNull final QosCallback callback) {
Objects.requireNonNull(socketInfo, "socketInfo must be non-null");
- Objects.requireNonNull(callback, "callback must be non-null");
Objects.requireNonNull(executor, "executor must be non-null");
+ Objects.requireNonNull(callback, "callback must be non-null");
try {
synchronized (mQosCallbackConnections) {
diff --git a/packages/Connectivity/framework/src/android/net/NetworkAgent.java b/packages/Connectivity/framework/src/android/net/NetworkAgent.java
index 1416bb9..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());
@@ -434,7 +433,7 @@
}
mInitialConfiguration = new InitialConfiguration(context,
- new NetworkCapabilities(nc, /* parcelLocationSensitiveFields */ true),
+ new NetworkCapabilities(nc, NetworkCapabilities.REDACT_NONE),
new LinkProperties(lp), score, config, ni);
}
@@ -829,6 +828,7 @@
* @hide
*/
@Deprecated
+ @SystemApi
public void setLegacySubtype(final int legacySubtype, @NonNull final String legacySubtypeName) {
mNetworkInfo.setSubtype(legacySubtype, legacySubtypeName);
queueOrSendNetworkInfo(mNetworkInfo);
@@ -878,8 +878,7 @@
mBandwidthUpdatePending.set(false);
mLastBwRefreshTime = System.currentTimeMillis();
final NetworkCapabilities nc =
- new NetworkCapabilities(networkCapabilities,
- /* parcelLocationSensitiveFields */ true);
+ new NetworkCapabilities(networkCapabilities, NetworkCapabilities.REDACT_NONE);
queueOrSendMessage(reg -> reg.sendNetworkCapabilities(nc));
}
@@ -963,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 1466629..881fa8c 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
@@ -19,6 +19,7 @@
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
import android.annotation.IntDef;
+import android.annotation.LongDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -64,6 +65,68 @@
public final class NetworkCapabilities implements Parcelable {
private static final String TAG = "NetworkCapabilities";
+ /**
+ * Mechanism to support redaction of fields in NetworkCapabilities that are guarded by specific
+ * app permissions.
+ **/
+ /**
+ * Don't redact any fields since the receiving app holds all the necessary permissions.
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final long REDACT_NONE = 0;
+
+ /**
+ * Redact any fields that need {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
+ * permission since the receiving app does not hold this permission or the location toggle
+ * is off.
+ *
+ * @see android.Manifest.permission#ACCESS_FINE_LOCATION
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final long REDACT_FOR_ACCESS_FINE_LOCATION = 1 << 0;
+
+ /**
+ * Redact any fields that need {@link android.Manifest.permission#LOCAL_MAC_ADDRESS}
+ * permission since the receiving app does not hold this permission.
+ *
+ * @see android.Manifest.permission#LOCAL_MAC_ADDRESS
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final long REDACT_FOR_LOCAL_MAC_ADDRESS = 1 << 1;
+
+ /**
+ *
+ * Redact any fields that need {@link android.Manifest.permission#NETWORK_SETTINGS}
+ * permission since the receiving app does not hold this permission.
+ *
+ * @see android.Manifest.permission#NETWORK_SETTINGS
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final long REDACT_FOR_NETWORK_SETTINGS = 1 << 2;
+
+ /**
+ * Redact all fields in this object that require any relevant permission.
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final long REDACT_ALL = -1L;
+
+ /** @hide */
+ @LongDef(flag = true, prefix = { "REDACT_" }, value = {
+ REDACT_NONE,
+ REDACT_FOR_ACCESS_FINE_LOCATION,
+ REDACT_FOR_LOCAL_MAC_ADDRESS,
+ REDACT_FOR_NETWORK_SETTINGS,
+ REDACT_ALL
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RedactionType {}
+
// Set to true when private DNS is broken.
private boolean mPrivateDnsBroken;
@@ -78,32 +141,31 @@
private String mRequestorPackageName;
/**
- * Indicates whether parceling should preserve fields that are set based on permissions of
- * the process receiving the {@link NetworkCapabilities}.
+ * Indicates what fields should be redacted from this instance.
*/
- private final boolean mParcelLocationSensitiveFields;
+ private final @RedactionType long mRedactions;
public NetworkCapabilities() {
- mParcelLocationSensitiveFields = false;
+ mRedactions = REDACT_ALL;
clearAll();
mNetworkCapabilities = DEFAULT_CAPABILITIES;
}
public NetworkCapabilities(NetworkCapabilities nc) {
- this(nc, false /* parcelLocationSensitiveFields */);
+ this(nc, REDACT_ALL);
}
/**
* Make a copy of NetworkCapabilities.
*
* @param nc Original NetworkCapabilities
- * @param parcelLocationSensitiveFields Whether to parcel location sensitive data or not.
+ * @param redactions bitmask of redactions that needs to be performed on this new instance of
+ * {@link NetworkCapabilities}.
* @hide
*/
- @SystemApi
- public NetworkCapabilities(
- @Nullable NetworkCapabilities nc, boolean parcelLocationSensitiveFields) {
- mParcelLocationSensitiveFields = parcelLocationSensitiveFields;
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public NetworkCapabilities(@Nullable NetworkCapabilities nc, @RedactionType long redactions) {
+ mRedactions = redactions;
if (nc != null) {
set(nc);
}
@@ -115,11 +177,13 @@
* @hide
*/
public void clearAll() {
- // Ensures that the internal copies maintained by the connectivity stack does not set
- // this bit.
- if (mParcelLocationSensitiveFields) {
+ // Ensures that the internal copies maintained by the connectivity stack does not set it to
+ // anything other than |REDACT_ALL|.
+ if (mRedactions != REDACT_ALL) {
+ // This is needed because the current redaction mechanism relies on redaction while
+ // parceling.
throw new UnsupportedOperationException(
- "Cannot clear NetworkCapabilities when parcelLocationSensitiveFields is set");
+ "Cannot clear NetworkCapabilities when mRedactions is set");
}
mNetworkCapabilities = mTransportTypes = mUnwantedNetworkCapabilities = 0;
mLinkUpBandwidthKbps = mLinkDownBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED;
@@ -149,7 +213,7 @@
mLinkDownBandwidthKbps = nc.mLinkDownBandwidthKbps;
mNetworkSpecifier = nc.mNetworkSpecifier;
if (nc.getTransportInfo() != null) {
- setTransportInfo(nc.getTransportInfo().makeCopy(mParcelLocationSensitiveFields));
+ setTransportInfo(nc.getTransportInfo().makeCopy(mRedactions));
} else {
setTransportInfo(null);
}
@@ -575,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;
}
@@ -659,6 +735,7 @@
}
/** @hide */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public boolean hasUnwantedCapability(@NetCapability int capability) {
return isValidCapability(capability)
&& ((mUnwantedNetworkCapabilities & (1 << capability)) != 0);
@@ -672,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;
}
/**
@@ -2350,6 +2433,23 @@
}
/**
+ * Returns a bitmask of all the applicable redactions (based on the permissions held by the
+ * receiving app) to be performed on this object.
+ *
+ * @return bitmask of redactions applicable on this instance.
+ * @hide
+ */
+ public @RedactionType long getApplicableRedactions() {
+ // Currently, there are no fields redacted in NetworkCapabilities itself, so we just
+ // passthrough the redactions required by the embedded TransportInfo. If this changes
+ // in the future, modify this method.
+ if (mTransportInfo == null) {
+ return NetworkCapabilities.REDACT_NONE;
+ }
+ return mTransportInfo.getApplicableRedactions();
+ }
+
+ /**
* Builder class for NetworkCapabilities.
*
* This class is mainly for for {@link NetworkAgent} instances to use. Many fields in
diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
index cf131f0..bcbc04f7 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
@@ -216,6 +216,14 @@
}
/**
+ * Creates a new Builder of NetworkRequest from an existing instance.
+ */
+ public Builder(@NonNull final NetworkRequest request) {
+ Objects.requireNonNull(request);
+ mNetworkCapabilities = request.networkCapabilities;
+ }
+
+ /**
* Build {@link NetworkRequest} give the current set of capabilities.
*/
public NetworkRequest build() {
@@ -305,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.
*
@@ -567,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/NetworkUtils.java b/packages/Connectivity/framework/src/android/net/NetworkUtils.java
index c0f2628..c4bebc0 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkUtils.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkUtils.java
@@ -323,22 +323,7 @@
*/
@UnsupportedAppUsage
public static String trimV4AddrZeros(String addr) {
- if (addr == null) return null;
- String[] octets = addr.split("\\.");
- if (octets.length != 4) return addr;
- StringBuilder builder = new StringBuilder(16);
- String result = null;
- for (int i = 0; i < 4; i++) {
- try {
- if (octets[i].length() > 3) return addr;
- builder.append(Integer.parseInt(octets[i]));
- } catch (NumberFormatException e) {
- return addr;
- }
- if (i < 3) builder.append('.');
- }
- result = builder.toString();
- return result;
+ return Inet4AddressUtils.trimAddressZeros(addr);
}
/**
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/Connectivity/framework/src/android/net/TransportInfo.java b/packages/Connectivity/framework/src/android/net/TransportInfo.java
index aa4bbb0..fa889ea 100644
--- a/packages/Connectivity/framework/src/android/net/TransportInfo.java
+++ b/packages/Connectivity/framework/src/android/net/TransportInfo.java
@@ -29,35 +29,47 @@
public interface TransportInfo {
/**
- * Create a copy of a {@link TransportInfo} that will preserve location sensitive fields that
- * were set based on the permissions of the process that originally received it.
+ * Create a copy of a {@link TransportInfo} with some fields redacted based on the permissions
+ * held by the receiving app.
*
- * <p>By default {@link TransportInfo} does not preserve such fields during parceling, as
- * they should not be shared outside of the process that receives them without appropriate
- * checks.
+ * <p>
+ * Usage by connectivity stack:
+ * <ul>
+ * <li> Connectivity stack will invoke {@link #getApplicableRedactions()} to find the list
+ * of redactions that are required by this {@link TransportInfo} instance.</li>
+ * <li> Connectivity stack then loops through each bit in the bitmask returned and checks if the
+ * receiving app holds the corresponding permission.
+ * <ul>
+ * <li> If the app holds the corresponding permission, the bit is cleared from the
+ * |redactions| bitmask. </li>
+ * <li> If the app does not hold the corresponding permission, the bit is retained in the
+ * |redactions| bitmask. </li>
+ * </ul>
+ * <li> Connectivity stack then invokes {@link #makeCopy(long)} with the necessary |redactions|
+ * to create a copy to send to the corresponding app. </li>
+ * </ul>
+ * </p>
*
- * @param parcelLocationSensitiveFields Whether the location sensitive fields should be kept
- * when parceling
- * @return Copy of this instance.
+ * @param redactions bitmask of redactions that needs to be performed on this instance.
+ * @return Copy of this instance with the necessary redactions.
* @hide
*/
- @SystemApi
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@NonNull
- default TransportInfo makeCopy(boolean parcelLocationSensitiveFields) {
+ default TransportInfo makeCopy(@NetworkCapabilities.RedactionType long redactions) {
return this;
}
/**
- * Returns whether this TransportInfo type has location sensitive fields or not (helps
- * to determine whether to perform a location permission check or not before sending to
- * apps).
+ * Returns a bitmask of all the applicable redactions (based on the permissions held by the
+ * receiving app) to be performed on this TransportInfo.
*
- * @return {@code true} if this instance contains location sensitive info, {@code false}
- * otherwise.
+ * @return bitmask of redactions applicable on this instance.
+ * @see #makeCopy(long)
* @hide
*/
- @SystemApi
- default boolean hasLocationSensitiveFields() {
- return false;
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ default @NetworkCapabilities.RedactionType long getApplicableRedactions() {
+ return NetworkCapabilities.REDACT_NONE;
}
}
diff --git a/packages/Connectivity/service/Android.bp b/packages/Connectivity/service/Android.bp
index f630cea..1330e71 100644
--- a/packages/Connectivity/service/Android.bp
+++ b/packages/Connectivity/service/Android.bp
@@ -68,6 +68,7 @@
"net-utils-framework-common",
"netd-client",
"PlatformProperties",
+ "service-connectivity-protos",
],
apex_available: [
"//apex_available:platform",
@@ -76,6 +77,21 @@
}
java_library {
+ name: "service-connectivity-protos",
+ proto: {
+ type: "nano",
+ },
+ srcs: [
+ ":system-messages-proto-src",
+ ],
+ libs: ["libprotobuf-java-nano"],
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.tethering",
+ ],
+}
+
+java_library {
name: "service-connectivity",
installable: true,
static_libs: [
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml
index 71674e4..9ff2a22 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml
@@ -78,6 +78,11 @@
<item>1,3</item>
</string-array>
+ <!-- Reserved privileged keepalive slots per transport. -->
+ <integer translatable="false" name="config_reservedPrivilegedKeepaliveSlots">2</integer>
+
+ <!-- Allowed unprivileged keepalive slots per uid. -->
+ <integer translatable="false" name="config_allowedUnprivilegedKeepalivePerUid">2</integer>
<!-- Default value for ConnectivityManager.getMultipathPreference() on metered networks. Actual
device behaviour is controlled by the metered multipath preference in
@@ -89,4 +94,33 @@
Settings.Global.NETWORK_AVOID_BAD_WIFI. This is the default value of that setting. -->
<integer translatable="false" name="config_networkAvoidBadWifi">1</integer>
+ <!-- Array of ConnectivityManager.TYPE_xxxx constants for networks that may only
+ be controlled by systemOrSignature apps. -->
+ <integer-array translatable="false" name="config_protectedNetworks">
+ <item>10</item>
+ <item>11</item>
+ <item>12</item>
+ <item>14</item>
+ <item>15</item>
+ </integer-array>
+
+ <!-- Whether the internal vehicle network should remain active even when no
+ apps requested it. -->
+ <bool name="config_vehicleInternalNetworkAlwaysRequested">false</bool>
+
+
+ <!-- If the hardware supports specially marking packets that caused a wakeup of the
+ main CPU, set this value to the mark used. -->
+ <integer name="config_networkWakeupPacketMark">0</integer>
+
+ <!-- Mask to use when checking skb mark defined in config_networkWakeupPacketMark above. -->
+ <integer name="config_networkWakeupPacketMask">0</integer>
+
+ <!-- Whether/how to notify the user on network switches. See LingerMonitor.java. -->
+ <integer translatable="false" name="config_networkNotifySwitchType">0</integer>
+
+ <!-- What types of network switches to notify. See LingerMonitor.java. -->
+ <string-array translatable="false" name="config_networkNotifySwitches">
+ </string-array>
+
</resources>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml
index 25e19ce..717d08e 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml
@@ -26,6 +26,12 @@
<item type="integer" name="config_networkMeteredMultipathPreference"/>
<item type="array" name="config_networkSupportedKeepaliveCount"/>
<item type="integer" name="config_networkAvoidBadWifi"/>
+ <item type="array" name="config_protectedNetworks"/>
+ <item type="bool" name="config_vehicleInternalNetworkAlwaysRequested"/>
+ <item type="integer" name="config_networkWakeupPacketMark"/>
+ <item type="integer" name="config_networkWakeupPacketMask"/>
+ <item type="integer" name="config_networkNotifySwitchType"/>
+ <item type="array" name="config_networkNotifySwitches"/>
</policy>
</overlayable>
diff --git a/packages/Connectivity/service/jarjar-rules.txt b/packages/Connectivity/service/jarjar-rules.txt
index a7b419b..5caa11b 100644
--- a/packages/Connectivity/service/jarjar-rules.txt
+++ b/packages/Connectivity/service/jarjar-rules.txt
@@ -12,3 +12,6 @@
# the one in com.android.internal.util
rule android.util.IndentingPrintWriter* android.connectivity.util.IndentingPrintWriter@1
rule com.android.internal.util.** com.android.connectivity.util.@1
+
+rule com.android.internal.messages.** com.android.connectivity.messages.@1
+rule com.google.protobuf.** com.android.connectivity.protobuf.@1
diff --git a/services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneIdValidator.java b/packages/Connectivity/service/proto/connectivityproto.proto
similarity index 61%
copy from services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneIdValidator.java
copy to packages/Connectivity/service/proto/connectivityproto.proto
index cab5ad2..a992d7c 100644
--- a/services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneIdValidator.java
+++ b/packages/Connectivity/service/proto/connectivityproto.proto
@@ -14,17 +14,7 @@
* limitations under the License.
*/
-package com.android.server.timezonedetector.location;
+syntax = "proto2";
-import android.annotation.NonNull;
-
-import com.android.i18n.timezone.ZoneInfoDb;
-
-class ZoneInfoDbTimeZoneIdValidator implements
- LocationTimeZoneProvider.TimeZoneIdValidator {
-
- @Override
- public boolean isValid(@NonNull String timeZoneId) {
- return ZoneInfoDb.getInstance().hasTimeZone(timeZoneId);
- }
-}
+// Connectivity protos can be created in this directory. Note this file must be included before
+// building system-messages-proto, otherwise it will not build by itself.
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index d3e4d1c..1c112c6 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -211,7 +211,7 @@
<string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Wi-Fi ሲገናኝ የማረም ሁነታ"</string>
<string name="adb_wireless_error" msgid="721958772149779856">"ስህተት"</string>
<string name="adb_wireless_settings" msgid="2295017847215680229">"ገመድ-አልባ debugging"</string>
- <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"የሚገኙ መሣሪያዎችን ለመመልከትና ለመጠቀም ገመድ-አልባ debuggingን ያብሩ"</string>
+ <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"የሚገኙ መሣሪያዎችን ለመመልከትና ለመጠቀም ገመድ-አልባ ማረምን ያብሩ"</string>
<string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"የQR ኮድን በመጠቀም መሣሪያን ያጣምሩ"</string>
<string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"የQR ኮድ መቃኛን በመጠቀም አዲስ መሣሪያዎችን ያጣምሩ"</string>
<string name="adb_pair_method_code_title" msgid="1122590300445142904">"የማጣመሪያ ኮድን በመጠቀም መሣሪያን ያጣምሩ"</string>
@@ -303,7 +303,7 @@
<string name="adb_warning_title" msgid="7708653449506485728">"የUSB ማረሚያ ይፈቀድ?"</string>
<string name="adb_warning_message" msgid="8145270656419669221">"የUSB አድስ ለግንባታ አላማ ብቻ የታሰበ ነው። ከኮምፒዩተርህ ወደ መሳሪያህ ውሂብ ለመገልበጥ፣ መሣሪያህ ላይ ያለ ማሳወቂያ መተግበሪያዎችን መጫን፣ እና ማስታወሻ ውሂብ ማንበብ ለመጠቀም ይቻላል።"</string>
<string name="adbwifi_warning_title" msgid="727104571653031865">"ገመድ-አልባ debugging ይፈቀድ?"</string>
- <string name="adbwifi_warning_message" msgid="8005936574322702388">"ገመድ-አልባ debugging ለግንባታ አላማዎች ብቻ የታሰበ ነው። ውሂብን ከኮምፒዩተርዎ ወደ መሳሪያዎ ለመቅዳት፣ መሣሪያዎ ላይ ያለማሳወቂያ መተግበሪያዎችን ለመጫን እና የምዝግብ ማስታወሻ ውሂብን ለማንበብ ይጠቀሙበት።"</string>
+ <string name="adbwifi_warning_message" msgid="8005936574322702388">"ገመድ-አልባ ማረም ለግንባታ አላማዎች ብቻ የታሰበ ነው። ውሂብን ከኮምፒዩተርዎ ወደ መሳሪያዎ ለመቅዳት፣ መሣሪያዎ ላይ ያለማሳወቂያ መተግበሪያዎችን ለመጫን እና የምዝግብ ማስታወሻ ውሂብን ለማንበብ ይጠቀሙበት።"</string>
<string name="adb_keys_warning_message" msgid="2968555274488101220">"የዩ ኤስ ቢ ማረም መዳረሻ ከዚህ ቀደም ፍቃድ ከሰጧቸው ኮምፒውተሮች ላይ ይሻሩ?"</string>
<string name="dev_settings_warning_title" msgid="8251234890169074553">"የግንባታ ቅንብሮችን ፍቀድ?"</string>
<string name="dev_settings_warning_message" msgid="37741686486073668">"እነዚህ ቅንብሮች የታሰቡት ለግንባታ አጠቃቀም ብቻ ናቸው። መሳሪያህን እና በሱ ላይ ያሉትን መተግበሪያዎች እንዲበለሹ ወይም በትክክል እንዳይሰሩ ሊያደርጉ ይችላሉ።"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index a0bf4b1..765f31c 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalie (Rot-Grün-Sehschwäche)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalie (Blau-Gelb-Sehschwäche)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Farbkorrektur"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (2333641630205214702) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="2333641630205214702">"Hier kannst du anpassen, wie Farben auf deinem Gerät dargestellt werden sollen. Das kann in folgenden Fällen hilfreich sein:<br/><br/> <ol> <li>&nbsp;Wenn Farben genauer dargestellt werden sollen</li> <li>&nbsp;Wenn du Farben entfernen möchtest, um dich besser konzentrieren zu können</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Außer Kraft gesetzt von \"<xliff:g id="TITLE">%1$s</xliff:g>\""</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Noch etwa <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index a18c0f1..b5da6be 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanopia (gorri-berdeak)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanopia (urdin-horia)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Koloreen zuzenketa"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (2333641630205214702) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="2333641630205214702">"Doitu nola bistaratzen diren koloreak gailuan. Kasu hauetan izan daiteke lagungarria:<br/><br/> <ol> <li>&nbsp;Koloreak zehatzago ikusi nahi dituzunean.</li> <li>&nbsp;Hobeto fokuratzeko, koloreak kendu nahi dituzunean.</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> hobespena gainjarri zaio"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> inguru gelditzen dira"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index fc788b6..a351ca0 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalie (rouge/vert)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalie (bleu-jaune)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Correction des couleurs"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (2333641630205214702) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="2333641630205214702">"Ajustez l\'affichage des couleurs sur votre appareil. Cette option peut vous être utile pour :<br/><br/> <ol> <li>&nbsp;Accentuer la précision des couleurs</li> <li>&nbsp;Supprimer les couleurs pour mieux vous concentrer</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Remplacé par <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Temps restant : environ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 90df3b1..23baeb9 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -131,7 +131,7 @@
<string name="bluetooth_hearingaid_left_pairing_message" msgid="8561855779703533591">"מתבצעת התאמה של מכשיר שמיעה שמאלי…"</string>
<string name="bluetooth_hearingaid_right_pairing_message" msgid="2655347721696331048">"מתבצעת התאמה של מכשיר שמיעה ימני…"</string>
<string name="bluetooth_hearingaid_left_battery_level" msgid="7375621694748104876">"שמאלי - טעינת הסוללה: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <string name="bluetooth_hearingaid_right_battery_level" msgid="1850094448499089312">"ימני - טעינת הסוללה: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_hearingaid_right_battery_level" msgid="1850094448499089312">"ימני – טעינת הסוללה: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi כבוי."</string>
<string name="accessibility_no_wifi" msgid="5297119459491085771">"Wi-Fi מנותק."</string>
<string name="accessibility_wifi_one_bar" msgid="6025652717281815212">"פס אחד של Wi-Fi."</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index e2b62a4..6c6a5d9 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"ಪ್ರೊಟನೋಮಲಿ (ಕೆಂಪು-ಹಸಿರು)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ಟ್ರಿಟನೋಮಲಿ (ನೀಲಿ-ಹಳದಿ)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"ಬಣ್ಣದ ತಿದ್ದುಪಡಿ"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (2333641630205214702) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="2333641630205214702">"ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿ ಬಣ್ಣಗಳು ಹೇಗೆ ಡಿಸ್ಪ್ಲೇ ಆಗುತ್ತವೆ ಎಂಬುದನ್ನು ಹೊಂದಿಸಿ. ನೀವು ಬಣ್ಣಗಳನ್ನು ಹೆಚ್ಚು ನಿಖರವಾಗಿ ನೋಡಲು ಬಯಸಿದಾಗ:<br/><br/> <ol> <li>&nbsp;ಇದು ಸಹಾಯಕವಾಗಿರುತ್ತದೆ</li> <li>&nbsp;ನಿಮಗೆ ಗಮನಹರಿಸಲು ಸಹಾಯ ಮಾಡಲು ಬಣ್ಣಗಳನ್ನು ತೆಗೆದುಹಾಕಿ</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ಮೂಲಕ ಅತಿಕ್ರಮಿಸುತ್ತದೆ"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ಸಮಯ ಬಾಕಿ ಉಳಿದಿದೆ"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 2b351bc..7ec137a 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"प्रोटानेमली (रातो, हरियो)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ट्रिटानोमेली (निलो-पंहेलो)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"रङ्ग सुधार"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (2333641630205214702) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="2333641630205214702">"तपाईंको यन्त्रमा रङहरू कस्ता देखिन्छन् भन्ने कुरा मिलाउनुहोस्। यो सुविधा निम्न अवस्थामा उपयोगी हुन सक्छ:<br/><br/> <ol> <li>&nbsp;तपाईं अझ सटीक रूपमा रङहरू देख्न चाहनुहुन्छ भने</li> <li>&nbsp;तपाईं कुनै कुरामा ध्यान केन्द्रित गर्न रङहरू हटाउन चाहनुहुन्छ भने</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> द्वारा अधिरोहित"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"लगभग <xliff:g id="TIME_REMAINING">%1$s</xliff:g> बाँकी छ"</string>
diff --git a/packages/SettingsLib/res/values-nl/arrays.xml b/packages/SettingsLib/res/values-nl/arrays.xml
index b5d064e..86b34f54 100644
--- a/packages/SettingsLib/res/values-nl/arrays.xml
+++ b/packages/SettingsLib/res/values-nl/arrays.xml
@@ -248,8 +248,8 @@
</string-array>
<string-array name="debug_hw_overdraw_entries">
<item msgid="1968128556747588800">"Uit"</item>
- <item msgid="3033215374382962216">"Gedeeltes met overbelasting weergeven"</item>
- <item msgid="3474333938380896988">"Gebieden voor deuteranomalie weergeven"</item>
+ <item msgid="3033215374382962216">"Gedeelten met overbelasting tonen"</item>
+ <item msgid="3474333938380896988">"Gebieden voor deuteranomalie tonen"</item>
</string-array>
<string-array name="app_process_limit_entries">
<item msgid="794656271086646068">"Standaardlimiet"</item>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index a3a490d..e5e03e1 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -309,7 +309,7 @@
<string name="dev_settings_warning_message" msgid="37741686486073668">"Deze instellingen zijn uitsluitend bedoeld voor ontwikkelingsgebruik. Je apparaat en apps kunnen hierdoor vastlopen of anders reageren."</string>
<string name="verify_apps_over_usb_title" msgid="6031809675604442636">"Apps verifiëren via USB"</string>
<string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Apps die zijn geïnstalleerd via ADB/ADT, controleren op schadelijk gedrag"</string>
- <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Bluetooth-apparaten zonder namen (alleen MAC-adressen) worden weergegeven"</string>
+ <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Bluetooth-apparaten zonder naam (alleen MAC-adressen) worden weergegeven"</string>
<string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Hiermee wordt de functie voor absoluut volume van Bluetooth uitgeschakeld in geval van volumeproblemen met externe apparaten, zoals een onacceptabel hoog volume of geen volumeregeling."</string>
<string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Hierdoor wordt de Gabeldorsche-functiestack voor bluetooth ingeschakeld."</string>
<string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Hiermee wordt de functie voor verbeterde connectiviteit ingeschakeld."</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index f1d4a8a..9ccfb80 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomaly (ਲਾਲ-ਹਰਾ)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomaly (ਨੀਲਾ-ਪੀਲਾ)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"ਰੰਗ ਸੁਧਾਈ"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (2333641630205214702) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="2333641630205214702">"ਆਪਣੇ ਡੀਵਾਈਸ \'ਤੇ ਰੰਗਾਂ ਨੂੰ ਦਿਖਾਉਣ ਦੇ ਤਰੀਕੇ ਨੂੰ ਵਿਵਸਥਿਤ ਕਰੋ। ਇਹ ਉਦੋਂ ਲਾਹੇਵੰਦ ਹੋ ਸਕਦਾ ਹੈ ਜਦੋਂ ਤੁਸੀਂ ਇਹ ਕਰਨਾ ਚਾਹੋਗੇ:<br/><br/> <ol> <li>&nbsp;ਰੰਗਾਂ ਨੂੰ ਹੋਰ ਸਟੀਕਤਾ ਨਾਲ ਦੇਖਣਾ</li> <li>&nbsp;ਫੋਕਸ ਕਰਨ ਵਿੱਚ ਤੁਹਾਡੀ ਮਦਦ ਲਈ ਰੰਗਾਂ ਨੂੰ ਹਟਾਉਣਾ</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ਦੁਆਰਾ ਓਵਰਰਾਈਡ ਕੀਤਾ"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"ਲਗਭਗ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ਬਾਕੀ"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 95ba023..f667c6a 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -424,8 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomali (e kuqe - e gjelbër)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomali (e kaltër - e verdhë)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Korrigjimi i ngjyrës"</string>
- <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (2333641630205214702) -->
- <skip />
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="2333641630205214702">"Rregullo mënyrën se si shfaqen ngjyrat në pajisjen tënde. Kjo mund të jetë e dobishme kur dëshiron që:<br/><br/> <ol> <li>&nbsp;T\'i shikosh ngjyrat me më shumë saktësi</li> <li>&nbsp;T\'i heqësh ngjyrat për të të ndihmuar të fokusohesh</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Mbivendosur nga <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Rreth <xliff:g id="TIME_REMAINING">%1$s</xliff:g> të mbetura"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 0b03d99..4c35d2f 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -424,7 +424,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomaly (سرخ سبز)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomaly (نیلا پیلا)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"رنگ کی اصلاح"</string>
- <string name="accessibility_display_daltonizer_preference_subtitle" msgid="2333641630205214702">"آپ کے آلے پر رنگوں کے ڈسپلے ہونے کے طریقے کو ایڈجسٹ کریں۔ یہ درج ذیل کے لیے مددگار ثابت ہوسکتا ہے :<br/>&ltlt;br/> <ol><li>جب آپ رنگوں کو مزید درست طریقے سے دیکھنا چاہیں </li> <li>&nbsp;فوکس کرنے میں مدد کرنے کے لئے رنگوں کو ہٹائیں </li> </ol>"</string>
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="2333641630205214702">"آپ کے آلے پر رنگوں کے ڈسپلے ہونے کے طریقے کو ایڈجسٹ کریں۔ یہ درج ذیل کے لیے مددگار ثابت ہوسکتا ہے :<br/>&ltlt;br/> <ol><li>جب آپ رنگوں کو مزید درست طریقے سے دیکھنا چاہیں </li> <li>&nbsp;فوکس کرنے میں مدد کرنے کے لئے رنگوں کو ہٹانا چاہیں </li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> کے ذریعہ منسوخ کردیا گیا"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"تقریباً <xliff:g id="TIME_REMAINING">%1$s</xliff:g> باقی ہے"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index cbfd4d8..41ccb16 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -19,7 +19,6 @@
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
-import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
@@ -77,25 +76,23 @@
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR).build();
private final NetworkCallback mNetworkCallback =
new NetworkCallback(NetworkCallback.FLAG_INCLUDE_LOCATION_INFO) {
- @Override
- public void onAvailable(
- Network network, NetworkCapabilities networkCapabilities,
- LinkProperties linkProperties, boolean blocked) {
- boolean isVcnOverWifi =
- networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
- && (Utils.tryGetWifiInfoForVcn(networkCapabilities) != null);
- boolean isWifi =
- networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI);
- if (isVcnOverWifi || isWifi) {
- mNetworks.add(network.getNetId());
- }
- }
-
// Note: onCapabilitiesChanged is guaranteed to be called "immediately" after onAvailable
// and onLinkPropertiesChanged.
@Override
public void onCapabilitiesChanged(
Network network, NetworkCapabilities networkCapabilities) {
+ if (!mNetworks.contains(network.getNetId())) {
+ // New network
+ boolean isVcnOverWifi =
+ networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
+ && (Utils.tryGetWifiInfoForVcn(networkCapabilities) != null);
+ boolean isWifi =
+ networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI);
+ if (isVcnOverWifi || isWifi) {
+ mNetworks.add(network.getNetId());
+ }
+ }
+
WifiInfo wifiInfo = null;
if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
wifiInfo = Utils.tryGetWifiInfoForVcn(networkCapabilities);
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/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index b86ae6d..2b4fef0 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -125,7 +125,6 @@
<uses-permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" />
<uses-permission android:name="android.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS" />
<uses-permission android:name="android.permission.CLEAR_FREEZE_PERIOD" />
- <uses-permission android:name="android.permission.QUERY_USERS" />
<uses-permission android:name="android.permission.MODIFY_QUIET_MODE" />
<uses-permission android:name="android.permission.ACCESS_LOWPAN_STATE"/>
<uses-permission android:name="android.permission.CHANGE_LOWPAN_STATE"/>
@@ -438,6 +437,9 @@
<!-- Permission required for CTS test - ResourceObserverNativeTest -->
<uses-permission android:name="android.permission.REGISTER_MEDIA_RESOURCE_OBSERVER" />
+ <!-- Permission required for CTS test - android.widget.cts.ToastTest -->
+ <uses-permission android:name="android.permission.UNLIMITED_TOASTS" />
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/res/layout/long_screenshot.xml b/packages/SystemUI/res/layout/long_screenshot.xml
index 19bcf95..6d44138c 100644
--- a/packages/SystemUI/res/layout/long_screenshot.xml
+++ b/packages/SystemUI/res/layout/long_screenshot.xml
@@ -48,10 +48,10 @@
<ImageView
android:id="@+id/preview"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_width="0px"
+ android:layout_height="0px"
android:layout_marginBottom="42dp"
- android:layout_marginHorizontal="48dp"
+ android:paddingHorizontal="48dp"
app:layout_constrainedHeight="true"
app:layout_constrainedWidth="true"
app:layout_constraintTop_toBottomOf="@id/save"
@@ -64,8 +64,8 @@
<com.android.systemui.screenshot.CropView
android:id="@+id/crop_view"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_width="0px"
+ android:layout_height="0px"
android:layout_marginBottom="42dp"
app:layout_constrainedHeight="true"
app:layout_constrainedWidth="true"
diff --git a/packages/SystemUI/res/layout/people_space_small_view.xml b/packages/SystemUI/res/layout/people_tile_large_empty.xml
similarity index 63%
copy from packages/SystemUI/res/layout/people_space_small_view.xml
copy to packages/SystemUI/res/layout/people_tile_large_empty.xml
index 7b1abae..1e00307 100644
--- a/packages/SystemUI/res/layout/people_space_small_view.xml
+++ b/packages/SystemUI/res/layout/people_tile_large_empty.xml
@@ -20,40 +20,48 @@
<LinearLayout
android:id="@+id/item"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
android:layout_gravity="center"
+ android:gravity="center"
android:background="@drawable/people_space_tile_view_card"
android:orientation="vertical"
- android:paddingHorizontal="4dp"
- android:paddingVertical="8dp">
+ android:paddingHorizontal="16dp"
+ android:paddingVertical="16dp">
<ImageView
android:id="@+id/person_icon"
android:layout_gravity="center"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1" />
-
- <ImageView
- android:id="@+id/predefined_icon"
- android:layout_gravity="center"
- android:paddingTop="4dp"
- android:layout_width="18dp"
- android:layout_height="22dp"
- android:layout_weight="1" />
+ android:layout_height="wrap_content" />
<TextView
android:id="@+id/name"
android:layout_gravity="center"
- android:paddingTop="4dp"
+ android:paddingTop="14dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_weight="1"
android:ellipsize="end"
- android:maxLines="1"
- android:paddingHorizontal="8dp"
+ android:singleLine="true"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
android:textColor="?android:attr/textColorPrimary"
- android:textSize="14sp" />
+ android:textSize="16sp" />
+ <TextView
+ android:id="@+id/last_interaction"
+ android:layout_gravity="center"
+ android:text="@string/empty_status"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textSize="14sp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:singleLine="true"/>
+ <ImageView
+ android:id="@+id/availability"
+ android:layout_gravity="center"
+ android:layout_width="10dp"
+ android:layout_height="26dp"
+ android:paddingTop="16dp"
+ android:background="@drawable/circle_green_10dp"/>
</LinearLayout>
</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_tile_large_with_content.xml b/packages/SystemUI/res/layout/people_tile_large_with_content.xml
new file mode 100644
index 0000000..f2341b5
--- /dev/null
+++ b/packages/SystemUI/res/layout/people_tile_large_with_content.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ 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.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/item"
+ android:background="@drawable/people_space_tile_view_card"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:padding="16dp"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="start|top"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/person_icon"
+ android:layout_marginStart="-2dp"
+ android:layout_marginTop="-2dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" />
+
+ <ImageView
+ android:id="@+id/availability"
+ android:layout_marginStart="-2dp"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:background="@drawable/circle_green_10dp" />
+ </LinearLayout>
+
+ <TextView
+ android:layout_gravity="center"
+ android:id="@+id/name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="12dp"
+ android:gravity="start"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:text="@string/empty_user_name"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="14sp" />
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingBottom="4dp"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/predefined_icon"
+ android:gravity="start|center_vertical"
+ android:paddingEnd="6dp"
+ android:layout_width="24dp"
+ android:layout_height="18dp" />
+
+ <TextView
+ android:layout_gravity="center"
+ android:id="@+id/subtext"
+ android:gravity="center_vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:text="@string/empty_user_name"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textSize="12sp" />
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/image"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/people_space_content_background"
+ android:gravity="center"
+ android:scaleType="centerCrop" />
+
+ <TextView
+ android:layout_gravity="center"
+ android:id="@+id/text_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:singleLine="false"
+ android:text="@string/empty_status"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="12sp" />
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/people_space_large_avatar_tile.xml b/packages/SystemUI/res/layout/people_tile_medium_empty.xml
similarity index 94%
rename from packages/SystemUI/res/layout/people_space_large_avatar_tile.xml
rename to packages/SystemUI/res/layout/people_tile_medium_empty.xml
index b1a37bf..c35787e 100644
--- a/packages/SystemUI/res/layout/people_space_large_avatar_tile.xml
+++ b/packages/SystemUI/res/layout/people_tile_medium_empty.xml
@@ -35,16 +35,17 @@
android:layout_height="match_parent">
<ImageView
android:id="@+id/person_icon"
- android:layout_width="60dp"
- android:layout_height="60dp"/>
+ android:layout_width="64dp"
+ android:layout_height="64dp"/>
<ImageView
android:id="@+id/availability"
+ android:layout_marginStart="-2dp"
android:layout_width="10dp"
android:layout_height="10dp"
android:background="@drawable/circle_green_10dp"/>
<LinearLayout
android:orientation="vertical"
- android:paddingStart="4dp"
+ android:paddingStart="6dp"
android:gravity="top"
android:layout_width="match_parent"
android:layout_height="wrap_content">
diff --git a/packages/SystemUI/res/layout/people_space_small_avatar_tile.xml b/packages/SystemUI/res/layout/people_tile_medium_with_content.xml
similarity index 71%
rename from packages/SystemUI/res/layout/people_space_small_avatar_tile.xml
rename to packages/SystemUI/res/layout/people_tile_medium_with_content.xml
index 6a606e1..e4e4cd8 100644
--- a/packages/SystemUI/res/layout/people_space_small_avatar_tile.xml
+++ b/packages/SystemUI/res/layout/people_tile_medium_with_content.xml
@@ -27,28 +27,37 @@
android:layout_gravity="center"
android:padding="8dp"
android:layout_width="match_parent"
- android:layout_height="wrap_content">
+ android:layout_height="match_parent">
+
<LinearLayout
android:orientation="horizontal"
android:gravity="top"
+ android:layout_weight="1"
android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <ImageView
- android:gravity="start"
- android:id="@+id/person_icon"
- android:layout_width="56dp"
- android:layout_height="56dp"/>
+ android:layout_height="0dp">
+
+ <ImageView
+ android:gravity="start"
+ android:id="@+id/person_icon"
+ android:layout_marginStart="-2dp"
+ android:layout_marginTop="-2dp"
+ android:layout_width="52dp"
+ android:layout_height="52dp" />
+
<ImageView
android:id="@+id/availability"
+ android:layout_marginStart="-2dp"
android:layout_width="10dp"
android:layout_height="10dp"
- android:background="@drawable/circle_green_10dp"/>
+ android:background="@drawable/circle_green_10dp" />
+
<LinearLayout
android:orientation="vertical"
android:gravity="top|start"
android:paddingStart="12dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
+
<TextView
android:id="@+id/subtext"
android:text="@string/empty_user_name"
@@ -58,38 +67,36 @@
android:paddingBottom="4dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:ellipsize="end"/>
- <LinearLayout
- android:id="@+id/content_background"
- android:background="@drawable/people_space_content_background"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:singleLine="true"
+ android:ellipsize="end" />
+
<ImageView
android:id="@+id/image"
android:gravity="center"
android:background="@drawable/people_space_content_background"
android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:scaleType="centerCrop" />
+
+ <TextView
+ android:id="@+id/text_content"
+ android:text="@string/empty_status"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="12sp"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:scaleType="centerCrop"/>
- </LinearLayout>
- <TextView
- android:id="@+id/text_content"
- android:text="@string/empty_status"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="12sp"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:maxLines="2"
- android:singleLine="false"
- android:ellipsize="end"/>
+ android:maxLines="2"
+ android:singleLine="false"
+ android:ellipsize="end" />
</LinearLayout>
</LinearLayout>
+
<LinearLayout
android:gravity="bottom"
+ android:layout_gravity="center_vertical"
android:orientation="horizontal"
android:paddingTop="4dp"
- android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="wrap_content">
@@ -101,16 +108,17 @@
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
android:textColor="?android:attr/textColorPrimary"
android:textSize="14sp"
- android:maxLines="1"
+ android:singleLine="true"
android:ellipsize="end"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ android:layout_height="wrap_content" />
+
<ImageView
android:id="@+id/predefined_icon"
- android:gravity="end"
+ android:gravity="end|center_vertical"
android:paddingStart="6dp"
android:layout_width="24dp"
- android:layout_height="18dp"/>
+ android:layout_height="18dp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/people_space_small_view.xml b/packages/SystemUI/res/layout/people_tile_small.xml
similarity index 94%
rename from packages/SystemUI/res/layout/people_space_small_view.xml
rename to packages/SystemUI/res/layout/people_tile_small.xml
index 7b1abae..914ee3c 100644
--- a/packages/SystemUI/res/layout/people_space_small_view.xml
+++ b/packages/SystemUI/res/layout/people_tile_small.xml
@@ -25,7 +25,8 @@
android:background="@drawable/people_space_tile_view_card"
android:orientation="vertical"
android:paddingHorizontal="4dp"
- android:paddingVertical="8dp">
+ android:paddingTop="6dp"
+ android:paddingBottom="8dp">
<ImageView
android:id="@+id/person_icon"
@@ -51,7 +52,7 @@
android:layout_weight="1"
android:ellipsize="end"
android:maxLines="1"
- android:paddingHorizontal="8dp"
+ android:paddingHorizontal="4dp"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
android:textColor="?android:attr/textColorPrimary"
android:textSize="14sp" />
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 0076c51..a773b22 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -202,7 +202,7 @@
<color name="global_screenshot_background_protection_start">#40000000</color> <!-- 25% black -->
<!-- Long screenshot UI -->
- <color name="screenshot_crop_scrim">#9444</color>
+ <color name="screenshot_crop_scrim">#6444</color>
<!-- GM2 colors -->
<color name="GM2_grey_50">#F8F9FA</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index b050945..ff4e8e0 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -334,7 +334,6 @@
<dimen name="screenshot_action_container_corner_radius">10dp</dimen>
<dimen name="screenshot_action_container_padding_vertical">6dp</dimen>
<dimen name="screenshot_action_container_margin_horizontal">8dp</dimen>
- <dimen name="screenshot_action_container_padding_left">96dp</dimen>
<dimen name="screenshot_action_container_padding_right">8dp</dimen>
<!-- Radius of the chip background on global screenshot actions -->
<dimen name="screenshot_button_corner_radius">20dp</dimen>
@@ -1355,6 +1354,18 @@
<dimen name="people_space_widget_radius">28dp</dimen>
<dimen name="people_space_image_radius">20dp</dimen>
<dimen name="people_space_widget_background_padding">6dp</dimen>
+ <dimen name="required_width_for_medium">146dp</dimen>
+ <dimen name="required_width_for_large">138dp</dimen>
+ <dimen name="required_height_for_large">182dp</dimen>
+ <dimen name="default_width">146dp</dimen>
+ <dimen name="default_height">92dp</dimen>
+ <dimen name="avatar_size_for_medium">52dp</dimen>
+ <dimen name="max_people_avatar_size_for_large_content">64dp</dimen>
+ <dimen name="max_people_avatar_size">108dp</dimen>
+ <dimen name="name_text_size_for_small">14sp</dimen>
+ <dimen name="name_text_size_for_large">24sp</dimen>
+ <dimen name="content_text_size_for_medium">12sp</dimen>
+ <dimen name="content_text_size_for_large">14sp</dimen>
<!-- Accessibility floating menu -->
<dimen name="accessibility_floating_menu_elevation">5dp</dimen>
diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
index b1e1434..d52a251 100644
--- a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
+++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
@@ -22,8 +22,8 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
-import android.net.ConnectivityManager;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.telephony.PhoneStateListener;
@@ -179,8 +179,8 @@
mBgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
mBgHandler.post(() -> {
- boolean supported = ConnectivityManager.from(mContext).isNetworkSupported(
- ConnectivityManager.TYPE_MOBILE);
+ boolean supported =
+ mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
if (supported && mNetworkSupported.compareAndSet(false, supported)) {
// This will set/remove the listeners appropriately. Note that it will never double
// add the listeners.
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapter.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapter.java
index bb4038e..b4858f4 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapter.java
@@ -18,10 +18,6 @@
import static android.view.View.GONE;
-import static com.android.systemui.accessibility.floatingmenu.AccessibilityTargetAdapter.ItemType.FIRST_ITEM;
-import static com.android.systemui.accessibility.floatingmenu.AccessibilityTargetAdapter.ItemType.LAST_ITEM;
-import static com.android.systemui.accessibility.floatingmenu.AccessibilityTargetAdapter.ItemType.REGULAR_ITEM;
-
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -47,16 +43,16 @@
private final List<AccessibilityTarget> mTargets;
@IntDef({
- FIRST_ITEM,
- REGULAR_ITEM,
- LAST_ITEM
+ AccessibilityTargetAdapter.FIRST_ITEM,
+ AccessibilityTargetAdapter.REGULAR_ITEM,
+ AccessibilityTargetAdapter.LAST_ITEM
})
@Retention(RetentionPolicy.SOURCE)
- @interface ItemType {
- int FIRST_ITEM = 0;
- int REGULAR_ITEM = 1;
- int LAST_ITEM = 2;
- }
+ @interface ItemType {}
+
+ private static final int FIRST_ITEM = 0;
+ private static final int REGULAR_ITEM = 1;
+ private static final int LAST_ITEM = 2;
public AccessibilityTargetAdapter(List<AccessibilityTarget> targets) {
mTargets = targets;
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index d3168c8..a7c1451 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -14,14 +14,8 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.PixelFormat;
import android.metrics.LogMaker;
import android.os.AsyncTask;
-import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
@@ -30,12 +24,6 @@
import android.provider.Settings;
import android.service.voice.VoiceInteractionSession;
import android.util.Log;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.ImageView;
import com.android.internal.app.AssistUtils;
import com.android.internal.app.IVoiceInteractionSessionListener;
@@ -43,7 +31,6 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.settingslib.applications.InterestingConfigChanges;
import com.android.systemui.R;
import com.android.systemui.assist.ui.DefaultUiController;
import com.android.systemui.dagger.SysUISingleton;
@@ -97,8 +84,6 @@
// Note that VERBOSE logging may leak PII (e.g. transcription contents).
private static final boolean VERBOSE = false;
- private static final String ASSIST_ICON_METADATA_NAME =
- "com.android.systemui.action_assist_icon";
private static final String INVOCATION_TIME_MS_KEY = "invocation_time_ms";
private static final String INVOCATION_PHONE_STATE_KEY = "invocation_phone_state";
public static final String INVOCATION_TYPE_KEY = "invocation_type";
@@ -123,69 +108,29 @@
private static final long TIMEOUT_ACTIVITY = 1000;
protected final Context mContext;
- private final WindowManager mWindowManager;
private final AssistDisclosure mAssistDisclosure;
- private final InterestingConfigChanges mInterestingConfigChanges;
private final PhoneStateMonitor mPhoneStateMonitor;
private final AssistHandleBehaviorController mHandleController;
private final UiController mUiController;
protected final Lazy<SysUiState> mSysUiState;
protected final AssistLogger mAssistLogger;
- private AssistOrbContainer mView;
private final DeviceProvisionedController mDeviceProvisionedController;
private final CommandQueue mCommandQueue;
+ private final AssistOrbController mOrbController;
protected final AssistUtils mAssistUtils;
- private final boolean mShouldEnableOrb;
private IVoiceInteractionSessionShowCallback mShowCallback =
new IVoiceInteractionSessionShowCallback.Stub() {
@Override
public void onFailed() throws RemoteException {
- mView.post(mHideRunnable);
+ mOrbController.postHide();
}
@Override
public void onShown() throws RemoteException {
- mView.post(mHideRunnable);
- }
- };
-
- private Runnable mHideRunnable = new Runnable() {
- @Override
- public void run() {
- mView.removeCallbacks(this);
- mView.show(false /* show */, true /* animate */);
- }
- };
-
- private ConfigurationController.ConfigurationListener mConfigurationListener =
- new ConfigurationController.ConfigurationListener() {
- @Override
- public void onConfigChanged(Configuration newConfig) {
- if (!mInterestingConfigChanges.applyNewConfig(mContext.getResources())) {
- return;
- }
- boolean visible = false;
- if (mView != null) {
- visible = mView.isShowing();
- if (mView.isAttachedToWindow()) {
- mWindowManager.removeView(mView);
- }
- }
-
- mView = (AssistOrbContainer) LayoutInflater.from(mContext).inflate(
- R.layout.assist_orb, null);
- mView.setVisibility(View.GONE);
- mView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
- | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
- WindowManager.LayoutParams lp = getLayoutParams();
- mWindowManager.addView(mView, lp);
- if (visible) {
- mView.show(true /* show */, false /* animate */);
- }
+ mOrbController.postHide();
}
};
@@ -205,21 +150,15 @@
mContext = context;
mDeviceProvisionedController = controller;
mCommandQueue = commandQueue;
- mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
mAssistUtils = assistUtils;
mAssistDisclosure = new AssistDisclosure(context, new Handler());
mPhoneStateMonitor = phoneStateMonitor;
mHandleController = handleController;
mAssistLogger = assistLogger;
- configurationController.addCallback(mConfigurationListener);
+ mOrbController = new AssistOrbController(configurationController, context);
registerVoiceInteractionSessionListener();
- mInterestingConfigChanges = new InterestingConfigChanges(ActivityInfo.CONFIG_ORIENTATION
- | ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_UI_MODE
- | ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_ASSETS_PATHS);
- mConfigurationListener.onConfigChanged(context.getResources().getConfiguration());
- mShouldEnableOrb = !ActivityManager.isLowRamDeviceStatic();
mUiController = defaultUiController;
@@ -282,7 +221,7 @@
}
protected boolean shouldShowOrb() {
- return false;
+ return !ActivityManager.isLowRamDeviceStatic();
}
public void startAssist(Bundle args) {
@@ -293,10 +232,8 @@
final boolean isService = assistComponent.equals(getVoiceInteractorComponentName());
if (!isService || (!isVoiceSessionRunning() && shouldShowOrb())) {
- showOrb(assistComponent, isService);
- mView.postDelayed(mHideRunnable, isService
- ? TIMEOUT_SERVICE
- : TIMEOUT_ACTIVITY);
+ mOrbController.showOrb(assistComponent, isService);
+ mOrbController.postHideDelayed(isService ? TIMEOUT_SERVICE : TIMEOUT_ACTIVITY);
}
if (args == null) {
@@ -340,30 +277,6 @@
mAssistUtils.hideCurrentSession();
}
- private WindowManager.LayoutParams getLayoutParams() {
- WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- mContext.getResources().getDimensionPixelSize(R.dimen.assist_orb_scrim_height),
- WindowManager.LayoutParams.TYPE_VOICE_INTERACTION_STARTING,
- WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
- PixelFormat.TRANSLUCENT);
- lp.token = new Binder();
- lp.gravity = Gravity.BOTTOM | Gravity.START;
- lp.setTitle("AssistPreviewPanel");
- lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
- | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
- return lp;
- }
-
- private void showOrb(@NonNull ComponentName assistComponent, boolean isService) {
- maybeSwapSearchIcon(assistComponent, isService);
- if (mShouldEnableOrb) {
- mView.show(true /* show */, true /* animate */);
- }
- }
-
private void startAssistInternal(Bundle args, @NonNull ComponentName assistComponent,
boolean isService) {
if (isService) {
@@ -440,44 +353,6 @@
return mAssistUtils.isSessionRunning();
}
- private void maybeSwapSearchIcon(@NonNull ComponentName assistComponent, boolean isService) {
- replaceDrawable(mView.getOrb().getLogo(), assistComponent, ASSIST_ICON_METADATA_NAME,
- isService);
- }
-
- public void replaceDrawable(ImageView v, ComponentName component, String name,
- boolean isService) {
- if (component != null) {
- try {
- PackageManager packageManager = mContext.getPackageManager();
- // Look for the search icon specified in the activity meta-data
- Bundle metaData = isService
- ? packageManager.getServiceInfo(
- component, PackageManager.GET_META_DATA).metaData
- : packageManager.getActivityInfo(
- component, PackageManager.GET_META_DATA).metaData;
- if (metaData != null) {
- int iconResId = metaData.getInt(name);
- if (iconResId != 0) {
- Resources res = packageManager.getResourcesForApplication(
- component.getPackageName());
- v.setImageDrawable(res.getDrawable(iconResId));
- return;
- }
- }
- } catch (PackageManager.NameNotFoundException e) {
- if (VERBOSE) {
- Log.v(TAG, "Assistant component "
- + component.flattenToShortString() + " not found");
- }
- } catch (Resources.NotFoundException nfe) {
- Log.w(TAG, "Failed to swap drawable from "
- + component.flattenToShortString(), nfe);
- }
- }
- v.setImageDrawable(null);
- }
-
protected AssistHandleBehaviorController getHandleBehaviorController() {
return mHandleController;
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistOrbContainer.java b/packages/SystemUI/src/com/android/systemui/assist/AssistOrbContainer.java
index f78436a..fe48c26 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistOrbContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistOrbContainer.java
@@ -55,14 +55,17 @@
mOrb = (AssistOrbView) findViewById(R.id.assist_orb);
}
- public void show(final boolean show, boolean animate) {
+ public void show(final boolean show, boolean animate, Runnable onDone) {
if (show) {
if (getVisibility() != View.VISIBLE) {
setVisibility(View.VISIBLE);
if (animate) {
- startEnterAnimation();
+ startEnterAnimation(onDone);
} else {
reset();
+ if (onDone != null) {
+ onDone.run();
+ }
}
}
} else {
@@ -72,10 +75,16 @@
public void run() {
mAnimatingOut = false;
setVisibility(View.GONE);
+ if (onDone != null) {
+ onDone.run();
+ }
}
});
} else {
setVisibility(View.GONE);
+ if (onDone != null) {
+ onDone.run();
+ }
}
}
}
@@ -87,7 +96,7 @@
mNavbarScrim.setAlpha(1f);
}
- private void startEnterAnimation() {
+ private void startEnterAnimation(Runnable onDone) {
if (mAnimatingOut) {
return;
}
@@ -106,7 +115,8 @@
.alpha(1f)
.setDuration(300)
.setStartDelay(0)
- .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+ .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
+ .withEndAction(onDone);
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistOrbController.java b/packages/SystemUI/src/com/android/systemui/assist/AssistOrbController.java
new file mode 100644
index 0000000..81a13a2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistOrbController.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.assist;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.os.Binder;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.ImageView;
+
+import com.android.settingslib.applications.InterestingConfigChanges;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+
+/**
+ * AssistOrbController controls the showing and hiding of the assistant orb.
+ */
+public class AssistOrbController {
+ private static final String ASSIST_ICON_METADATA_NAME =
+ "com.android.systemui.action_assist_icon";
+ private static final String TAG = "AssistOrbController";
+ private static final boolean VERBOSE = false;
+
+ private final InterestingConfigChanges mInterestingConfigChanges;
+ private AssistOrbContainer mView;
+ private final Context mContext;
+ private final WindowManager mWindowManager;
+
+ private Runnable mHideRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mView.removeCallbacks(this);
+ mView.show(false /* show */, true /* animate */, () -> {
+ mWindowManager.removeView(mView);
+ });
+ }
+ };
+
+ private ConfigurationController.ConfigurationListener mConfigurationListener =
+ new ConfigurationController.ConfigurationListener() {
+ @Override
+ public void onConfigChanged(Configuration newConfig) {
+ if (!mInterestingConfigChanges.applyNewConfig(mContext.getResources())) {
+ return;
+ }
+ boolean visible = false;
+ if (mView != null) {
+ visible = mView.isShowing();
+ if (mView.isAttachedToWindow()) {
+ mWindowManager.removeView(mView);
+ }
+ }
+
+ if (visible) {
+ showOrb(false);
+ }
+ }
+ };
+
+ AssistOrbController(ConfigurationController configurationController, Context context) {
+ mContext = context;
+ mWindowManager = mContext.getSystemService(WindowManager.class);
+ mInterestingConfigChanges = new InterestingConfigChanges(ActivityInfo.CONFIG_ORIENTATION
+ | ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_UI_MODE
+ | ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_ASSETS_PATHS);
+
+ configurationController.addCallback(mConfigurationListener);
+ mConfigurationListener.onConfigChanged(context.getResources().getConfiguration());
+ }
+
+ public void postHide() {
+ mView.post(mHideRunnable);
+ }
+
+ public void postHideDelayed(long delayMs) {
+ mView.postDelayed(mHideRunnable, delayMs);
+ }
+
+ private void showOrb(boolean animated) {
+ if (mView == null) {
+ mView = (AssistOrbContainer) LayoutInflater.from(mContext).inflate(
+ R.layout.assist_orb, null);
+ mView.setVisibility(View.GONE);
+ mView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
+ }
+ if (!mView.isAttachedToWindow()) {
+ WindowManager.LayoutParams params = getLayoutParams();
+ mWindowManager.addView(mView, params);
+ }
+ mView.show(true, animated, null);
+ }
+
+ private WindowManager.LayoutParams getLayoutParams() {
+ WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ mContext.getResources().getDimensionPixelSize(R.dimen.assist_orb_scrim_height),
+ WindowManager.LayoutParams.TYPE_VOICE_INTERACTION_STARTING,
+ WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+ PixelFormat.TRANSLUCENT);
+ lp.token = new Binder();
+ lp.gravity = Gravity.BOTTOM | Gravity.START;
+ lp.setTitle("AssistPreviewPanel");
+ lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
+ | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
+ return lp;
+ }
+
+ public void showOrb(@NonNull ComponentName assistComponent, boolean isService) {
+ showOrb(true);
+ maybeSwapSearchIcon(assistComponent, isService);
+ }
+
+ private void maybeSwapSearchIcon(@NonNull ComponentName assistComponent, boolean isService) {
+ replaceDrawable(mView.getOrb().getLogo(), assistComponent, ASSIST_ICON_METADATA_NAME,
+ isService);
+ }
+
+ public void replaceDrawable(ImageView v, ComponentName component, String name,
+ boolean isService) {
+ if (component != null) {
+ try {
+ PackageManager packageManager = mContext.getPackageManager();
+ // Look for the search icon specified in the activity meta-data
+ Bundle metaData = isService
+ ? packageManager.getServiceInfo(
+ component, PackageManager.GET_META_DATA).metaData
+ : packageManager.getActivityInfo(
+ component, PackageManager.GET_META_DATA).metaData;
+ if (metaData != null) {
+ int iconResId = metaData.getInt(name);
+ if (iconResId != 0) {
+ Resources res = packageManager.getResourcesForApplication(
+ component.getPackageName());
+ v.setImageDrawable(res.getDrawable(iconResId));
+ return;
+ }
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ if (VERBOSE) {
+ Log.v(TAG, "Assistant component "
+ + component.flattenToShortString() + " not found");
+ }
+ } catch (Resources.NotFoundException nfe) {
+ Log.w(TAG, "Failed to swap drawable from "
+ + component.flattenToShortString(), nfe);
+ }
+ }
+ v.setImageDrawable(null);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 08e5d56..1f652db 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -33,6 +33,7 @@
@NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager;
private boolean mForceShow;
+ private boolean mQsExpanded;
protected UdfpsKeyguardViewController(
@NonNull UdfpsKeyguardView view,
@@ -64,7 +65,7 @@
protected void onViewDetached() {
super.onViewDetached();
mStatusBarStateController.removeCallback(mStateListener);
- mAlternateAuthInterceptor.reset();
+ mAlternateAuthInterceptor.resetForceShow();
mKeyguardViewManager.setAlternateAuthInterceptor(null);
}
@@ -100,6 +101,11 @@
if (mForceShow) {
return false;
}
+
+ if (mQsExpanded) {
+ return true;
+ }
+
return super.shouldPauseAuth();
}
@@ -130,7 +136,7 @@
}
@Override
- public boolean reset() {
+ public boolean resetForceShow() {
if (!mForceShow) {
return false;
}
@@ -140,7 +146,7 @@
}
@Override
- public boolean isShowingAlternativeAuth() {
+ public boolean isShowingAlternateAuth() {
return mForceShow;
}
@@ -150,6 +156,12 @@
}
@Override
+ public void setQsExpanded(boolean expanded) {
+ mQsExpanded = expanded;
+ updatePauseAuth();
+ }
+
+ @Override
public void dump(PrintWriter pw) {
pw.print(getTag());
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 461a730..553e5a7 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -50,6 +50,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
@@ -356,7 +357,8 @@
filter.addAction(TelephonyManager.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter);
- mHasTelephony = connectivityManager.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+ mHasTelephony =
+ context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
// get notified of phone state changes
telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index f9c2a2a..93ce5a8 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -18,42 +18,23 @@
import static android.app.Notification.CATEGORY_MISSED_CALL;
import static android.app.Notification.EXTRA_MESSAGES;
-import static android.app.people.ConversationStatus.ACTIVITY_ANNIVERSARY;
-import static android.app.people.ConversationStatus.ACTIVITY_AUDIO;
-import static android.app.people.ConversationStatus.ACTIVITY_BIRTHDAY;
-import static android.app.people.ConversationStatus.ACTIVITY_GAME;
-import static android.app.people.ConversationStatus.ACTIVITY_LOCATION;
-import static android.app.people.ConversationStatus.ACTIVITY_NEW_STORY;
-import static android.app.people.ConversationStatus.ACTIVITY_UPCOMING_BIRTHDAY;
-import static android.app.people.ConversationStatus.ACTIVITY_VIDEO;
-import static android.app.people.ConversationStatus.AVAILABILITY_AVAILABLE;
-import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT;
-import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH;
-import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT;
-import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH;
-import android.annotation.Nullable;
import android.app.INotificationManager;
import android.app.Notification;
-import android.app.PendingIntent;
import android.app.people.ConversationChannel;
-import android.app.people.ConversationStatus;
import android.app.people.IPeopleManager;
import android.app.people.PeopleSpaceTile;
import android.appwidget.AppWidgetManager;
import android.content.Context;
-import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
-import android.content.res.Configuration;
import android.database.Cursor;
import android.database.SQLException;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
import android.icu.text.MeasureFormat;
import android.icu.util.Measure;
import android.icu.util.MeasureUnit;
@@ -66,10 +47,7 @@
import android.service.notification.ConversationChannelWrapper;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
-import android.util.IconDrawableFactory;
import android.util.Log;
-import android.util.TypedValue;
-import android.view.View;
import android.widget.RemoteViews;
import androidx.preference.PreferenceManager;
@@ -81,8 +59,6 @@
import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.R;
import com.android.systemui.people.widget.AppWidgetOptionsHelper;
-import com.android.systemui.people.widget.LaunchConversationActivity;
-import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
import com.android.systemui.people.widget.PeopleTileKey;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -99,10 +75,7 @@
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
-import java.util.Optional;
import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -123,18 +96,6 @@
public static final PeopleTileKey EMPTY_KEY =
new PeopleTileKey(EMPTY_STRING, INVALID_USER_ID, EMPTY_STRING);
- public static final int SMALL_LAYOUT = 0;
- public static final int MEDIUM_LAYOUT = 1;
- @VisibleForTesting
- static final int REQUIRED_WIDTH_FOR_MEDIUM = 146;
- private static final int AVATAR_SIZE_FOR_MEDIUM = 56;
- private static final int DEFAULT_WIDTH = 146;
- private static final int DEFAULT_HEIGHT = 92;
-
- private static final Pattern DOUBLE_EXCLAMATION_PATTERN = Pattern.compile("[!][!]+");
- private static final Pattern DOUBLE_QUESTION_PATTERN = Pattern.compile("[?][?]+");
- private static final Pattern ANY_DOUBLE_MARK_PATTERN = Pattern.compile("[!?][!?]+");
- private static final Pattern MIXED_MARK_PATTERN = Pattern.compile("![?].*|.*[?]!");
/** Represents whether {@link StatusBarNotification} was posted or removed. */
public enum NotificationAction {
@@ -336,363 +297,6 @@
.build();
}
- /** Creates a {@link RemoteViews} for {@code tile}. */
- public static RemoteViews createRemoteViews(Context context,
- PeopleSpaceTile tile, int appWidgetId, Bundle options) {
- int layoutSize = getLayoutSize(context, options);
- RemoteViews viewsForTile = getViewForTile(context, tile, layoutSize);
- int maxAvatarSize = getMaxAvatarSize(context, options, layoutSize);
- RemoteViews views = setCommonRemoteViewsFields(context, viewsForTile, tile, maxAvatarSize);
- return setLaunchIntents(context, views, tile, appWidgetId);
- }
-
- /**
- * The prioritization for the {@code tile} content is missed calls, followed by notification
- * content, then birthdays, then the most recent status, and finally last interaction.
- */
- private static RemoteViews getViewForTile(Context context, PeopleSpaceTile tile,
- int layoutSize) {
- if (Objects.equals(tile.getNotificationCategory(), CATEGORY_MISSED_CALL)) {
- if (DEBUG) Log.d(TAG, "Create missed call view");
- return createMissedCallRemoteViews(context, tile, layoutSize);
- }
-
- if (tile.getNotificationKey() != null) {
- if (DEBUG) Log.d(TAG, "Create notification view");
- return createNotificationRemoteViews(context, tile, layoutSize);
- }
-
- // TODO: Add sorting when we expose timestamp of statuses.
- List<ConversationStatus> statusesForEntireView =
- tile.getStatuses() == null ? Arrays.asList() : tile.getStatuses().stream().filter(
- c -> isStatusValidForEntireStatusView(c)).collect(Collectors.toList());
- ConversationStatus birthdayStatus = getBirthdayStatus(tile, statusesForEntireView);
- if (birthdayStatus != null) {
- if (DEBUG) Log.d(TAG, "Create birthday view");
- return createStatusRemoteViews(context, birthdayStatus, layoutSize);
- }
-
- if (!statusesForEntireView.isEmpty()) {
- if (DEBUG) {
- Log.d(TAG,
- "Create status view for: " + statusesForEntireView.get(0).getActivity());
- }
- return createStatusRemoteViews(context, statusesForEntireView.get(0), layoutSize);
- }
-
- return createLastInteractionRemoteViews(context, tile, layoutSize);
- }
-
- /** Calculates the best layout relative to the size in {@code options}. */
- private static int getLayoutSize(Context context, Bundle options) {
- int display = context.getResources().getConfiguration().orientation;
- int width = display == Configuration.ORIENTATION_PORTRAIT
- ? options.getInt(OPTION_APPWIDGET_MIN_WIDTH, DEFAULT_WIDTH) : options.getInt(
- OPTION_APPWIDGET_MAX_WIDTH, DEFAULT_WIDTH);
- int height = display == Configuration.ORIENTATION_PORTRAIT ? options.getInt(
- OPTION_APPWIDGET_MAX_HEIGHT, DEFAULT_HEIGHT)
- : options.getInt(OPTION_APPWIDGET_MIN_HEIGHT, DEFAULT_HEIGHT);
- // Small layout used below a certain minimum width with any height.
- if (width < REQUIRED_WIDTH_FOR_MEDIUM) {
- if (DEBUG) Log.d(TAG, "Small view for width: " + width + " height: " + height);
- return SMALL_LAYOUT;
- }
- if (DEBUG) Log.d(TAG, "Medium view for width: " + width + " height: " + height);
- return MEDIUM_LAYOUT;
- }
-
- /** Returns the max avatar size for {@code layoutSize} under the current {@code options}. */
- private static int getMaxAvatarSize(Context context, Bundle options, int layoutSize) {
- int avatarHeightSpace = AVATAR_SIZE_FOR_MEDIUM;
- int avatarWidthSpace = AVATAR_SIZE_FOR_MEDIUM;
-
- if (layoutSize == SMALL_LAYOUT) {
- int display = context.getResources().getConfiguration().orientation;
- int width = display == Configuration.ORIENTATION_PORTRAIT
- ? options.getInt(OPTION_APPWIDGET_MIN_WIDTH, DEFAULT_WIDTH) : options.getInt(
- OPTION_APPWIDGET_MAX_WIDTH, DEFAULT_WIDTH);
- int height = display == Configuration.ORIENTATION_PORTRAIT ? options.getInt(
- OPTION_APPWIDGET_MAX_HEIGHT, DEFAULT_HEIGHT)
- : options.getInt(OPTION_APPWIDGET_MIN_HEIGHT, DEFAULT_HEIGHT);
- avatarHeightSpace = height - (8 + 4 + 18 + 8);
- avatarWidthSpace = width - (4 + 4);
- }
- if (DEBUG) Log.d(TAG, "Height: " + avatarHeightSpace + " width: " + avatarWidthSpace);
- return Math.min(avatarHeightSpace, avatarWidthSpace);
- }
-
- @Nullable
- private static ConversationStatus getBirthdayStatus(PeopleSpaceTile tile,
- List<ConversationStatus> statuses) {
- Optional<ConversationStatus> birthdayStatus = statuses.stream().filter(
- c -> c.getActivity() == ACTIVITY_BIRTHDAY).findFirst();
- if (birthdayStatus.isPresent()) {
- return birthdayStatus.get();
- }
- if (!TextUtils.isEmpty(tile.getBirthdayText())) {
- return new ConversationStatus.Builder(tile.getId(), ACTIVITY_BIRTHDAY).build();
- }
-
- return null;
- }
-
- /**
- * Returns whether a {@code status} should have its own entire templated view.
- *
- * <p>A status may still be shown on the view (for example, as a new story ring) even if it's
- * not valid to compose an entire view.
- */
- private static boolean isStatusValidForEntireStatusView(ConversationStatus status) {
- switch (status.getActivity()) {
- // Birthday & Anniversary don't require text provided or icon provided.
- case ACTIVITY_BIRTHDAY:
- case ACTIVITY_ANNIVERSARY:
- return true;
- default:
- // For future birthday, location, new story, video, music, game, and other, the
- // app must provide either text or an icon.
- return !TextUtils.isEmpty(status.getDescription())
- || status.getIcon() != null;
- }
- }
-
- private static RemoteViews createStatusRemoteViews(Context context, ConversationStatus status,
- int layoutSize) {
- int layout = layoutSize == SMALL_LAYOUT ? R.layout.people_space_small_view
- : R.layout.people_space_small_avatar_tile;
- RemoteViews views = new RemoteViews(context.getPackageName(), layout);
- CharSequence statusText = status.getDescription();
- if (TextUtils.isEmpty(statusText)) {
- statusText = getStatusTextByType(context, status.getActivity());
- }
- views.setViewVisibility(R.id.subtext, View.GONE);
- views.setViewVisibility(R.id.text_content, View.VISIBLE);
- TypedValue typedValue = new TypedValue();
- context.getTheme().resolveAttribute(android.R.attr.textColorSecondary, typedValue, true);
- int secondaryTextColor = context.getColor(typedValue.resourceId);
- views.setInt(R.id.text_content, "setTextColor", secondaryTextColor);
- views.setTextViewText(R.id.text_content, statusText);
- Icon statusIcon = status.getIcon();
- if (statusIcon != null) {
- views.setImageViewIcon(R.id.image, statusIcon);
- views.setBoolean(R.id.content_background, "setClipToOutline", true);
- } else {
- views.setViewVisibility(R.id.content_background, View.GONE);
- }
- // TODO: Set status pre-defined icons
- views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_person);
- ensurePredefinedIconVisibleOnSmallView(views, layoutSize);
- return views;
- }
-
- private static String getStatusTextByType(Context context, int activity) {
- switch (activity) {
- case ACTIVITY_BIRTHDAY:
- return context.getString(R.string.birthday_status);
- case ACTIVITY_UPCOMING_BIRTHDAY:
- return context.getString(R.string.upcoming_birthday_status);
- case ACTIVITY_ANNIVERSARY:
- return context.getString(R.string.anniversary_status);
- case ACTIVITY_LOCATION:
- return context.getString(R.string.location_status);
- case ACTIVITY_NEW_STORY:
- return context.getString(R.string.new_story_status);
- case ACTIVITY_VIDEO:
- return context.getString(R.string.video_status);
- case ACTIVITY_AUDIO:
- return context.getString(R.string.audio_status);
- case ACTIVITY_GAME:
- return context.getString(R.string.game_status);
- default:
- return EMPTY_STRING;
- }
- }
-
- private static RemoteViews setCommonRemoteViewsFields(Context context, RemoteViews views,
- PeopleSpaceTile tile, int maxAvatarSize) {
- try {
- boolean isAvailable =
- tile.getStatuses() != null && tile.getStatuses().stream().anyMatch(
- c -> c.getAvailability() == AVAILABILITY_AVAILABLE);
- if (isAvailable) {
- views.setViewVisibility(R.id.availability, View.VISIBLE);
- } else {
- views.setViewVisibility(R.id.availability, View.GONE);
- }
- boolean hasNewStory =
- tile.getStatuses() != null && tile.getStatuses().stream().anyMatch(
- c -> c.getActivity() == ACTIVITY_NEW_STORY);
- views.setTextViewText(R.id.name, tile.getUserName().toString());
- views.setBoolean(R.id.content_background, "setClipToOutline", true);
- Icon icon = tile.getUserIcon();
- PeopleStoryIconFactory storyIcon = new PeopleStoryIconFactory(context,
- context.getPackageManager(),
- IconDrawableFactory.newInstance(context, false),
- maxAvatarSize);
- Drawable drawable = icon.loadDrawable(context);
- Drawable personDrawable = storyIcon.getPeopleTileDrawable(drawable,
- tile.getPackageName(), getUserId(tile), tile.isImportantConversation(),
- hasNewStory);
- Bitmap bitmap = convertDrawableToBitmap(personDrawable);
- views.setImageViewBitmap(R.id.person_icon, bitmap);
-
- return views;
- } catch (Exception e) {
- Log.e(TAG, "Failed to set common fields: " + e);
- }
- return views;
- }
-
- private static RemoteViews setLaunchIntents(Context context, RemoteViews views,
- PeopleSpaceTile tile, int appWidgetId) {
- try {
- Intent activityIntent = new Intent(context, LaunchConversationActivity.class);
- activityIntent.addFlags(
- Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_CLEAR_TASK
- | Intent.FLAG_ACTIVITY_NO_HISTORY
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- activityIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_TILE_ID, tile.getId());
- activityIntent.putExtra(
- PeopleSpaceWidgetProvider.EXTRA_PACKAGE_NAME, tile.getPackageName());
- activityIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_USER_HANDLE,
- tile.getUserHandle());
- activityIntent.putExtra(
- PeopleSpaceWidgetProvider.EXTRA_NOTIFICATION_KEY, tile.getNotificationKey());
- views.setOnClickPendingIntent(R.id.item, PendingIntent.getActivity(
- context,
- appWidgetId,
- activityIntent,
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE));
- return views;
- } catch (Exception e) {
- Log.e(TAG, "Failed to add launch intents: " + e);
- }
-
- return views;
- }
-
- private static RemoteViews createMissedCallRemoteViews(Context context,
- PeopleSpaceTile tile, int layoutSize) {
- int layout = layoutSize == SMALL_LAYOUT ? R.layout.people_space_small_view
- : R.layout.people_space_small_avatar_tile;
- RemoteViews views = new RemoteViews(context.getPackageName(), layout);
- views.setViewVisibility(R.id.subtext, View.GONE);
- views.setViewVisibility(R.id.text_content, View.VISIBLE);
- views.setTextViewText(R.id.text_content, tile.getNotificationContent());
- views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_phone_missed);
- ensurePredefinedIconVisibleOnSmallView(views, layoutSize);
- views.setBoolean(R.id.content_background, "setClipToOutline", true);
- return views;
- }
-
- private static void ensurePredefinedIconVisibleOnSmallView(RemoteViews views, int layoutSize) {
- if (layoutSize == SMALL_LAYOUT) {
- views.setViewVisibility(R.id.name, View.GONE);
- views.setViewVisibility(R.id.predefined_icon, View.VISIBLE);
- }
- }
-
- private static RemoteViews createNotificationRemoteViews(Context context,
- PeopleSpaceTile tile, int layoutSize) {
- int layout = layoutSize == SMALL_LAYOUT ? R.layout.people_space_small_view
- : R.layout.people_space_small_avatar_tile;
- RemoteViews views = new RemoteViews(context.getPackageName(), layout);
- if (layoutSize != MEDIUM_LAYOUT) {
- views.setViewVisibility(R.id.name, View.GONE);
- views.setViewVisibility(R.id.predefined_icon, View.VISIBLE);
- }
- Uri image = tile.getNotificationDataUri();
- ensurePredefinedIconVisibleOnSmallView(views, layoutSize);
- if (image != null) {
- // TODO: Use NotificationInlineImageCache
- views.setImageViewUri(R.id.image, image);
- views.setViewVisibility(R.id.content_background, View.VISIBLE);
- views.setBoolean(R.id.content_background, "setClipToOutline", true);
- views.setViewVisibility(R.id.text_content, View.GONE);
- views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_photo_camera);
- } else {
- CharSequence content = tile.getNotificationContent();
- views = setPunctuationRemoteViewsFields(views, content);
- views.setTextViewText(R.id.text_content, tile.getNotificationContent());
- // TODO: Measure max lines from height.
- views.setInt(R.id.text_content, "setMaxLines", 2);
- TypedValue typedValue = new TypedValue();
- context.getTheme().resolveAttribute(android.R.attr.textColorPrimary, typedValue, true);
- int primaryTextColor = context.getColor(typedValue.resourceId);
- views.setInt(R.id.text_content, "setTextColor", primaryTextColor);
- views.setViewVisibility(R.id.text_content, View.VISIBLE);
- views.setViewVisibility(R.id.content_background, View.GONE);
- views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_message);
- }
- // TODO: Set subtext as Group Sender name once storing the name in PeopleSpaceTile.
- views.setViewVisibility(R.id.subtext, View.GONE);
- return views;
- }
-
- private static RemoteViews createLastInteractionRemoteViews(Context context,
- PeopleSpaceTile tile, int layoutSize) {
- int layout = layoutSize == SMALL_LAYOUT ? R.layout.people_space_small_view
- : R.layout.people_space_large_avatar_tile;
- RemoteViews views = new RemoteViews(context.getPackageName(), layout);
- if (layoutSize == SMALL_LAYOUT) {
- views.setViewVisibility(R.id.name, View.VISIBLE);
- views.setViewVisibility(R.id.predefined_icon, View.GONE);
- }
- String status = PeopleSpaceUtils.getLastInteractionString(
- context, tile.getLastInteractionTimestamp());
- views.setTextViewText(R.id.last_interaction, status);
- return views;
- }
-
- private static RemoteViews setPunctuationRemoteViewsFields(
- RemoteViews views, CharSequence content) {
- String punctuation = getBackgroundTextFromMessage(content.toString());
- int visibility = View.GONE;
- if (punctuation != null) {
- visibility = View.VISIBLE;
- }
- views.setTextViewText(R.id.punctuation1, punctuation);
- views.setTextViewText(R.id.punctuation2, punctuation);
- views.setTextViewText(R.id.punctuation3, punctuation);
- views.setTextViewText(R.id.punctuation4, punctuation);
- views.setTextViewText(R.id.punctuation5, punctuation);
- views.setTextViewText(R.id.punctuation6, punctuation);
-
- views.setViewVisibility(R.id.punctuation1, visibility);
- views.setViewVisibility(R.id.punctuation2, visibility);
- views.setViewVisibility(R.id.punctuation3, visibility);
- views.setViewVisibility(R.id.punctuation4, visibility);
- views.setViewVisibility(R.id.punctuation5, visibility);
- views.setViewVisibility(R.id.punctuation6, visibility);
-
- return views;
- }
-
- /** Gets character for tile background decoration based on notification content. */
- @VisibleForTesting
- static String getBackgroundTextFromMessage(String message) {
- if (!ANY_DOUBLE_MARK_PATTERN.matcher(message).find()) {
- return null;
- }
- if (MIXED_MARK_PATTERN.matcher(message).find()) {
- return "!?";
- }
- Matcher doubleQuestionMatcher = DOUBLE_QUESTION_PATTERN.matcher(message);
- if (!doubleQuestionMatcher.find()) {
- return "!";
- }
- Matcher doubleExclamationMatcher = DOUBLE_EXCLAMATION_PATTERN.matcher(message);
- if (!doubleExclamationMatcher.find()) {
- return "?";
- }
- // If we have both "!!" and "??", return the one that comes first.
- if (doubleQuestionMatcher.start() < doubleExclamationMatcher.start()) {
- return "?";
- }
- return "!";
- }
-
/** Gets the most recent {@link Notification.MessagingStyle.Message} from the notification. */
@VisibleForTesting
public static Notification.MessagingStyle.Message getLastMessagingStyleMessage(
@@ -917,7 +521,8 @@
public static void updateAppWidgetViews(AppWidgetManager appWidgetManager,
Context context, int appWidgetId, PeopleSpaceTile tile, Bundle options) {
if (DEBUG) Log.d(TAG, "Widget: " + appWidgetId + ", " + tile.getUserName());
- RemoteViews views = createRemoteViews(context, tile, appWidgetId, options);
+ RemoteViews views = new PeopleTileViewHelper(context, tile, appWidgetId,
+ options).getViews();
// Tell the AppWidgetManager to perform an update on the current app widget.
appWidgetManager.updateAppWidget(appWidgetId, views);
@@ -1005,7 +610,7 @@
if (DEBUG) Log.i(TAG, "Returning tile preview for shortcutId: " + shortcutId);
Bundle bundle = new Bundle();
- return PeopleSpaceUtils.createRemoteViews(context, augmentedTile, 0, bundle);
+ return new PeopleTileViewHelper(context, augmentedTile, 0, bundle).getViews();
}
/** Returns the userId associated with a {@link PeopleSpaceTile} */
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
new file mode 100644
index 0000000..ae81ab04
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
@@ -0,0 +1,553 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.people;
+
+import static android.app.Notification.CATEGORY_MISSED_CALL;
+import static android.app.people.ConversationStatus.ACTIVITY_ANNIVERSARY;
+import static android.app.people.ConversationStatus.ACTIVITY_AUDIO;
+import static android.app.people.ConversationStatus.ACTIVITY_BIRTHDAY;
+import static android.app.people.ConversationStatus.ACTIVITY_GAME;
+import static android.app.people.ConversationStatus.ACTIVITY_LOCATION;
+import static android.app.people.ConversationStatus.ACTIVITY_NEW_STORY;
+import static android.app.people.ConversationStatus.ACTIVITY_UPCOMING_BIRTHDAY;
+import static android.app.people.ConversationStatus.ACTIVITY_VIDEO;
+import static android.app.people.ConversationStatus.AVAILABILITY_AVAILABLE;
+import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT;
+import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH;
+import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT;
+import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH;
+
+import static com.android.systemui.people.PeopleSpaceUtils.convertDrawableToBitmap;
+import static com.android.systemui.people.PeopleSpaceUtils.getUserId;
+
+import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.app.people.ConversationStatus;
+import android.app.people.PeopleSpaceTile;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.IconDrawableFactory;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.RemoteViews;
+import android.widget.TextView;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.R;
+import com.android.systemui.people.widget.LaunchConversationActivity;
+import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+class PeopleTileViewHelper {
+ /** Turns on debugging information about People Space. */
+ public static final boolean DEBUG = true;
+ private static final String TAG = "PeopleTileView";
+
+ public static final int LAYOUT_SMALL = 0;
+ public static final int LAYOUT_MEDIUM = 1;
+ public static final int LAYOUT_LARGE = 2;
+
+ private static final int MIN_CONTENT_MAX_LINES = 2;
+
+ private static final int FIXED_HEIGHT_DIMENS_FOR_LARGE_CONTENT = 14 + 12 + 4 + 16;
+ private static final int FIXED_HEIGHT_DIMENS_FOR_MEDIUM_CONTENT = 8 + 4 + 4 + 8;
+ private static final int FIXED_HEIGHT_DIMENS_FOR_SMALL = 6 + 4 + 8;
+ private static final int FIXED_WIDTH_DIMENS_FOR_SMALL = 4 + 4;
+
+ private static final Pattern DOUBLE_EXCLAMATION_PATTERN = Pattern.compile("[!][!]+");
+ private static final Pattern DOUBLE_QUESTION_PATTERN = Pattern.compile("[?][?]+");
+ private static final Pattern ANY_DOUBLE_MARK_PATTERN = Pattern.compile("[!?][!?]+");
+ private static final Pattern MIXED_MARK_PATTERN = Pattern.compile("![?].*|.*[?]!");
+
+ public static final String EMPTY_STRING = "";
+
+ private Context mContext;
+ private PeopleSpaceTile mTile;
+ private float mDensity;
+ private int mAppWidgetId;
+ private int mWidth;
+ private int mHeight;
+ private int mLayoutSize;
+
+ PeopleTileViewHelper(Context context, PeopleSpaceTile tile,
+ int appWidgetId, Bundle options) {
+ mContext = context;
+ mTile = tile;
+ mAppWidgetId = appWidgetId;
+ mDensity = mContext.getResources().getDisplayMetrics().density;
+ int display = mContext.getResources().getConfiguration().orientation;
+ mWidth = display == Configuration.ORIENTATION_PORTRAIT
+ ? options.getInt(OPTION_APPWIDGET_MIN_WIDTH,
+ getSizeInDp(R.dimen.default_width)) : options.getInt(
+ OPTION_APPWIDGET_MAX_WIDTH,
+ getSizeInDp(R.dimen.default_width));
+ mHeight = display == Configuration.ORIENTATION_PORTRAIT ? options.getInt(
+ OPTION_APPWIDGET_MAX_HEIGHT,
+ getSizeInDp(R.dimen.default_height))
+ : options.getInt(OPTION_APPWIDGET_MIN_HEIGHT,
+ getSizeInDp(R.dimen.default_height));
+ mLayoutSize = getLayoutSize();
+ }
+
+ public RemoteViews getViews() {
+ RemoteViews viewsForTile = getViewForTile();
+ int maxAvatarSize = getMaxAvatarSize(viewsForTile);
+ RemoteViews views = setCommonRemoteViewsFields(viewsForTile, maxAvatarSize);
+ return setLaunchIntents(views);
+ }
+
+ /**
+ * The prioritization for the {@code mTile} content is missed calls, followed by notification
+ * content, then birthdays, then the most recent status, and finally last interaction.
+ */
+ private RemoteViews getViewForTile() {
+ if (Objects.equals(mTile.getNotificationCategory(), CATEGORY_MISSED_CALL)) {
+ if (DEBUG) Log.d(TAG, "Create missed call view");
+ return createMissedCallRemoteViews();
+ }
+
+ if (mTile.getNotificationKey() != null) {
+ if (DEBUG) Log.d(TAG, "Create notification view");
+ return createNotificationRemoteViews();
+ }
+
+ // TODO: Add sorting when we expose timestamp of statuses.
+ List<ConversationStatus> statusesForEntireView =
+ mTile.getStatuses() == null ? Arrays.asList() : mTile.getStatuses().stream().filter(
+ c -> isStatusValidForEntireStatusView(c)).collect(Collectors.toList());
+ ConversationStatus birthdayStatus = getBirthdayStatus(statusesForEntireView);
+ if (birthdayStatus != null) {
+ if (DEBUG) Log.d(TAG, "Create birthday view");
+ return createStatusRemoteViews(birthdayStatus);
+ }
+
+ if (!statusesForEntireView.isEmpty()) {
+ if (DEBUG) {
+ Log.d(TAG,
+ "Create status view for: " + statusesForEntireView.get(0).getActivity());
+ }
+ return createStatusRemoteViews(statusesForEntireView.get(0));
+ }
+
+ return createLastInteractionRemoteViews();
+ }
+
+ private void setMaxLines(RemoteViews views) {
+ int textSize = mLayoutSize == LAYOUT_LARGE ? getSizeInDp(
+ R.dimen.content_text_size_for_medium)
+ : getSizeInDp(R.dimen.content_text_size_for_medium);
+ int lineHeight = getLineHeight(textSize);
+ int notificationContentHeight = getContentHeightForLayout(lineHeight);
+ int maxAdaptiveLines = Math.floorDiv(notificationContentHeight, lineHeight);
+ int maxLines = Math.max(MIN_CONTENT_MAX_LINES, maxAdaptiveLines);
+ views.setInt(R.id.text_content, "setMaxLines", maxLines);
+ }
+
+ private int getLineHeight(int textSize) {
+ try {
+ TextView text = new TextView(mContext);
+ text.setTextSize(textSize);
+ int lineHeight = (int) (text.getLineHeight() / mDensity);
+ return lineHeight;
+ } catch (Exception e) {
+ Log.e(TAG, "Could not create text view: " + e);
+ return getSizeInDp(
+ R.dimen.content_text_size_for_medium);
+ }
+ }
+
+ private int getSizeInDp(int dimenResourceId) {
+ return (int) (mContext.getResources().getDimension(dimenResourceId) / mDensity);
+ }
+
+ private int getContentHeightForLayout(int lineHeight) {
+ switch (mLayoutSize) {
+ case LAYOUT_MEDIUM:
+ return mHeight - (lineHeight + FIXED_HEIGHT_DIMENS_FOR_MEDIUM_CONTENT);
+ case LAYOUT_LARGE:
+ return mHeight - (getSizeInDp(
+ R.dimen.max_people_avatar_size_for_large_content) + lineHeight
+ + FIXED_HEIGHT_DIMENS_FOR_LARGE_CONTENT);
+ default:
+ return -1;
+ }
+ }
+
+ /** Calculates the best layout relative to the size in {@code options}. */
+ private int getLayoutSize() {
+ if (mHeight >= getSizeInDp(R.dimen.required_width_for_large)
+ && mWidth >= getSizeInDp(R.dimen.required_width_for_large)) {
+ if (DEBUG) Log.d(TAG, "Large view for mWidth: " + mWidth + " mHeight: " + mHeight);
+ return LAYOUT_LARGE;
+ }
+ // Small layout used below a certain minimum mWidth with any mHeight.
+ if (mWidth >= getSizeInDp(R.dimen.required_width_for_medium)) {
+ if (DEBUG) Log.d(TAG, "Medium view for mWidth: " + mWidth + " mHeight: " + mHeight);
+ return LAYOUT_MEDIUM;
+ }
+ // Small layout can always handle our minimum mWidth and mHeight for our widget.
+ if (DEBUG) Log.d(TAG, "Small view for mWidth: " + mWidth + " mHeight: " + mHeight);
+ return LAYOUT_SMALL;
+ }
+
+ /** Returns the max avatar size for {@code views} under the current {@code options}. */
+ private int getMaxAvatarSize(RemoteViews views) {
+ int layoutId = views.getLayoutId();
+ int avatarSize = getSizeInDp(R.dimen.avatar_size_for_medium);
+ if (layoutId == R.layout.people_tile_medium_empty) {
+ return getSizeInDp(
+ R.dimen.max_people_avatar_size_for_large_content);
+ }
+ if (layoutId == R.layout.people_tile_medium_with_content) {
+ return getSizeInDp(R.dimen.avatar_size_for_medium);
+ }
+
+ // Calculate adaptive avatar size for remaining layouts.
+ if (layoutId == R.layout.people_tile_small) {
+ int avatarHeightSpace = mHeight - (FIXED_HEIGHT_DIMENS_FOR_SMALL + Math.max(18,
+ getLineHeight(getSizeInDp(
+ R.dimen.name_text_size_for_small))));
+ int avatarWidthSpace = mWidth - FIXED_WIDTH_DIMENS_FOR_SMALL;
+ avatarSize = Math.min(avatarHeightSpace, avatarWidthSpace);
+ }
+
+ if (layoutId == R.layout.people_tile_large_with_content) {
+ avatarSize = mHeight - (FIXED_HEIGHT_DIMENS_FOR_LARGE_CONTENT + (getLineHeight(
+ getSizeInDp(R.dimen.content_text_size_for_large))
+ * 3));
+ return Math.min(avatarSize, getSizeInDp(
+ R.dimen.max_people_avatar_size_for_large_content));
+ }
+
+ if (layoutId == R.layout.people_tile_large_empty) {
+ int avatarHeightSpace = mHeight - (14 + 14 + getLineHeight(
+ getSizeInDp(R.dimen.name_text_size_for_large))
+ + getLineHeight(
+ getSizeInDp(R.dimen.content_text_size_for_large))
+ + 16 + 10 + 14);
+ int avatarWidthSpace = mWidth - (14 + 14);
+ avatarSize = Math.min(avatarHeightSpace, avatarWidthSpace);
+ }
+ return Math.min(avatarSize,
+ getSizeInDp(R.dimen.max_people_avatar_size));
+ }
+
+ private RemoteViews setCommonRemoteViewsFields(RemoteViews views,
+ int maxAvatarSize) {
+ try {
+ boolean isAvailable =
+ mTile.getStatuses() != null && mTile.getStatuses().stream().anyMatch(
+ c -> c.getAvailability() == AVAILABILITY_AVAILABLE);
+ if (isAvailable) {
+ views.setViewVisibility(R.id.availability, View.VISIBLE);
+ } else {
+ views.setViewVisibility(R.id.availability, View.GONE);
+ }
+ boolean hasNewStory =
+ mTile.getStatuses() != null && mTile.getStatuses().stream().anyMatch(
+ c -> c.getActivity() == ACTIVITY_NEW_STORY);
+ views.setTextViewText(R.id.name, mTile.getUserName().toString());
+ views.setBoolean(R.id.image, "setClipToOutline", true);
+
+ Icon icon = mTile.getUserIcon();
+ PeopleStoryIconFactory storyIcon = new PeopleStoryIconFactory(mContext,
+ mContext.getPackageManager(),
+ IconDrawableFactory.newInstance(mContext, false),
+ maxAvatarSize);
+ Drawable drawable = icon.loadDrawable(mContext);
+ Drawable personDrawable = storyIcon.getPeopleTileDrawable(drawable,
+ mTile.getPackageName(), getUserId(mTile), mTile.isImportantConversation(),
+ hasNewStory);
+ Bitmap bitmap = convertDrawableToBitmap(personDrawable);
+ views.setImageViewBitmap(R.id.person_icon, bitmap);
+
+ return views;
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to set common fields: " + e);
+ }
+ return views;
+ }
+
+ private RemoteViews setLaunchIntents(RemoteViews views) {
+ try {
+ Intent activityIntent = new Intent(mContext, LaunchConversationActivity.class);
+ activityIntent.addFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_CLEAR_TASK
+ | Intent.FLAG_ACTIVITY_NO_HISTORY
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ activityIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_TILE_ID, mTile.getId());
+ activityIntent.putExtra(
+ PeopleSpaceWidgetProvider.EXTRA_PACKAGE_NAME, mTile.getPackageName());
+ activityIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_USER_HANDLE,
+ mTile.getUserHandle());
+ activityIntent.putExtra(
+ PeopleSpaceWidgetProvider.EXTRA_NOTIFICATION_KEY, mTile.getNotificationKey());
+ views.setOnClickPendingIntent(R.id.item, PendingIntent.getActivity(
+ mContext,
+ mAppWidgetId,
+ activityIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE));
+ return views;
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to add launch intents: " + e);
+ }
+
+ return views;
+ }
+
+ private RemoteViews createMissedCallRemoteViews() {
+ RemoteViews views = getViewForContentLayout();
+ views.setViewVisibility(R.id.predefined_icon, View.VISIBLE);
+ setMaxLines(views);
+ views.setTextViewText(R.id.text_content, mTile.getNotificationContent());
+ views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_phone_missed);
+ return views;
+ }
+
+ private RemoteViews createNotificationRemoteViews() {
+ RemoteViews views = getViewForContentLayout();
+ Uri image = mTile.getNotificationDataUri();
+ if (image != null) {
+ // TODO: Use NotificationInlineImageCache
+ views.setImageViewUri(R.id.image, image);
+ views.setViewVisibility(R.id.image, View.VISIBLE);
+ views.setViewVisibility(R.id.text_content, View.GONE);
+ views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_photo_camera);
+ } else {
+ setMaxLines(views);
+ CharSequence content = mTile.getNotificationContent();
+ views = setPunctuationRemoteViewsFields(views, content);
+ TypedValue typedValue = new TypedValue();
+ mContext.getTheme().resolveAttribute(android.R.attr.textColorPrimary, typedValue, true);
+ int primaryTextColor = mContext.getColor(typedValue.resourceId);
+ views.setInt(R.id.text_content, "setTextColor", primaryTextColor);
+ views.setTextViewText(R.id.text_content, mTile.getNotificationContent());
+ views.setViewVisibility(R.id.image, View.GONE);
+ views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_message);
+ }
+ // TODO: Set subtext as Group Sender name once storing the name in PeopleSpaceTile and
+ // subtract 1 from maxLines when present.
+ views.setViewVisibility(R.id.subtext, View.GONE);
+ return views;
+ }
+
+ private RemoteViews createStatusRemoteViews(ConversationStatus status) {
+ RemoteViews views = getViewForContentLayout();
+ CharSequence statusText = status.getDescription();
+ if (TextUtils.isEmpty(statusText)) {
+ statusText = getStatusTextByType(status.getActivity());
+ }
+ views.setViewVisibility(R.id.predefined_icon, View.VISIBLE);
+ setMaxLines(views);
+ // Secondary text color for statuses.
+ TypedValue typedValue = new TypedValue();
+ mContext.getTheme().resolveAttribute(android.R.attr.textColorSecondary, typedValue, true);
+ int secondaryTextColor = mContext.getColor(typedValue.resourceId);
+ views.setInt(R.id.text_content, "setTextColor", secondaryTextColor);
+ views.setTextViewText(R.id.text_content, statusText);
+
+ Icon statusIcon = status.getIcon();
+ if (statusIcon != null) {
+ // No multi-line text with status images on medium layout.
+ views.setViewVisibility(R.id.text_content, View.GONE);
+ // Show 1-line subtext on large layout with status images.
+ if (mLayoutSize == LAYOUT_LARGE) {
+ views.setViewVisibility(R.id.subtext, View.VISIBLE);
+ views.setTextViewText(R.id.subtext, statusText);
+ }
+ views.setViewVisibility(R.id.image, View.VISIBLE);
+ views.setImageViewIcon(R.id.image, statusIcon);
+ } else {
+ views.setViewVisibility(R.id.image, View.GONE);
+ }
+ // TODO: Set status pre-defined icons
+ views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_person);
+ return views;
+ }
+
+ @Nullable
+ private ConversationStatus getBirthdayStatus(
+ List<ConversationStatus> statuses) {
+ Optional<ConversationStatus> birthdayStatus = statuses.stream().filter(
+ c -> c.getActivity() == ACTIVITY_BIRTHDAY).findFirst();
+ if (birthdayStatus.isPresent()) {
+ return birthdayStatus.get();
+ }
+ if (!TextUtils.isEmpty(mTile.getBirthdayText())) {
+ return new ConversationStatus.Builder(mTile.getId(), ACTIVITY_BIRTHDAY).build();
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns whether a {@code status} should have its own entire templated view.
+ *
+ * <p>A status may still be shown on the view (for example, as a new story ring) even if it's
+ * not valid to compose an entire view.
+ */
+ private boolean isStatusValidForEntireStatusView(ConversationStatus status) {
+ switch (status.getActivity()) {
+ // Birthday & Anniversary don't require text provided or icon provided.
+ case ACTIVITY_BIRTHDAY:
+ case ACTIVITY_ANNIVERSARY:
+ return true;
+ default:
+ // For future birthday, location, new story, video, music, game, and other, the
+ // app must provide either text or an icon.
+ return !TextUtils.isEmpty(status.getDescription())
+ || status.getIcon() != null;
+ }
+ }
+
+ private String getStatusTextByType(int activity) {
+ switch (activity) {
+ case ACTIVITY_BIRTHDAY:
+ return mContext.getString(R.string.birthday_status);
+ case ACTIVITY_UPCOMING_BIRTHDAY:
+ return mContext.getString(R.string.upcoming_birthday_status);
+ case ACTIVITY_ANNIVERSARY:
+ return mContext.getString(R.string.anniversary_status);
+ case ACTIVITY_LOCATION:
+ return mContext.getString(R.string.location_status);
+ case ACTIVITY_NEW_STORY:
+ return mContext.getString(R.string.new_story_status);
+ case ACTIVITY_VIDEO:
+ return mContext.getString(R.string.video_status);
+ case ACTIVITY_AUDIO:
+ return mContext.getString(R.string.audio_status);
+ case ACTIVITY_GAME:
+ return mContext.getString(R.string.game_status);
+ default:
+ return EMPTY_STRING;
+ }
+ }
+
+ private RemoteViews setPunctuationRemoteViewsFields(
+ RemoteViews views, CharSequence content) {
+ String punctuation = getBackgroundTextFromMessage(content.toString());
+ int visibility = View.GONE;
+ if (punctuation != null) {
+ visibility = View.VISIBLE;
+ }
+ views.setTextViewText(R.id.punctuation1, punctuation);
+ views.setTextViewText(R.id.punctuation2, punctuation);
+ views.setTextViewText(R.id.punctuation3, punctuation);
+ views.setTextViewText(R.id.punctuation4, punctuation);
+ views.setTextViewText(R.id.punctuation5, punctuation);
+ views.setTextViewText(R.id.punctuation6, punctuation);
+
+ views.setViewVisibility(R.id.punctuation1, visibility);
+ views.setViewVisibility(R.id.punctuation2, visibility);
+ views.setViewVisibility(R.id.punctuation3, visibility);
+ views.setViewVisibility(R.id.punctuation4, visibility);
+ views.setViewVisibility(R.id.punctuation5, visibility);
+ views.setViewVisibility(R.id.punctuation6, visibility);
+
+ return views;
+ }
+
+ /** Gets character for mTile background decoration based on notification content. */
+ @VisibleForTesting
+ String getBackgroundTextFromMessage(String message) {
+ if (!ANY_DOUBLE_MARK_PATTERN.matcher(message).find()) {
+ return null;
+ }
+ if (MIXED_MARK_PATTERN.matcher(message).find()) {
+ return "!?";
+ }
+ Matcher doubleQuestionMatcher = DOUBLE_QUESTION_PATTERN.matcher(message);
+ if (!doubleQuestionMatcher.find()) {
+ return "!";
+ }
+ Matcher doubleExclamationMatcher = DOUBLE_EXCLAMATION_PATTERN.matcher(message);
+ if (!doubleExclamationMatcher.find()) {
+ return "?";
+ }
+ // If we have both "!!" and "??", return the one that comes first.
+ if (doubleQuestionMatcher.start() < doubleExclamationMatcher.start()) {
+ return "?";
+ }
+ return "!";
+ }
+
+ private RemoteViews getViewForContentLayout() {
+ RemoteViews views = new RemoteViews(mContext.getPackageName(),
+ getLayoutForContent());
+ if (mLayoutSize == LAYOUT_SMALL) {
+ views.setViewVisibility(R.id.predefined_icon, View.VISIBLE);
+ views.setViewVisibility(R.id.name, View.GONE);
+ } else {
+ views.setViewVisibility(R.id.predefined_icon, View.GONE);
+ views.setViewVisibility(R.id.name, View.VISIBLE);
+ views.setViewVisibility(R.id.text_content, View.VISIBLE);
+ views.setViewVisibility(R.id.subtext, View.GONE);
+ }
+ return views;
+ }
+
+ private RemoteViews createLastInteractionRemoteViews() {
+ RemoteViews views = new RemoteViews(mContext.getPackageName(), getEmptyLayout());
+ if (mLayoutSize == LAYOUT_SMALL) {
+ views.setViewVisibility(R.id.name, View.VISIBLE);
+ views.setViewVisibility(R.id.predefined_icon, View.GONE);
+ }
+ String status = PeopleSpaceUtils.getLastInteractionString(mContext,
+ mTile.getLastInteractionTimestamp());
+ views.setTextViewText(R.id.last_interaction, status);
+ return views;
+ }
+
+ private int getEmptyLayout() {
+ switch (mLayoutSize) {
+ case LAYOUT_MEDIUM:
+ return R.layout.people_tile_medium_empty;
+ case LAYOUT_LARGE:
+ return R.layout.people_tile_large_empty;
+ case LAYOUT_SMALL:
+ default:
+ return R.layout.people_tile_small;
+ }
+ }
+
+ private int getLayoutForContent() {
+ switch (mLayoutSize) {
+ case LAYOUT_MEDIUM:
+ return R.layout.people_tile_medium_with_content;
+ case LAYOUT_LARGE:
+ return R.layout.people_tile_large_with_content;
+ case LAYOUT_SMALL:
+ default:
+ return R.layout.people_tile_small;
+ }
+ }
+}
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/screenshot/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
index 1ec175c..78ee896 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
@@ -16,8 +16,6 @@
package com.android.systemui.screenshot;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
@@ -25,6 +23,7 @@
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -32,6 +31,7 @@
import android.util.IntArray;
import android.util.Log;
import android.util.MathUtils;
+import android.util.Range;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
@@ -49,29 +49,30 @@
*/
public class CropView extends View {
private static final String TAG = "CropView";
+
public enum CropBoundary {
- NONE, TOP, BOTTOM
+ NONE, TOP, BOTTOM, LEFT, RIGHT
}
private final float mCropTouchMargin;
private final Paint mShadePaint;
private final Paint mHandlePaint;
- // Top and bottom crops are stored as floats [0, 1], representing the top and bottom of the
- // view, respectively.
- private float mTopCrop = 0f;
- private float mBottomCrop = 1f;
-
- // When the user is dragging a handle, these variables store the distance between the top/bottom
- // crop values and
- private float mTopDelta = 0f;
- private float mBottomDelta = 0f;
+ // Crop rect with each element represented as [0,1] along its proper axis.
+ private RectF mCrop = new RectF(0, 0, 1, 1);
private int mExtraTopPadding;
private int mExtraBottomPadding;
+ private int mImageWidth;
private CropBoundary mCurrentDraggingBoundary = CropBoundary.NONE;
+ // The starting value of mCurrentDraggingBoundary's crop, used to compute touch deltas.
+ private float mMovementStartValue;
private float mStartingY; // y coordinate of ACTION_DOWN
+ private float mStartingX;
+ // The allowable values for the current boundary being dragged
+ private Range<Float> mMotionRange;
+
private CropInteractionListener mCropInteractionListener;
public CropView(Context context, @Nullable AttributeSet attrs) {
@@ -86,6 +87,7 @@
mShadePaint.setColor(t.getColor(R.styleable.CropView_scrimColor, Color.TRANSPARENT));
mHandlePaint = new Paint();
mHandlePaint.setColor(t.getColor(R.styleable.CropView_handleColor, Color.BLACK));
+ mHandlePaint.setStrokeCap(Paint.Cap.ROUND);
mHandlePaint.setStrokeWidth(
t.getDimensionPixelSize(R.styleable.CropView_handleThickness, 20));
t.recycle();
@@ -100,8 +102,7 @@
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
- ss.mTopBoundary = getTopBoundary();
- ss.mBottomBoundary = getBottomBoundary();
+ ss.mCrop = mCrop;
return ss;
}
@@ -110,45 +111,67 @@
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
- setBoundaryTo(CropBoundary.TOP, ss.mTopBoundary);
- setBoundaryTo(CropBoundary.BOTTOM, ss.mBottomBoundary);
+ mCrop = ss.mCrop;
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
- float top = mTopCrop + mTopDelta;
- float bottom = mBottomCrop + mBottomDelta;
- drawShade(canvas, 0, top);
- drawShade(canvas, bottom, 1f);
- drawHandle(canvas, top, /* draw the handle tab down */ false);
- drawHandle(canvas, bottom, /* draw the handle tab up */ true);
+ drawShade(canvas, 0, 0, 1, mCrop.top);
+ drawShade(canvas, 0, mCrop.bottom, 1, 1);
+ drawShade(canvas, 0, mCrop.top, mCrop.left, mCrop.bottom);
+ drawShade(canvas, mCrop.right, mCrop.top, 1, mCrop.bottom);
+ drawHorizontalHandle(canvas, mCrop.top, /* draw the handle tab up */ true);
+ drawHorizontalHandle(canvas, mCrop.bottom, /* draw the handle tab down */ false);
+ drawVerticalHandle(canvas, mCrop.left, /* left */ true);
+ drawVerticalHandle(canvas, mCrop.right, /* right */ false);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
- int topPx = fractionToPixels(mTopCrop);
- int bottomPx = fractionToPixels(mBottomCrop);
+ int topPx = fractionToVerticalPixels(mCrop.top);
+ int bottomPx = fractionToVerticalPixels(mCrop.bottom);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
- mCurrentDraggingBoundary = nearestBoundary(event, topPx, bottomPx);
+ mCurrentDraggingBoundary = nearestBoundary(event, topPx, bottomPx,
+ fractionToHorizontalPixels(mCrop.left),
+ fractionToHorizontalPixels(mCrop.right));
if (mCurrentDraggingBoundary != CropBoundary.NONE) {
mStartingY = event.getY();
+ mStartingX = event.getX();
+ mMovementStartValue = getBoundaryPosition(mCurrentDraggingBoundary);
updateListener(event);
+ switch (mCurrentDraggingBoundary) {
+ case TOP:
+ mMotionRange = new Range<>(0f,
+ mCrop.bottom - pixelDistanceToFraction(mCropTouchMargin,
+ CropBoundary.BOTTOM));
+ break;
+ case BOTTOM:
+ mMotionRange = new Range<>(
+ mCrop.top + pixelDistanceToFraction(mCropTouchMargin,
+ CropBoundary.TOP), 1f);
+ break;
+ case LEFT:
+ mMotionRange = new Range<>(0f,
+ mCrop.right - pixelDistanceToFraction(mCropTouchMargin,
+ CropBoundary.RIGHT));
+ break;
+ case RIGHT:
+ mMotionRange = new Range<>(
+ mCrop.left + pixelDistanceToFraction(mCropTouchMargin,
+ CropBoundary.LEFT), 1f);
+ break;
+ }
}
return true;
case MotionEvent.ACTION_MOVE:
if (mCurrentDraggingBoundary != CropBoundary.NONE) {
- float delta = event.getY() - mStartingY;
- if (mCurrentDraggingBoundary == CropBoundary.TOP) {
- mTopDelta = pixelDistanceToFraction((int) MathUtils.constrain(delta,
- -topPx + mExtraTopPadding,
- bottomPx - 2 * mCropTouchMargin - topPx));
- } else { // Bottom
- mBottomDelta = pixelDistanceToFraction((int) MathUtils.constrain(delta,
- topPx + 2 * mCropTouchMargin - bottomPx,
- getHeight() - bottomPx - mExtraBottomPadding));
- }
+ float deltaPx = isVertical(mCurrentDraggingBoundary) ? event.getY() - mStartingY
+ : event.getX() - mStartingX;
+ float delta = pixelDistanceToFraction((int) deltaPx, mCurrentDraggingBoundary);
+ setBoundaryPosition(mCurrentDraggingBoundary,
+ mMotionRange.clamp(mMovementStartValue + delta));
updateListener(event);
invalidate();
return true;
@@ -156,8 +179,6 @@
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
if (mCurrentDraggingBoundary != CropBoundary.NONE) {
- // Commit the delta to the stored crop values.
- commitDeltas(mCurrentDraggingBoundary);
updateListener(event);
}
}
@@ -167,22 +188,46 @@
/**
* Set the given boundary to the given value without animation.
*/
- public void setBoundaryTo(CropBoundary boundary, float value) {
+ public void setBoundaryPosition(CropBoundary boundary, float position) {
switch (boundary) {
case TOP:
- mTopCrop = value;
+ mCrop.top = position;
break;
case BOTTOM:
- mBottomCrop = value;
+ mCrop.bottom = position;
+ break;
+ case LEFT:
+ mCrop.left = position;
+ break;
+ case RIGHT:
+ mCrop.right = position;
break;
case NONE:
- Log.w(TAG, "No boundary selected for animation");
+ Log.w(TAG, "No boundary selected");
break;
}
invalidate();
}
+ private float getBoundaryPosition(CropBoundary boundary) {
+ switch (boundary) {
+ case TOP:
+ return mCrop.top;
+ case BOTTOM:
+ return mCrop.bottom;
+ case LEFT:
+ return mCrop.left;
+ case RIGHT:
+ return mCrop.right;
+ }
+ return 0;
+ }
+
+ private static boolean isVertical(CropBoundary boundary) {
+ return boundary == CropBoundary.TOP || boundary == CropBoundary.BOTTOM;
+ }
+
/**
* Animate the given boundary to the given value.
*/
@@ -191,28 +236,13 @@
Log.w(TAG, "No boundary selected for animation");
return;
}
- float totalDelta = (boundary == CropBoundary.TOP) ? (value - mTopCrop)
- : (value - mBottomCrop);
+ float start = getBoundaryPosition(boundary);
ValueAnimator animator = new ValueAnimator();
animator.addUpdateListener(animation -> {
- if (boundary == CropBoundary.TOP) {
- mTopDelta = animation.getAnimatedFraction() * totalDelta;
- } else {
- mBottomDelta = animation.getAnimatedFraction() * totalDelta;
- }
+ setBoundaryPosition(boundary,
+ MathUtils.lerp(start, value, animation.getAnimatedFraction()));
invalidate();
});
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- commitDeltas(boundary);
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- commitDeltas(boundary);
- }
- });
animator.setFloatValues(0f, 1f);
animator.setDuration(750);
animator.setInterpolator(new FastOutSlowInInterpolator());
@@ -230,65 +260,79 @@
}
/**
- * @return value [0,1] representing the position of the top crop boundary. Does not reflect
- * changes from any in-progress touch input.
+ * Set the pixel width of the image on the screen (on-screen dimension, not actual bitmap
+ * dimension)
*/
- public float getTopBoundary() {
- return mTopCrop;
+ public void setImageWidth(int width) {
+ mImageWidth = width;
+ invalidate();
}
/**
- * @return value [0,1] representing the position of the bottom crop boundary. Does not reflect
- * changes from any in-progress touch input.
+ * @return RectF with values [0,1] representing the position of the boundaries along image axes.
*/
- public float getBottomBoundary() {
- return mBottomCrop;
+ public Rect getCropBoundaries(int imageWidth, int imageHeight) {
+ return new Rect((int) (mCrop.left * imageWidth), (int) (mCrop.top * imageHeight),
+ (int) (mCrop.right * imageWidth), (int) (mCrop.bottom * imageHeight));
}
public void setCropInteractionListener(CropInteractionListener listener) {
mCropInteractionListener = listener;
}
- private void commitDeltas(CropBoundary boundary) {
- if (boundary == CropBoundary.TOP) {
- mTopCrop += mTopDelta;
- mTopDelta = 0;
- } else if (boundary == CropBoundary.BOTTOM) {
- mBottomCrop += mBottomDelta;
- mBottomDelta = 0;
- }
- }
-
private void updateListener(MotionEvent event) {
- if (mCropInteractionListener != null) {
- float boundaryPosition = (mCurrentDraggingBoundary == CropBoundary.TOP)
- ? mTopCrop + mTopDelta : mBottomCrop + mBottomDelta;
+ if (mCropInteractionListener != null && (isVertical(mCurrentDraggingBoundary))) {
+ float boundaryPosition = getBoundaryPosition(mCurrentDraggingBoundary);
mCropInteractionListener.onCropMotionEvent(event, mCurrentDraggingBoundary,
- boundaryPosition, fractionToPixels(boundaryPosition));
+ boundaryPosition, fractionToVerticalPixels(boundaryPosition),
+ (mCrop.left + mCrop.right) / 2);
}
}
- private void drawShade(Canvas canvas, float fracStart, float fracEnd) {
- canvas.drawRect(0, fractionToPixels(fracStart), getWidth(),
- fractionToPixels(fracEnd), mShadePaint);
+ /**
+ * Draw a shade to the given canvas with the given [0,1] fractional image bounds.
+ */
+ private void drawShade(Canvas canvas, float left, float top, float right, float bottom) {
+ canvas.drawRect(fractionToHorizontalPixels(left), fractionToVerticalPixels(top),
+ fractionToHorizontalPixels(right),
+ fractionToVerticalPixels(bottom), mShadePaint);
}
- private void drawHandle(Canvas canvas, float frac, boolean handleTabUp) {
- int y = fractionToPixels(frac);
- canvas.drawLine(0, y, getWidth(), y, mHandlePaint);
- float radius = 15 * getResources().getDisplayMetrics().density;
- float x = getWidth() * .9f;
+ private void drawHorizontalHandle(Canvas canvas, float frac, boolean handleTabUp) {
+ int y = fractionToVerticalPixels(frac);
+ canvas.drawLine(fractionToHorizontalPixels(mCrop.left), y,
+ fractionToHorizontalPixels(mCrop.right), y, mHandlePaint);
+ float radius = 8 * getResources().getDisplayMetrics().density;
+ int x = (fractionToHorizontalPixels(mCrop.left) + fractionToHorizontalPixels(mCrop.right))
+ / 2;
canvas.drawArc(x - radius, y - radius, x + radius, y + radius, handleTabUp ? 180 : 0, 180,
true, mHandlePaint);
}
+ private void drawVerticalHandle(Canvas canvas, float frac, boolean handleTabLeft) {
+ int x = fractionToHorizontalPixels(frac);
+ canvas.drawLine(x, fractionToVerticalPixels(mCrop.top), x,
+ fractionToVerticalPixels(mCrop.bottom), mHandlePaint);
+ float radius = 8 * getResources().getDisplayMetrics().density;
+ int y = (fractionToVerticalPixels(getBoundaryPosition(CropBoundary.TOP))
+ + fractionToVerticalPixels(
+ getBoundaryPosition(CropBoundary.BOTTOM))) / 2;
+ canvas.drawArc(x - radius, y - radius, x + radius, y + radius, handleTabLeft ? 90 : 270,
+ 180,
+ true, mHandlePaint);
+ }
+
/**
* Convert the given fraction position to pixel position within the View.
*/
- private int fractionToPixels(float frac) {
+ private int fractionToVerticalPixels(float frac) {
return (int) (mExtraTopPadding + frac * getImageHeight());
}
+ private int fractionToHorizontalPixels(float frac) {
+ return (int) ((getWidth() - mImageWidth) / 2 + frac * mImageWidth);
+ }
+
private int getImageHeight() {
return getHeight() - mExtraTopPadding - mExtraBottomPadding;
}
@@ -296,17 +340,30 @@
/**
* Convert the given pixel distance to fraction of the image.
*/
- private float pixelDistanceToFraction(int px) {
- return px / (float) getImageHeight();
+ private float pixelDistanceToFraction(float px, CropBoundary boundary) {
+ if (isVertical(boundary)) {
+ return px / getImageHeight();
+ } else {
+ return px / mImageWidth;
+ }
}
- private CropBoundary nearestBoundary(MotionEvent event, int topPx, int bottomPx) {
+ private CropBoundary nearestBoundary(MotionEvent event, int topPx, int bottomPx, int leftPx,
+ int rightPx) {
if (Math.abs(event.getY() - topPx) < mCropTouchMargin) {
return CropBoundary.TOP;
}
if (Math.abs(event.getY() - bottomPx) < mCropTouchMargin) {
return CropBoundary.BOTTOM;
}
+ if (event.getY() > topPx || event.getY() < bottomPx) {
+ if (Math.abs(event.getX() - leftPx) < mCropTouchMargin) {
+ return CropBoundary.LEFT;
+ }
+ if (Math.abs(event.getX() - rightPx) < mCropTouchMargin) {
+ return CropBoundary.RIGHT;
+ }
+ }
return CropBoundary.NONE;
}
@@ -321,10 +378,10 @@
@Override
protected int getVirtualViewAt(float x, float y) {
- if (Math.abs(y - fractionToPixels(mTopCrop)) < mCropTouchMargin) {
+ if (Math.abs(y - fractionToVerticalPixels(mCrop.top)) < mCropTouchMargin) {
return TOP_HANDLE_ID;
}
- if (Math.abs(y - fractionToPixels(mBottomCrop)) < mCropTouchMargin) {
+ if (Math.abs(y - fractionToVerticalPixels(mCrop.bottom)) < mCropTouchMargin) {
return BOTTOM_HANDLE_ID;
}
return ExploreByTouchHelper.INVALID_ID;
@@ -338,7 +395,7 @@
@Override
protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) {
- switch(virtualViewId) {
+ switch (virtualViewId) {
case TOP_HANDLE_ID:
event.setContentDescription(
getResources().getString(R.string.screenshot_top_boundary));
@@ -353,16 +410,16 @@
@Override
protected void onPopulateNodeForVirtualView(int virtualViewId,
AccessibilityNodeInfo node) {
- switch(virtualViewId) {
+ switch (virtualViewId) {
case TOP_HANDLE_ID:
node.setContentDescription(
getResources().getString(R.string.screenshot_top_boundary));
- setNodePositions(mTopCrop, node);
+ setNodePositions(mCrop.top, node);
break;
case BOTTOM_HANDLE_ID:
node.setContentDescription(
getResources().getString(R.string.screenshot_bottom_boundary));
- setNodePositions(mBottomCrop, node);
+ setNodePositions(mCrop.bottom, node);
break;
}
@@ -380,7 +437,7 @@
}
private void setNodePositions(float fraction, AccessibilityNodeInfo node) {
- int pixels = fractionToPixels(fraction);
+ int pixels = fractionToVerticalPixels(fraction);
Rect rect = new Rect(0, (int) (pixels - mCropTouchMargin),
getWidth(), (int) (pixels + mCropTouchMargin));
node.setBoundsInParent(rect);
@@ -400,12 +457,11 @@
* boundaries.
*/
void onCropMotionEvent(MotionEvent event, CropBoundary boundary, float boundaryPosition,
- int boundaryPositionPx);
+ int boundaryPositionPx, float horizontalCenter);
}
static class SavedState extends BaseSavedState {
- float mTopBoundary;
- float mBottomBoundary;
+ RectF mCrop;
/**
* Constructor called from {@link CropView#onSaveInstanceState()}
@@ -419,15 +475,13 @@
*/
private SavedState(Parcel in) {
super(in);
- mTopBoundary = in.readFloat();
- mBottomBoundary = in.readFloat();
+ mCrop = in.readParcelable(ClassLoader.getSystemClassLoader());
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
- out.writeFloat(mTopBoundary);
- out.writeFloat(mBottomBoundary);
+ out.writeParcelable(mCrop, 0);
}
public static final Parcelable.Creator<SavedState> CREATOR
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
index 6a004c2..01afacf 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
@@ -128,7 +128,7 @@
mPreview.addOnLayoutChangeListener(
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
- updateCropLocation());
+ updateImageDimensions());
Intent intent = getIntent();
mScrollCaptureResponse = intent.getParcelableExtra(EXTRA_CAPTURE_RESPONSE);
@@ -206,7 +206,7 @@
Log.d(TAG, "onCaptureCompleted(longScreenshot=" + longScreenshot + ")");
mLongScreenshot = longScreenshot;
mPreview.setImageDrawable(mLongScreenshot.getDrawable());
- updateCropLocation();
+ updateImageDimensions();
mMagnifierView.setDrawable(mLongScreenshot.getDrawable(),
mLongScreenshot.getWidth(), mLongScreenshot.getHeight());
// Original boundaries go from the image tile set's y=0 to y=pageSize, so
@@ -363,10 +363,8 @@
return;
}
- Rect bounds = new Rect(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
- int height = bounds.height();
- bounds.top = (int) (height * mCropView.getTopBoundary());
- bounds.bottom = (int) (height * mCropView.getBottomBoundary());
+ Rect bounds = mCropView.getCropBoundaries(drawable.getIntrinsicWidth(),
+ drawable.getIntrinsicHeight());
if (bounds.isEmpty()) {
Log.w(TAG, "Crop bounds empty, skipping export.");
@@ -404,14 +402,16 @@
}
}
- private void updateCropLocation() {
+ private void updateImageDimensions() {
Drawable drawable = mPreview.getDrawable();
if (drawable == null) {
return;
}
Rect bounds = drawable.getBounds();
float imageRatio = bounds.width() / (float) bounds.height();
- float viewRatio = mPreview.getWidth() / (float) mPreview.getHeight();
+ int previewWidth = mPreview.getWidth() - mPreview.getPaddingLeft()
+ - mPreview.getPaddingRight();
+ float viewRatio = previewWidth / (float) mPreview.getHeight();
if (imageRatio > viewRatio) {
// Image is full width and height is constrained, compute extra padding to inform
@@ -419,9 +419,12 @@
float imageHeight = mPreview.getHeight() * viewRatio / imageRatio;
int extraPadding = (int) (mPreview.getHeight() - imageHeight) / 2;
mCropView.setExtraPadding(extraPadding, extraPadding);
+ mCropView.setImageWidth(previewWidth);
} else {
// Image is full height
mCropView.setExtraPadding(0, 0);
+ mCropView.setImageWidth((int) (mPreview.getHeight() * imageRatio));
}
+
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java b/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java
index 90f3042..08cd91c 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java
@@ -58,6 +58,7 @@
private float mCheckerboardBoxSize = 40;
private float mLastCropPosition;
+ private float mLastCenter = 0.5f;
private CropView.CropBoundary mCropBoundary;
private ViewPropertyAnimator mTranslationAnimator;
@@ -131,7 +132,7 @@
canvas.save();
// Translate such that the center of this view represents the center of the crop
// boundary.
- canvas.translate(-mDrawable.getBounds().width() / 2 + getWidth() / 2,
+ canvas.translate(-mDrawable.getBounds().width() * mLastCenter + getWidth() / 2,
-mDrawable.getBounds().height() * mLastCropPosition + getHeight() / 2);
mDrawable.draw(canvas);
canvas.restore();
@@ -148,8 +149,9 @@
@Override
public void onCropMotionEvent(MotionEvent event, CropView.CropBoundary boundary,
- float cropPosition, int cropPositionPx) {
+ float cropPosition, int cropPositionPx, float horizontalCenter) {
mCropBoundary = boundary;
+ mLastCenter = horizontalCenter;
boolean touchOnRight = event.getX() > getParentWidth() / 2;
float translateXTarget = touchOnRight ? 0 : getParentWidth() - getWidth();
switch (event.getAction()) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index 5c650ad..d15f1ff 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -142,6 +142,8 @@
private ScreenshotViewCallback mCallbacks;
private Animator mDismissAnimation;
private boolean mPendingSharedTransition;
+ private GestureDetector mSwipeDetector;
+ private SwipeDismissHandler mSwipeDismissHandler;
private final ArrayList<ScreenshotActionChip> mSmartChips = new ArrayList<>();
private PendingInteraction mPendingInteraction;
@@ -181,6 +183,23 @@
mContext.getDisplay().getRealMetrics(mDisplayMetrics);
mAccessibilityManager = AccessibilityManager.getInstance(mContext);
+
+ mSwipeDetector = new GestureDetector(mContext,
+ new GestureDetector.SimpleOnGestureListener() {
+ final Rect mActionsRect = new Rect();
+
+ @Override
+ public boolean onScroll(
+ MotionEvent ev1, MotionEvent ev2, float distanceX, float distanceY) {
+ mActionsContainer.getBoundsOnScreen(mActionsRect);
+ // return true if we aren't in the actions bar, or if we are but it isn't
+ // scrollable in the direction of movement
+ return !mActionsRect.contains((int) ev2.getRawX(), (int) ev2.getRawY())
+ || !mActionsContainer.canScrollHorizontally((int) distanceX);
+ }
+ });
+ mSwipeDetector.setIsLongpressEnabled(false);
+ mSwipeDismissHandler = new SwipeDismissHandler();
}
/**
@@ -230,6 +249,15 @@
inoutInfo.touchableRegion.set(touchRegion);
}
+ @Override // ViewGroup
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ // always pass through the down event so the swipe handler knows the initial state
+ if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ mSwipeDismissHandler.onTouch(this, ev);
+ }
+ return mSwipeDetector.onTouchEvent(ev);
+ }
+
@Override // View
protected void onFinishInflate() {
mScreenshotStatic = requireNonNull(findViewById(R.id.global_screenshot_static));
@@ -455,11 +483,7 @@
createScreenshotActionsShadeAnimation().start();
- SwipeDismissHandler swipeDismissHandler = new SwipeDismissHandler();
- mScreenshotPreview.setOnTouchListener(swipeDismissHandler);
- mActionsContainer.setOnTouchListener(swipeDismissHandler);
- mActionsContainerBackground.setOnTouchListener(swipeDismissHandler);
- mBackgroundProtection.setOnTouchListener(swipeDismissHandler);
+ setOnTouchListener(mSwipeDismissHandler);
}
});
@@ -776,30 +800,16 @@
}
class SwipeDismissHandler implements OnTouchListener {
-
- // if distance moved on ACTION_UP is less than this, register a click
- // otherwise, run return animator
- private static final float CLICK_MOVEMENT_THRESHOLD_DP = 1;
// distance needed to register a dismissal
private static final float DISMISS_DISTANCE_THRESHOLD_DP = 30;
private final GestureDetector mGestureDetector;
- private final float mDismissStartX;
- private final Rect mActionsRect = new Rect();
private float mStartX;
- private float mStartY;
- private float mTranslationX = 0;
-
- // tracks whether mStartX has been set for this interaction
- private boolean mInteractionStarted = false;
- // tracks whether we're dragging the UI (as opposed to scrolling the actions bar)
- private boolean mIsDragging = false;
SwipeDismissHandler() {
GestureDetector.OnGestureListener gestureListener = new SwipeDismissGestureListener();
mGestureDetector = new GestureDetector(mContext, gestureListener);
- mDismissStartX = mDismissButton.getX();
}
@Override
@@ -807,13 +817,9 @@
boolean gestureResult = mGestureDetector.onTouchEvent(event);
mCallbacks.onUserInteraction();
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
- mInteractionStarted = true;
mStartX = event.getRawX();
- mStartY = event.getRawY();
return true;
} else if (event.getActionMasked() == MotionEvent.ACTION_UP) {
- mInteractionStarted = false;
- mIsDragging = false;
if (isPastDismissThreshold()
&& (mDismissAnimation == null || !mDismissAnimation.isRunning())) {
if (DEBUG_INPUT) {
@@ -821,87 +827,47 @@
}
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SWIPE_DISMISSED);
animateDismissal(createSwipeDismissAnimation());
- return true;
- } else if (MathUtils.dist(mStartX, mStartY, event.getRawX(), event.getRawY())
- > dpToPx(CLICK_MOVEMENT_THRESHOLD_DP)) {
- // if we've moved a non-negligible distance (but not past the threshold),
+ } else {
+ // if we've moved, but not past the threshold, start the return animation
if (DEBUG_DISMISS) {
Log.d(TAG, "swipe gesture abandoned");
}
- // start the return animation
if ((mDismissAnimation == null || !mDismissAnimation.isRunning())) {
createSwipeReturnAnimation().start();
}
- return true;
- } else {
- if (view == mScreenshotPreview) {
- mScreenshotPreview.performClick();
- }
}
+ return true;
}
return gestureResult;
}
class SwipeDismissGestureListener extends GestureDetector.SimpleOnGestureListener {
-
- /**
- * This is somewhat complicated to handle because we want to allow scrolling the actions
- * bar (if it extends off the screen) as well as dismissing the UI horizontally by
- * dragging the actions bar. In addition, we don't get the initial ACTION_DOWN because
- * it is consumed by the action bar view.
- *
- * So, we use a gated system: first, keep track of the pointer location as we move;
- * next, check whether the actions bar can scroll in the direction we're moving in. If
- * it can, let the actions bar handle the event; otherwise, we've gone as far as we can
- * and can start dragging the UI instead.
- */
@Override
public boolean onScroll(
MotionEvent ev1, MotionEvent ev2, float distanceX, float distanceY) {
- mActionsContainer.getBoundsOnScreen(mActionsRect);
- if (!mInteractionStarted) {
- if (mActionsRect.contains((int) ev2.getRawX(), (int) ev2.getRawY())) {
- mStartX = ev2.getRawX();
- mInteractionStarted = true;
- }
- } else {
- float distance = ev2.getRawX() - mStartX;
- if ((mIsDragging && distance * mTranslationX > 0)
- || !mActionsRect.contains((int) ev2.getRawX(), (int) ev2.getRawY())
- || !mActionsContainer.canScrollHorizontally(-1 * (int) distance)) {
- mIsDragging = true;
- mTranslationX = distance;
- mScreenshotStatic.setTranslationX(mTranslationX);
- return true;
- } else {
- mStartX = ev2.getRawX();
- }
- }
- return false;
+ mScreenshotStatic.setTranslationX(ev2.getRawX() - mStartX);
+ return true;
}
}
private boolean isPastDismissThreshold() {
- if (mDirectionLTR) {
- return mTranslationX <= -1 * dpToPx(DISMISS_DISTANCE_THRESHOLD_DP);
- } else {
- return mTranslationX >= dpToPx(DISMISS_DISTANCE_THRESHOLD_DP);
- }
+ float distance = Math.abs(mScreenshotStatic.getTranslationX());
+ return distance >= dpToPx(DISMISS_DISTANCE_THRESHOLD_DP);
}
private ValueAnimator createSwipeDismissAnimation() {
ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
- float startX = mTranslationX;
- float finalX = mDirectionLTR
- ? -1 * (mActionsContainerBackground.getX()
- + mActionsContainerBackground.getWidth())
+ float startX = mScreenshotStatic.getTranslationX();
+ // make sure the UI gets all the way off the screen in the direction of movement
+ // (the actions container background is guaranteed to be both the leftmost and
+ // rightmost UI element in LTR and RTL)
+ float finalX = startX < 0
+ ? -1 * mActionsContainerBackground.getRight()
: mDisplayMetrics.widthPixels;
anim.addUpdateListener(animation -> {
- float translation = MathUtils.lerp(startX, finalX,
- animation.getAnimatedFraction());
+ float translation = MathUtils.lerp(startX, finalX, animation.getAnimatedFraction());
mScreenshotStatic.setTranslationX(translation);
-
setAlpha(1 - animation.getAnimatedFraction());
});
anim.setDuration(400);
@@ -910,9 +876,8 @@
private ValueAnimator createSwipeReturnAnimation() {
ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
- float startX = mTranslationX;
+ float startX = mScreenshotStatic.getTranslationX();
float finalX = 0;
- mTranslationX = 0;
anim.addUpdateListener(animation -> {
float translation = MathUtils.lerp(
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/OperatorNameView.java b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java
index 2aba103..cc7a4f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java
@@ -16,7 +16,6 @@
import android.content.Context;
import android.graphics.Rect;
-import android.net.ConnectivityManager;
import android.os.Bundle;
import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
@@ -123,8 +122,7 @@
.getValue(KEY_SHOW_OPERATOR_NAME, 1) != 0;
setVisibility(showOperatorName ? VISIBLE : GONE);
- boolean hasMobile = ConnectivityManager.from(mContext)
- .isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+ boolean hasMobile = mContext.getSystemService(TelephonyManager.class).isDataCapable();
boolean airplaneMode = WirelessUtils.isAirplaneModeOn(mContext);
if (!hasMobile || airplaneMode) {
setText(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/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 4ef6668..415cfff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -1325,6 +1325,9 @@
traceQsJank(true /* startTracing */, false /* wasCancelled */);
flingSettings(0 /* velocity */, FLING_EXPAND);
mQSDetailDisplayer.showDetailAdapter(qsDetailAdapter, 0, 0);
+ if (mAccessibilityManager.isEnabled()) {
+ mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
+ }
}
public void expandWithoutQs() {
@@ -1824,6 +1827,7 @@
mNotificationContainerParent.setQsExpanded(expanded);
mPulseExpansionHandler.setQsExpanded(expanded);
mKeyguardBypassController.setQSExpanded(expanded);
+ mStatusBarKeyguardViewManager.setQsExpanded(expanded);
}
}
@@ -3389,7 +3393,7 @@
return new TouchHandler() {
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
- if (mStatusBarKeyguardViewManager.isShowingAlternativeAuthOrAnimating()) {
+ if (mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()) {
return true;
}
if (mBlockTouches || mQsFullyExpanded && mQs.disallowPanelTouches()) {
@@ -3420,8 +3424,8 @@
@Override
public boolean onTouch(View v, MotionEvent event) {
final boolean showingOrAnimatingAltAuth =
- mStatusBarKeyguardViewManager.isShowingAlternativeAuthOrAnimating();
- if (showingOrAnimatingAltAuth) {
+ mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating();
+ if (showingOrAnimatingAltAuth && event.getAction() == MotionEvent.ACTION_DOWN) {
mStatusBarKeyguardViewManager.resetAlternateAuth();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index ed4f324..77abe79 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -454,7 +454,7 @@
mView.postOnAnimation(mPostCollapseRunnable);
}
} else if (!mStatusBar.isBouncerShowing()
- && !mStatusBarKeyguardViewManager.isShowingAlternativeAuthOrAnimating()) {
+ && !mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()) {
boolean expands = onEmptySpaceClick(mInitialTouchX);
onTrackingStopped(expands);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 0e1fe22..c83b60d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -4224,7 +4224,7 @@
mNotificationPanelViewController.isLaunchingAffordanceWithPreview();
mScrimController.setLaunchingAffordanceWithPreview(launchingAffordanceWithPreview);
- if (mStatusBarKeyguardViewManager.isShowingAlternativeAuth()) {
+ if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) {
mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED);
} else if (mBouncerShowing) {
// Bouncer needs the front scrim when it's on top of an activity,
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/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index c1f300b..7ee7aa4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -476,11 +476,12 @@
return;
}
- if (mAlternateAuthInterceptor != null
- && mAlternateAuthInterceptor.showAlternativeAuthMethod()) {
- mStatusBar.updateScrimController();
+ if (mAlternateAuthInterceptor != null) {
mAfterKeyguardGoneAction = r;
mKeyguardGoneCancelAction = cancelAction;
+ if (mAlternateAuthInterceptor.showAlternativeAuthMethod()) {
+ mStatusBar.updateScrimController();
+ }
return;
}
@@ -528,7 +529,7 @@
* Stop showing any alternate auth methods
*/
public void resetAlternateAuth() {
- if (mAlternateAuthInterceptor != null && mAlternateAuthInterceptor.reset()) {
+ if (mAlternateAuthInterceptor != null && mAlternateAuthInterceptor.resetForceShow()) {
mStatusBar.updateScrimController();
}
}
@@ -1125,18 +1126,27 @@
setDozing(isDozing);
}
+ /**
+ * Set whether qs is currently expanded
+ */
+ public void setQsExpanded(boolean expanded) {
+ if (mAlternateAuthInterceptor != null) {
+ mAlternateAuthInterceptor.setQsExpanded(expanded);
+ }
+ }
+
public KeyguardBouncer getBouncer() {
return mBouncer;
}
- public boolean isShowingAlternativeAuth() {
+ public boolean isShowingAlternateAuth() {
return mAlternateAuthInterceptor != null
- && mAlternateAuthInterceptor.isShowingAlternativeAuth();
+ && mAlternateAuthInterceptor.isShowingAlternateAuth();
}
- public boolean isShowingAlternativeAuthOrAnimating() {
+ public boolean isShowingAlternateAuthOrAnimating() {
return mAlternateAuthInterceptor != null
- && (mAlternateAuthInterceptor.isShowingAlternativeAuth()
+ && (mAlternateAuthInterceptor.isShowingAlternateAuth()
|| mAlternateAuthInterceptor.isAnimating());
}
@@ -1169,12 +1179,12 @@
* reset the state to the default (only keyguard showing, no auth methods showing)
* @return whether alternative auth method was newly hidden
*/
- boolean reset();
+ boolean resetForceShow();
/**
* @return true if alternative auth method is showing
*/
- boolean isShowingAlternativeAuth();
+ boolean isShowingAlternateAuth();
/**
* print information for the alternate auth interceptor registered
@@ -1185,5 +1195,10 @@
* @return true if the new auth method is currently animating in or out.
*/
boolean isAnimating();
+
+ /**
+ * Set whether qs is currently expanded
+ */
+ void setQsExpanded(boolean expanded);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java
index 5dc9104..f2ee858 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java
@@ -21,7 +21,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.net.ConnectivityManager;
import android.provider.Settings;
import android.telephony.SubscriptionInfo;
import android.telephony.TelephonyManager;
@@ -95,8 +94,7 @@
}
public void update() {
- boolean hasMobile = ConnectivityManager.from(mContext)
- .isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+ boolean hasMobile = mContext.getSystemService(TelephonyManager.class).isDataCapable();
boolean airplaneMode = (Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.AIRPLANE_MODE_ON, 0) == 1);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index db039b4..8a86021 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -79,6 +79,7 @@
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.BitSet;
import java.util.Collections;
import java.util.Comparator;
@@ -241,8 +242,7 @@
mSubscriptionManager = subManager;
mSubDefaults = defaultsHandler;
mConnectivityManager = connectivityManager;
- mHasMobileDataFeature =
- mConnectivityManager.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+ mHasMobileDataFeature = telephonyManager.isDataCapable();
mDemoModeController = demoModeController;
// telephony
@@ -337,10 +337,19 @@
// This callback is invoked a lot (i.e. when RSSI changes), so avoid updating
// icons when connectivity state has remained the same.
- if (network.equals(mLastNetwork) &&
- networkCapabilities.equalsTransportTypes(mLastNetworkCapabilities) &&
- validated == lastValidated) {
- return;
+ if (network.equals(mLastNetwork) && validated == lastValidated) {
+ // Should not rely on getTransportTypes() returning the same order of transport
+ // types. So sort the array before comparing.
+ int[] newTypes = networkCapabilities.getTransportTypes();
+ Arrays.sort(newTypes);
+
+ int[] lastTypes = (mLastNetworkCapabilities != null)
+ ? mLastNetworkCapabilities.getTransportTypes() : null;
+ if (lastTypes != null) Arrays.sort(lastTypes);
+
+ if (Arrays.equals(newTypes, lastTypes)) {
+ return;
+ }
}
mLastNetwork = network;
mLastNetworkCapabilities = networkCapabilities;
@@ -430,14 +439,13 @@
filter.addAction(Intent.ACTION_SERVICE_STATE);
filter.addAction(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED);
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
- filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mReceiverHandler);
mListening = true;
// Initial setup of connectivity. Handled as if we had received a sticky broadcast of
- // ConnectivityManager.CONNECTIVITY_ACTION or ConnectivityManager.INET_CONDITION_ACTION.
+ // ConnectivityManager.CONNECTIVITY_ACTION.
mReceiverHandler.post(this::updateConnectivity);
// Initial setup of WifiSignalController. Handled as if we had received a sticky broadcast
@@ -682,7 +690,6 @@
final String action = intent.getAction();
switch (action) {
case ConnectivityManager.CONNECTIVITY_ACTION:
- case ConnectivityManager.INET_CONDITION_ACTION:
updateConnectivity();
break;
case Intent.ACTION_AIRPLANE_MODE_CHANGED:
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
index bbb2f1a..278663b 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
@@ -65,8 +65,6 @@
"android.theme.customization.accent_color";
static final String OVERLAY_CATEGORY_SYSTEM_PALETTE =
"android.theme.customization.system_palette";
- static final String OVERLAY_CATEGORY_NEUTRAL_PALETTE =
- "android.theme.customization.neutral_palette";
@VisibleForTesting
static final String OVERLAY_CATEGORY_FONT = "android.theme.customization.font";
@VisibleForTesting
@@ -94,7 +92,6 @@
*/
static final List<String> THEME_CATEGORIES = Lists.newArrayList(
OVERLAY_CATEGORY_SYSTEM_PALETTE,
- OVERLAY_CATEGORY_NEUTRAL_PALETTE,
OVERLAY_CATEGORY_ICON_LAUNCHER,
OVERLAY_CATEGORY_SHAPE,
OVERLAY_CATEGORY_FONT,
@@ -108,7 +105,6 @@
@VisibleForTesting
static final Set<String> SYSTEM_USER_CATEGORIES = Sets.newHashSet(
OVERLAY_CATEGORY_SYSTEM_PALETTE,
- OVERLAY_CATEGORY_NEUTRAL_PALETTE,
OVERLAY_CATEGORY_ACCENT_COLOR,
OVERLAY_CATEGORY_FONT,
OVERLAY_CATEGORY_SHAPE,
@@ -131,8 +127,8 @@
mLauncherPackage = launcherPackage;
mThemePickerPackage = themePickerPackage;
mTargetPackageToCategories.put(ANDROID_PACKAGE, Sets.newHashSet(
- OVERLAY_CATEGORY_SYSTEM_PALETTE, OVERLAY_CATEGORY_NEUTRAL_PALETTE,
- OVERLAY_CATEGORY_ACCENT_COLOR, OVERLAY_CATEGORY_FONT, OVERLAY_CATEGORY_SHAPE,
+ OVERLAY_CATEGORY_SYSTEM_PALETTE, OVERLAY_CATEGORY_ACCENT_COLOR,
+ OVERLAY_CATEGORY_FONT, OVERLAY_CATEGORY_SHAPE,
OVERLAY_CATEGORY_ICON_ANDROID));
mTargetPackageToCategories.put(SYSUI_PACKAGE,
Sets.newHashSet(OVERLAY_CATEGORY_ICON_SYSUI));
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index f192287..d317712 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -16,7 +16,6 @@
package com.android.systemui.theme;
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ACCENT_COLOR;
-import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_NEUTRAL_PALETTE;
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_SYSTEM_PALETTE;
import android.annotation.Nullable;
@@ -82,9 +81,8 @@
protected static final String TAG = "ThemeOverlayController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- protected static final int PRIMARY = 0;
- protected static final int SECONDARY = 1;
- protected static final int NEUTRAL = 2;
+ protected static final int NEUTRAL = 0;
+ protected static final int ACCENT = 1;
private final ThemeOverlayApplier mThemeManager;
private final UserManager mUserManager;
@@ -103,8 +101,6 @@
protected int mMainWallpaperColor = Color.TRANSPARENT;
// Accent color extracted from wallpaper, NOT the color used on the overlay
protected int mWallpaperAccentColor = Color.TRANSPARENT;
- // System colors overlay
- private FabricatedOverlay mPrimaryOverlay;
// Accent colors overlay
private FabricatedOverlay mSecondaryOverlay;
// Neutral system colors overlay
@@ -205,7 +201,7 @@
mainColor = Color.TRANSPARENT;
accentCandidate = Color.TRANSPARENT;
} else {
- mainColor = getDominantColor(currentColors);
+ mainColor = getNeutralColor(currentColors);
accentCandidate = getAccentColor(currentColors);
}
@@ -218,13 +214,12 @@
mWallpaperAccentColor = accentCandidate;
if (mIsMonetEnabled) {
- mPrimaryOverlay = getOverlay(mMainWallpaperColor, PRIMARY);
- mSecondaryOverlay = getOverlay(mWallpaperAccentColor, SECONDARY);
+ mSecondaryOverlay = getOverlay(mWallpaperAccentColor, ACCENT);
mNeutralOverlay = getOverlay(mMainWallpaperColor, NEUTRAL);
mNeedsOverlayCreation = true;
if (DEBUG) {
- Log.d(TAG, "fetched overlays. primary: " + mPrimaryOverlay + " secondary: "
- + mSecondaryOverlay + " neutral: " + mNeutralOverlay);
+ Log.d(TAG, "fetched overlays. accent: " + mSecondaryOverlay
+ + " neutral: " + mNeutralOverlay);
}
}
@@ -234,7 +229,7 @@
/**
* Return the main theme color from a given {@link WallpaperColors} instance.
*/
- protected int getDominantColor(@NonNull WallpaperColors wallpaperColors) {
+ protected int getNeutralColor(@NonNull WallpaperColors wallpaperColors) {
return wallpaperColors.getPrimaryColor().toArgb();
}
@@ -283,8 +278,6 @@
if (mIsMonetEnabled && systemPalette != null && systemPalette.getPackageName() != null) {
try {
int color = Integer.parseInt(systemPalette.getPackageName().toLowerCase(), 16);
- mPrimaryOverlay = getOverlay(color, PRIMARY);
- // Neutral palette is always derived from primary color.
mNeutralOverlay = getOverlay(color, NEUTRAL);
mNeedsOverlayCreation = true;
categoryToPackage.remove(OVERLAY_CATEGORY_SYSTEM_PALETTE);
@@ -308,7 +301,7 @@
if (mIsMonetEnabled && accentPalette != null && accentPalette.getPackageName() != null) {
try {
int color = Integer.parseInt(accentPalette.getPackageName().toLowerCase(), 16);
- mSecondaryOverlay = getOverlay(color, SECONDARY);
+ mSecondaryOverlay = getOverlay(color, ACCENT);
mNeedsOverlayCreation = true;
categoryToPackage.remove(OVERLAY_CATEGORY_ACCENT_COLOR);
} catch (NumberFormatException e) {
@@ -326,9 +319,8 @@
// Compatibility with legacy themes, where full packages were defined, instead of just
// colors.
if (!categoryToPackage.containsKey(OVERLAY_CATEGORY_SYSTEM_PALETTE)
- && mPrimaryOverlay != null) {
- categoryToPackage.put(OVERLAY_CATEGORY_SYSTEM_PALETTE, mPrimaryOverlay.getIdentifier());
- categoryToPackage.put(OVERLAY_CATEGORY_NEUTRAL_PALETTE,
+ && mNeutralOverlay != null) {
+ categoryToPackage.put(OVERLAY_CATEGORY_SYSTEM_PALETTE,
mNeutralOverlay.getIdentifier());
}
if (!categoryToPackage.containsKey(OVERLAY_CATEGORY_ACCENT_COLOR)
@@ -350,7 +342,7 @@
if (mNeedsOverlayCreation) {
mNeedsOverlayCreation = false;
mThemeManager.applyCurrentUserOverlays(categoryToPackage, new FabricatedOverlay[] {
- mPrimaryOverlay, mSecondaryOverlay, mNeutralOverlay
+ mSecondaryOverlay, mNeutralOverlay
}, currentUser, managedProfiles);
} else {
mThemeManager.applyCurrentUserOverlays(categoryToPackage, null, currentUser,
@@ -363,7 +355,6 @@
pw.println("mSystemColors=" + mSystemColors);
pw.println("mMainWallpaperColor=" + Integer.toHexString(mMainWallpaperColor));
pw.println("mWallpaperAccentColor=" + Integer.toHexString(mWallpaperAccentColor));
- pw.println("mPrimaryOverlay=" + mPrimaryOverlay);
pw.println("mSecondaryOverlay=" + mSecondaryOverlay);
pw.println("mNeutralOverlay=" + mNeutralOverlay);
pw.println("mIsMonetEnabled=" + mIsMonetEnabled);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
index aa4122f..6f2c0af 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java
@@ -37,7 +37,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.net.ConnectivityManager;
+import android.content.pm.PackageManager;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Handler;
@@ -99,7 +99,7 @@
@Mock
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
- private ConnectivityManager mConnectivityManager;
+ private PackageManager mPackageManager;
@Mock
private TelephonyManager mTelephonyManager;
@Mock
@@ -123,8 +123,8 @@
mTestableLooper = TestableLooper.get(this);
mContext.addMockSystemService(WifiManager.class, mWifiManager);
- mContext.addMockSystemService(ConnectivityManager.class, mConnectivityManager);
- when(mConnectivityManager.isNetworkSupported(anyInt())).thenReturn(true);
+ mContext.addMockSystemService(PackageManager.class, mPackageManager);
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(true);
mContext.addMockSystemService(TelephonyManager.class, mTelephonyManager);
mContext.addMockSystemService(SubscriptionManager.class, mSubscriptionManager);
mContext.getOrCreateTestableResources().addOverride(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index 65f0f7b..4410992 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -170,11 +170,11 @@
mAltAuthInterceptor.showAlternativeAuthMethod(); // force show
assertFalse(mController.shouldPauseAuth());
- assertTrue(mAltAuthInterceptor.isShowingAlternativeAuth());
+ assertTrue(mAltAuthInterceptor.isShowingAlternateAuth());
- mAltAuthInterceptor.reset(); // stop force show
+ mAltAuthInterceptor.resetForceShow(); // stop force show
assertTrue(mController.shouldPauseAuth());
- assertFalse(mAltAuthInterceptor.isShowingAlternativeAuth());
+ assertFalse(mAltAuthInterceptor.isShowingAlternateAuth());
}
@Test
@@ -190,7 +190,7 @@
mController.onViewDetached();
// THEN alt auth state reports not showing
- assertFalse(mAltAuthInterceptor.isShowingAlternativeAuth());
+ assertFalse(mAltAuthInterceptor.isShowingAlternateAuth());
}
private void sendStatusBarStateChanged(int statusBarState) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
index ee98a59..1c7a84a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
@@ -16,20 +16,11 @@
package com.android.systemui.people;
-import static android.app.Notification.CATEGORY_MISSED_CALL;
-import static android.app.people.ConversationStatus.ACTIVITY_BIRTHDAY;
-import static android.app.people.ConversationStatus.ACTIVITY_GAME;
-import static android.app.people.ConversationStatus.ACTIVITY_NEW_STORY;
-import static android.app.people.ConversationStatus.AVAILABILITY_AVAILABLE;
-import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH;
-
import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME;
-import static com.android.systemui.people.PeopleSpaceUtils.REQUIRED_WIDTH_FOR_MEDIUM;
import static com.android.systemui.people.widget.AppWidgetOptionsHelper.OPTIONS_PEOPLE_TILE;
import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -48,7 +39,6 @@
import android.app.NotificationManager;
import android.app.Person;
import android.app.people.ConversationChannel;
-import android.app.people.ConversationStatus;
import android.app.people.IPeopleManager;
import android.app.people.PeopleSpaceTile;
import android.appwidget.AppWidgetManager;
@@ -70,9 +60,7 @@
import android.service.notification.ConversationChannelWrapper;
import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
-import android.view.View;
-import android.widget.RemoteViews;
-import android.widget.TextView;
+import android.util.DisplayMetrics;
import androidx.test.filters.SmallTest;
@@ -117,8 +105,6 @@
private static final int TEST_COLUMN_INDEX = 1;
private static final Uri URI = Uri.parse("fake_uri");
private static final Icon ICON = Icon.createWithResource("package", R.drawable.ic_android);
- private static final String GAME_DESCRIPTION = "Playing a game!";
- private static final CharSequence MISSED_CALL = "Custom missed call message";
private static final String NAME = "username";
private static final Person PERSON = new Person.Builder()
.setName("name")
@@ -126,11 +112,6 @@
.setUri(URI.toString())
.setBot(false)
.build();
- private static final PeopleSpaceTile PERSON_TILE_WITHOUT_NOTIFICATION =
- new PeopleSpaceTile
- .Builder(SHORTCUT_ID_1, NAME, ICON, new Intent())
- .setLastInteractionTimestamp(0L)
- .build();
private static final PeopleSpaceTile PERSON_TILE =
new PeopleSpaceTile
.Builder(SHORTCUT_ID_1, NAME, ICON, new Intent())
@@ -139,16 +120,6 @@
.setNotificationContent(NOTIFICATION_CONTENT)
.setNotificationDataUri(URI)
.build();
- private static final ConversationStatus GAME_STATUS =
- new ConversationStatus
- .Builder(PERSON_TILE.getId(), ACTIVITY_GAME)
- .setDescription(GAME_DESCRIPTION)
- .build();
- private static final ConversationStatus NEW_STORY_WITH_AVAILABILITY =
- new ConversationStatus
- .Builder(PERSON_TILE.getId(), ACTIVITY_NEW_STORY)
- .setAvailability(AVAILABILITY_AVAILABLE)
- .build();
private final ShortcutInfo mShortcutInfo = new ShortcutInfo.Builder(mContext,
SHORTCUT_ID_1).setLongLabel(
@@ -244,6 +215,12 @@
when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITHOUT_SHORTCUT)))
.thenReturn(new Bundle());
+ Configuration configuration = mock(Configuration.class);
+ DisplayMetrics displayMetrics = mock(DisplayMetrics.class);
+ Resources resources = mock(Resources.class);
+ when(mMockContext.getResources()).thenReturn(resources);
+ when(resources.getConfiguration()).thenReturn(configuration);
+ when(resources.getDisplayMetrics()).thenReturn(displayMetrics);
when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver);
when(mMockContentResolver.query(any(Uri.class), any(), anyString(), any(),
isNull())).thenReturn(mMockCursor);
@@ -254,10 +231,6 @@
when(mMockContext.getPackageManager()).thenReturn(mPackageManager);
when(mMockContext.getString(R.string.over_timestamp)).thenReturn(
mContext.getString(R.string.over_timestamp));
- Configuration configuration = mock(Configuration.class);
- Resources resources = mock(Resources.class);
- when(mMockContext.getResources()).thenReturn(resources);
- when(resources.getConfiguration()).thenReturn(configuration);
when(mPackageManager.getApplicationIcon(anyString())).thenReturn(null);
when(mNotificationEntryManager.getVisibleNotifications())
.thenReturn(List.of(mNotificationEntry1, mNotificationEntry2, mNotificationEntry3));
@@ -362,95 +335,6 @@
}
@Test
- public void testGetBackgroundTextFromMessageNoPunctuation() {
- String backgroundText = PeopleSpaceUtils.getBackgroundTextFromMessage("test");
-
- assertThat(backgroundText).isNull();
- }
-
- @Test
- public void testGetBackgroundTextFromMessageSingleExclamation() {
- String backgroundText = PeopleSpaceUtils.getBackgroundTextFromMessage("test!");
-
- assertThat(backgroundText).isNull();
- }
-
- @Test
- public void testGetBackgroundTextFromMessageSingleQuestion() {
- String backgroundText = PeopleSpaceUtils.getBackgroundTextFromMessage("?test");
-
- assertThat(backgroundText).isNull();
- }
-
- @Test
- public void testGetBackgroundTextFromMessageSeparatedMarks() {
- String backgroundText = PeopleSpaceUtils.getBackgroundTextFromMessage("test! right!");
-
- assertThat(backgroundText).isNull();
- }
-
- @Test
- public void testGetBackgroundTextFromMessageDoubleExclamation() {
- String backgroundText = PeopleSpaceUtils.getBackgroundTextFromMessage("!!test");
-
- assertThat(backgroundText).isEqualTo("!");
- }
-
- @Test
- public void testGetBackgroundTextFromMessageDoubleQuestion() {
- String backgroundText = PeopleSpaceUtils.getBackgroundTextFromMessage("test??");
-
- assertThat(backgroundText).isEqualTo("?");
- }
-
- @Test
- public void testGetBackgroundTextFromMessageMixed() {
- String backgroundText = PeopleSpaceUtils.getBackgroundTextFromMessage("test?!");
-
- assertThat(backgroundText).isEqualTo("!?");
- }
-
- @Test
- public void testGetBackgroundTextFromMessageMixedInTheMiddle() {
- String backgroundText = PeopleSpaceUtils.getBackgroundTextFromMessage(
- "test!? in the middle");
-
- assertThat(backgroundText).isEqualTo("!?");
- }
-
- @Test
- public void testGetBackgroundTextFromMessageMixedDifferentOrder() {
- String backgroundText = PeopleSpaceUtils.getBackgroundTextFromMessage(
- "test!? in the middle");
-
- assertThat(backgroundText).isEqualTo("!?");
- }
-
- @Test
- public void testGetBackgroundTextFromMessageMultiple() {
- String backgroundText = PeopleSpaceUtils.getBackgroundTextFromMessage(
- "test!?!!? in the middle");
-
- assertThat(backgroundText).isEqualTo("!?");
- }
-
- @Test
- public void testGetBackgroundTextFromMessageQuestionFirst() {
- String backgroundText = PeopleSpaceUtils.getBackgroundTextFromMessage(
- "test?? in the middle!!");
-
- assertThat(backgroundText).isEqualTo("?");
- }
-
- @Test
- public void testGetBackgroundTextFromMessageExclamationFirst() {
- String backgroundText = PeopleSpaceUtils.getBackgroundTextFromMessage(
- "test!! in the middle??");
-
- assertThat(backgroundText).isEqualTo("!");
- }
-
- @Test
public void testGetLastMessagingStyleMessage() {
StatusBarNotification sbn = new SbnBuilder()
.setNotification(mNotification1)
@@ -687,225 +571,6 @@
any());
}
- @Test
- public void testCreateRemoteViewsWithLastInteractionTime() {
- RemoteViews views = PeopleSpaceUtils.createRemoteViews(mMockContext,
- PERSON_TILE_WITHOUT_NOTIFICATION, 0, mOptions);
- View result = views.apply(mContext, null);
-
- TextView name = (TextView) result.findViewById(R.id.name);
- assertEquals(name.getText(), NAME);
- // Has last interaction.
- TextView lastInteraction = (TextView) result.findViewById(R.id.last_interaction);
- assertEquals(lastInteraction.getText(), mContext.getString(R.string.basic_status));
- // No availability.
- View availability = result.findViewById(R.id.availability);
- assertEquals(View.GONE, availability.getVisibility());
- // Shows person icon.
- View personIcon = result.findViewById(R.id.person_icon);
- assertEquals(View.VISIBLE, personIcon.getVisibility());
- // No status.
- assertThat((View) result.findViewById(R.id.text_content)).isNull();
-
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, REQUIRED_WIDTH_FOR_MEDIUM - 1);
- RemoteViews smallView = PeopleSpaceUtils.createRemoteViews(mContext,
- PERSON_TILE_WITHOUT_NOTIFICATION, 0, mOptions);
- View smallResult = smallView.apply(mContext, null);
-
- // Show name over predefined icon.
- assertEquals(View.VISIBLE, smallResult.findViewById(R.id.name).getVisibility());
- assertEquals(View.GONE, smallResult.findViewById(R.id.predefined_icon).getVisibility());
- // Shows person icon.
- assertEquals(View.VISIBLE, smallResult.findViewById(R.id.person_icon).getVisibility());
- }
-
- @Test
- public void testCreateRemoteViewsWithGameTypeOnlyIsIgnored() {
- PeopleSpaceTile tileWithAvailabilityAndNewStory =
- PERSON_TILE_WITHOUT_NOTIFICATION.toBuilder().setStatuses(
- Arrays.asList(NEW_STORY_WITH_AVAILABILITY,
- new ConversationStatus.Builder(
- PERSON_TILE_WITHOUT_NOTIFICATION.getId(),
- ACTIVITY_GAME).build())).build();
- RemoteViews views = PeopleSpaceUtils.createRemoteViews(mMockContext,
- tileWithAvailabilityAndNewStory, 0, mOptions);
- View result = views.apply(mContext, null);
-
- TextView name = (TextView) result.findViewById(R.id.name);
- assertEquals(name.getText(), NAME);
- // Has last interaction over status.
- TextView lastInteraction = (TextView) result.findViewById(R.id.last_interaction);
- assertEquals(lastInteraction.getText(), mContext.getString(R.string.basic_status));
- // Has availability.
- View availability = result.findViewById(R.id.availability);
- assertEquals(View.VISIBLE, availability.getVisibility());
- // Has person icon.
- View personIcon = result.findViewById(R.id.person_icon);
- assertEquals(View.VISIBLE, personIcon.getVisibility());
- // No status.
- assertThat((View) result.findViewById(R.id.text_content)).isNull();
-
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, REQUIRED_WIDTH_FOR_MEDIUM - 1);
- RemoteViews smallView = PeopleSpaceUtils.createRemoteViews(mContext,
- tileWithAvailabilityAndNewStory, 0, mOptions);
- View smallResult = smallView.apply(mContext, null);
-
- // Show name rather than game type.
- assertEquals(View.VISIBLE, smallResult.findViewById(R.id.name).getVisibility());
- assertEquals(View.GONE, smallResult.findViewById(R.id.predefined_icon).getVisibility());
- // Has person icon.
- assertEquals(View.VISIBLE,
- smallResult.findViewById(R.id.person_icon).getVisibility());
- }
-
- @Test
- public void testCreateRemoteViewsWithBirthdayTypeOnlyIsNotIgnored() {
- PeopleSpaceTile tileWithStatusTemplate =
- PERSON_TILE_WITHOUT_NOTIFICATION.toBuilder().setStatuses(
- Arrays.asList(
- NEW_STORY_WITH_AVAILABILITY, new ConversationStatus.Builder(
- PERSON_TILE_WITHOUT_NOTIFICATION.getId(),
- ACTIVITY_BIRTHDAY).build())).build();
- RemoteViews views = PeopleSpaceUtils.createRemoteViews(mContext,
- tileWithStatusTemplate, 0, mOptions);
- View result = views.apply(mContext, null);
-
- TextView name = (TextView) result.findViewById(R.id.name);
- assertEquals(name.getText(), NAME);
- // Has availability.
- View availability = result.findViewById(R.id.availability);
- assertEquals(View.VISIBLE, availability.getVisibility());
- // Has person icon.
- View personIcon = result.findViewById(R.id.person_icon);
- assertEquals(View.VISIBLE, personIcon.getVisibility());
- // Has status text from backup text.
- TextView statusContent = (TextView) result.findViewById(R.id.text_content);
- assertEquals(statusContent.getText(), mContext.getString(R.string.birthday_status));
-
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, REQUIRED_WIDTH_FOR_MEDIUM - 1);
- RemoteViews smallView = PeopleSpaceUtils.createRemoteViews(mContext,
- tileWithStatusTemplate, 0, mOptions);
- View smallResult = smallView.apply(mContext, null);
-
- // Show icon instead of name.
- assertEquals(View.GONE, smallResult.findViewById(R.id.name).getVisibility());
- assertEquals(View.VISIBLE,
- smallResult.findViewById(R.id.predefined_icon).getVisibility());
- // Has person icon.
- assertEquals(View.VISIBLE,
- smallResult.findViewById(R.id.person_icon).getVisibility());
- }
-
- @Test
- public void testCreateRemoteViewsWithStatusTemplate() {
- PeopleSpaceTile tileWithStatusTemplate =
- PERSON_TILE_WITHOUT_NOTIFICATION.toBuilder().setStatuses(
- Arrays.asList(GAME_STATUS,
- NEW_STORY_WITH_AVAILABILITY)).build();
- RemoteViews views = PeopleSpaceUtils.createRemoteViews(mContext,
- tileWithStatusTemplate, 0, mOptions);
- View result = views.apply(mContext, null);
-
- TextView name = (TextView) result.findViewById(R.id.name);
- assertEquals(name.getText(), NAME);
- // Has availability.
- View availability = result.findViewById(R.id.availability);
- assertEquals(View.VISIBLE, availability.getVisibility());
- // Has person icon.
- View personIcon = result.findViewById(R.id.person_icon);
- assertEquals(View.VISIBLE, personIcon.getVisibility());
- // Has status.
- TextView statusContent = (TextView) result.findViewById(R.id.text_content);
- assertEquals(statusContent.getText(), GAME_DESCRIPTION);
-
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, REQUIRED_WIDTH_FOR_MEDIUM - 1);
- RemoteViews smallView = PeopleSpaceUtils.createRemoteViews(mContext,
- tileWithStatusTemplate, 0, mOptions);
- View smallResult = smallView.apply(mContext, null);
-
- // Show icon instead of name.
- assertEquals(View.GONE, smallResult.findViewById(R.id.name).getVisibility());
- assertEquals(View.VISIBLE,
- smallResult.findViewById(R.id.predefined_icon).getVisibility());
- // Has person icon.
- assertEquals(View.VISIBLE,
- smallResult.findViewById(R.id.person_icon).getVisibility());
- }
-
- @Test
- public void testCreateRemoteViewsWithMissedCallNotification() {
- PeopleSpaceTile tileWithMissedCallNotification = PERSON_TILE.toBuilder()
- .setNotificationDataUri(null)
- .setNotificationCategory(CATEGORY_MISSED_CALL)
- .setNotificationContent(MISSED_CALL)
- .build();
- RemoteViews views = PeopleSpaceUtils.createRemoteViews(mContext,
- tileWithMissedCallNotification, 0, mOptions);
- View result = views.apply(mContext, null);
-
- TextView name = (TextView) result.findViewById(R.id.name);
- assertEquals(name.getText(), NAME);
- // Has availability.
- View availability = result.findViewById(R.id.availability);
- assertEquals(View.GONE, availability.getVisibility());
- // Has person icon.
- View personIcon = result.findViewById(R.id.person_icon);
- assertEquals(View.VISIBLE, personIcon.getVisibility());
- // Has missed call notification content.
- TextView statusContent = (TextView) result.findViewById(R.id.text_content);
- assertEquals(statusContent.getText(), MISSED_CALL);
-
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, REQUIRED_WIDTH_FOR_MEDIUM - 1);
- RemoteViews smallView = PeopleSpaceUtils.createRemoteViews(mContext,
- tileWithMissedCallNotification, 0, mOptions);
- View smallResult = smallView.apply(mContext, null);
-
- // Show icon instead of name.
- assertEquals(View.GONE, smallResult.findViewById(R.id.name).getVisibility());
- assertEquals(View.VISIBLE,
- smallResult.findViewById(R.id.predefined_icon).getVisibility());
- // Has person icon.
- assertEquals(View.VISIBLE, smallResult.findViewById(R.id.person_icon).getVisibility());
- }
-
- @Test
- public void testCreateRemoteViewsWithNotificationTemplate() {
- PeopleSpaceTile tileWithStatusAndNotification = PERSON_TILE.toBuilder()
- .setNotificationDataUri(null)
- .setStatuses(Arrays.asList(GAME_STATUS,
- NEW_STORY_WITH_AVAILABILITY)).build();
- RemoteViews views = PeopleSpaceUtils.createRemoteViews(mContext,
- tileWithStatusAndNotification, 0, mOptions);
- View result = views.apply(mContext, null);
-
- TextView name = (TextView) result.findViewById(R.id.name);
- assertEquals(name.getText(), NAME);
- TextView subtext = (TextView) result.findViewById(R.id.subtext);
- assertEquals(View.GONE, subtext.getVisibility());
- // Has availability.
- View availability = result.findViewById(R.id.availability);
- assertEquals(View.VISIBLE, availability.getVisibility());
- // Has person icon.
- View personIcon = result.findViewById(R.id.person_icon);
- assertEquals(View.VISIBLE, personIcon.getVisibility());
- // Has notification content.
- TextView statusContent = (TextView) result.findViewById(R.id.text_content);
- assertEquals(statusContent.getText(), NOTIFICATION_CONTENT);
-
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, REQUIRED_WIDTH_FOR_MEDIUM - 1);
- RemoteViews smallView = PeopleSpaceUtils.createRemoteViews(mContext,
- tileWithStatusAndNotification, 0, mOptions);
- View smallResult = smallView.apply(mContext, null);
-
- // Show icon instead of name.
- assertEquals(View.GONE, smallResult.findViewById(R.id.name).getVisibility());
- assertEquals(View.VISIBLE,
- smallResult.findViewById(R.id.predefined_icon).getVisibility());
- // Has person icon.
- assertEquals(View.VISIBLE,
- smallResult.findViewById(R.id.person_icon).getVisibility());
- }
-
private ConversationChannelWrapper getConversationChannelWrapper(String shortcutId,
boolean importantConversation, long lastInteractionTimestamp) throws Exception {
ConversationChannelWrapper convo = new ConversationChannelWrapper();
@@ -931,13 +596,4 @@
eq(shortcutId))).thenReturn(lastInteractionTimestamp);
return convo;
}
-
- private ConversationChannel getConversationChannelWithoutTimestamp(String shortcutId)
- throws Exception {
- ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(mContext, shortcutId).setLongLabel(
- "name").build();
- ConversationChannel convo = new ConversationChannel(shortcutInfo, 0, null, null,
- 0L, false);
- return convo;
- }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
new file mode 100644
index 0000000..39bf060
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
@@ -0,0 +1,587 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.people;
+
+import static android.app.Notification.CATEGORY_MISSED_CALL;
+import static android.app.people.ConversationStatus.ACTIVITY_BIRTHDAY;
+import static android.app.people.ConversationStatus.ACTIVITY_GAME;
+import static android.app.people.ConversationStatus.ACTIVITY_NEW_STORY;
+import static android.app.people.ConversationStatus.AVAILABILITY_AVAILABLE;
+import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH;
+
+import static com.android.systemui.people.widget.AppWidgetOptionsHelper.OPTIONS_PEOPLE_TILE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.people.ConversationStatus;
+import android.app.people.PeopleSpaceTile;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.os.Bundle;
+import android.testing.AndroidTestingRunner;
+import android.util.DisplayMetrics;
+import android.view.View;
+import android.widget.RemoteViews;
+import android.widget.TextView;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class PeopleTileViewHelperTest extends SysuiTestCase {
+
+ private static final String SHORTCUT_ID_1 = "101";
+ private static final String NOTIFICATION_KEY = "notification_key";
+ private static final String NOTIFICATION_CONTENT = "notification_content";
+ private static final Uri URI = Uri.parse("fake_uri");
+ private static final Icon ICON = Icon.createWithResource("package", R.drawable.ic_android);
+ private static final String GAME_DESCRIPTION = "Playing a game!";
+ private static final CharSequence MISSED_CALL = "Custom missed call message";
+ private static final String NAME = "username";
+ private static final PeopleSpaceTile PERSON_TILE_WITHOUT_NOTIFICATION =
+ new PeopleSpaceTile
+ .Builder(SHORTCUT_ID_1, NAME, ICON, new Intent())
+ .setLastInteractionTimestamp(0L)
+ .build();
+ private static final PeopleSpaceTile PERSON_TILE =
+ new PeopleSpaceTile
+ .Builder(SHORTCUT_ID_1, NAME, ICON, new Intent())
+ .setLastInteractionTimestamp(123L)
+ .setNotificationKey(NOTIFICATION_KEY)
+ .setNotificationContent(NOTIFICATION_CONTENT)
+ .setNotificationDataUri(URI)
+ .build();
+ private static final ConversationStatus GAME_STATUS =
+ new ConversationStatus
+ .Builder(PERSON_TILE.getId(), ACTIVITY_GAME)
+ .setDescription(GAME_DESCRIPTION)
+ .build();
+ private static final ConversationStatus NEW_STORY_WITH_AVAILABILITY =
+ new ConversationStatus
+ .Builder(PERSON_TILE.getId(), ACTIVITY_NEW_STORY)
+ .setAvailability(AVAILABILITY_AVAILABLE)
+ .build();
+
+ @Mock
+ private Context mMockContext;
+ @Mock
+ private PackageManager mPackageManager;
+
+ private Bundle mOptions;
+ private PeopleTileViewHelper mPeopleTileViewHelper;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mOptions = new Bundle();
+ mOptions.putParcelable(OPTIONS_PEOPLE_TILE, PERSON_TILE);
+
+ when(mMockContext.getString(R.string.birthday_status)).thenReturn(
+ mContext.getString(R.string.birthday_status));
+ when(mMockContext.getString(R.string.basic_status)).thenReturn(
+ mContext.getString(R.string.basic_status));
+ when(mMockContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mMockContext.getString(R.string.over_timestamp)).thenReturn(
+ mContext.getString(R.string.over_timestamp));
+ Configuration configuration = mock(Configuration.class);
+ DisplayMetrics displayMetrics = mock(DisplayMetrics.class);
+ Resources resources = mock(Resources.class);
+ when(mMockContext.getResources()).thenReturn(resources);
+ when(resources.getConfiguration()).thenReturn(configuration);
+ when(resources.getDisplayMetrics()).thenReturn(displayMetrics);
+ TextView textView = mock(TextView.class);
+ // when(new TextView(mMockContext)).thenReturn(textView);
+ when(textView.getLineHeight()).thenReturn(16);
+ when(mPackageManager.getApplicationIcon(anyString())).thenReturn(null);
+ mPeopleTileViewHelper = new PeopleTileViewHelper(mContext,
+ PERSON_TILE, 0, mOptions);
+ }
+
+ @Test
+ public void testCreateRemoteViewsWithLastInteractionTime() {
+ RemoteViews views = new PeopleTileViewHelper(mContext,
+ PERSON_TILE_WITHOUT_NOTIFICATION, 0, mOptions).getViews();
+ View result = views.apply(mContext, null);
+
+ TextView name = (TextView) result.findViewById(R.id.name);
+ assertEquals(name.getText(), NAME);
+ // Has last interaction.
+ TextView lastInteraction = (TextView) result.findViewById(R.id.last_interaction);
+ assertEquals(lastInteraction.getText(), mContext.getString(R.string.basic_status));
+ // No availability.
+ assertEquals(View.GONE, result.findViewById(R.id.availability).getVisibility());
+ // Shows person icon.
+ assertEquals(View.VISIBLE, result.findViewById(R.id.person_icon).getVisibility());
+ // No status.
+ assertThat((View) result.findViewById(R.id.text_content)).isNull();
+
+ mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ getSizeInDp(R.dimen.required_width_for_medium) - 1);
+ RemoteViews smallView = new PeopleTileViewHelper(mContext,
+ PERSON_TILE_WITHOUT_NOTIFICATION, 0, mOptions).getViews();
+ View smallResult = smallView.apply(mContext, null);
+
+ // Show name over predefined icon.
+ assertEquals(View.VISIBLE, smallResult.findViewById(R.id.name).getVisibility());
+ assertEquals(View.GONE, smallResult.findViewById(R.id.predefined_icon).getVisibility());
+ // Shows person icon.
+ assertEquals(View.VISIBLE, smallResult.findViewById(R.id.person_icon).getVisibility());
+
+ mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ getSizeInDp(R.dimen.required_width_for_large));
+ mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ getSizeInDp(R.dimen.required_height_for_large));
+ RemoteViews largeView = new PeopleTileViewHelper(mContext,
+ PERSON_TILE_WITHOUT_NOTIFICATION, 0, mOptions).getViews();
+ View largeResult = largeView.apply(mContext, null);
+
+ name = (TextView) largeResult.findViewById(R.id.name);
+ assertEquals(name.getText(), NAME);
+ // Has last interaction.
+ lastInteraction = (TextView) result.findViewById(R.id.last_interaction);
+ assertEquals(lastInteraction.getText(), mContext.getString(R.string.basic_status));
+ // No availability.
+ assertEquals(View.GONE, result.findViewById(R.id.availability).getVisibility());
+ // Shows person icon.
+ assertEquals(View.VISIBLE, result.findViewById(R.id.person_icon).getVisibility());
+ // No status.
+ assertThat((View) result.findViewById(R.id.text_content)).isNull();
+ }
+
+ @Test
+ public void testCreateRemoteViewsWithGameTypeOnlyIsIgnored() {
+ PeopleSpaceTile tileWithAvailabilityAndNewStory =
+ PERSON_TILE_WITHOUT_NOTIFICATION.toBuilder().setStatuses(
+ Arrays.asList(NEW_STORY_WITH_AVAILABILITY,
+ new ConversationStatus.Builder(
+ PERSON_TILE_WITHOUT_NOTIFICATION.getId(),
+ ACTIVITY_GAME).build())).build();
+ RemoteViews views = new PeopleTileViewHelper(mContext,
+ tileWithAvailabilityAndNewStory, 0, mOptions).getViews();
+ View result = views.apply(mContext, null);
+
+ TextView name = (TextView) result.findViewById(R.id.name);
+ assertEquals(name.getText(), NAME);
+ // Has last interaction over status.
+ TextView lastInteraction = (TextView) result.findViewById(R.id.last_interaction);
+ assertEquals(lastInteraction.getText(), mContext.getString(R.string.basic_status));
+ // Has availability.
+ assertEquals(View.VISIBLE, result.findViewById(R.id.availability).getVisibility());
+ // Has person icon.
+ assertEquals(View.VISIBLE, result.findViewById(R.id.person_icon).getVisibility());
+ // No status.
+ assertThat((View) result.findViewById(R.id.text_content)).isNull();
+
+ mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ getSizeInDp(R.dimen.required_width_for_medium) - 1);
+ RemoteViews smallView = new PeopleTileViewHelper(mContext,
+ tileWithAvailabilityAndNewStory, 0, mOptions).getViews();
+ View smallResult = smallView.apply(mContext, null);
+
+ // Show name rather than game type.
+ assertEquals(View.VISIBLE, smallResult.findViewById(R.id.name).getVisibility());
+ assertEquals(View.GONE, smallResult.findViewById(R.id.predefined_icon).getVisibility());
+ // Has person icon.
+ assertEquals(View.VISIBLE,
+ smallResult.findViewById(R.id.person_icon).getVisibility());
+
+ mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ getSizeInDp(R.dimen.required_width_for_large));
+ mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ getSizeInDp(R.dimen.required_height_for_large));
+ RemoteViews largeView = new PeopleTileViewHelper(mContext,
+ tileWithAvailabilityAndNewStory, 0, mOptions).getViews();
+ View largeResult = largeView.apply(mContext, null);
+
+ name = (TextView) largeResult.findViewById(R.id.name);
+ assertEquals(name.getText(), NAME);
+ // Has last interaction.
+ lastInteraction = (TextView) result.findViewById(R.id.last_interaction);
+ assertEquals(lastInteraction.getText(), mContext.getString(R.string.basic_status));
+ // Has availability.
+ assertEquals(View.VISIBLE, result.findViewById(R.id.availability).getVisibility());
+ // Shows person icon.
+ assertEquals(View.VISIBLE, result.findViewById(R.id.person_icon).getVisibility());
+ // No status.
+ assertThat((View) result.findViewById(R.id.text_content)).isNull();
+ }
+
+ @Test
+ public void testCreateRemoteViewsWithBirthdayTypeOnlyIsNotIgnored() {
+ PeopleSpaceTile tileWithStatusTemplate =
+ PERSON_TILE_WITHOUT_NOTIFICATION.toBuilder().setStatuses(
+ Arrays.asList(
+ NEW_STORY_WITH_AVAILABILITY, new ConversationStatus.Builder(
+ PERSON_TILE_WITHOUT_NOTIFICATION.getId(),
+ ACTIVITY_BIRTHDAY).build())).build();
+ RemoteViews views = new PeopleTileViewHelper(mContext,
+ tileWithStatusTemplate, 0, mOptions).getViews();
+ View result = views.apply(mContext, null);
+
+ TextView name = (TextView) result.findViewById(R.id.name);
+ assertEquals(name.getText(), NAME);
+ assertEquals(View.GONE, result.findViewById(R.id.subtext).getVisibility());
+ assertEquals(View.VISIBLE, result.findViewById(R.id.predefined_icon).getVisibility());
+ // Has availability.
+ assertEquals(View.VISIBLE, result.findViewById(R.id.availability).getVisibility());
+ // Has person icon.
+ assertEquals(View.VISIBLE, result.findViewById(R.id.person_icon).getVisibility());
+ // Has status text from backup text.
+ TextView statusContent = (TextView) result.findViewById(R.id.text_content);
+ assertEquals(View.VISIBLE, statusContent.getVisibility());
+ assertEquals(statusContent.getText(), mContext.getString(R.string.birthday_status));
+ assertThat(statusContent.getMaxLines()).isEqualTo(3);
+
+ mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ getSizeInDp(R.dimen.required_width_for_medium) - 1);
+ RemoteViews smallView = new PeopleTileViewHelper(mContext,
+ tileWithStatusTemplate, 0, mOptions).getViews();
+ View smallResult = smallView.apply(mContext, null);
+
+ // Show icon instead of name.
+ assertEquals(View.GONE, smallResult.findViewById(R.id.name).getVisibility());
+ assertEquals(View.VISIBLE,
+ smallResult.findViewById(R.id.predefined_icon).getVisibility());
+ // Has person icon.
+ assertEquals(View.VISIBLE,
+ smallResult.findViewById(R.id.person_icon).getVisibility());
+
+ mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ getSizeInDp(R.dimen.required_width_for_large));
+ mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ getSizeInDp(R.dimen.required_height_for_large));
+ RemoteViews largeView = new PeopleTileViewHelper(mContext,
+ tileWithStatusTemplate, 0, mOptions).getViews();
+ View largeResult = largeView.apply(mContext, null);
+
+ name = (TextView) largeResult.findViewById(R.id.name);
+ assertEquals(name.getText(), NAME);
+ assertEquals(View.GONE, largeResult.findViewById(R.id.subtext).getVisibility());
+ assertEquals(View.VISIBLE, largeResult.findViewById(R.id.predefined_icon).getVisibility());
+ // Has availability.
+ assertEquals(View.VISIBLE, largeResult.findViewById(R.id.availability).getVisibility());
+ // Has person icon.
+ View personIcon = largeResult.findViewById(R.id.person_icon);
+ assertEquals(View.VISIBLE, personIcon.getVisibility());
+ // Has notification content.
+ statusContent = (TextView) largeResult.findViewById(R.id.text_content);
+ assertEquals(View.VISIBLE, statusContent.getVisibility());
+ assertEquals(statusContent.getText(), mContext.getString(R.string.birthday_status));
+ assertThat(statusContent.getMaxLines()).isEqualTo(3);
+ }
+
+ @Test
+ public void testCreateRemoteViewsWithStatusTemplate() {
+ PeopleSpaceTile tileWithStatusTemplate =
+ PERSON_TILE_WITHOUT_NOTIFICATION.toBuilder().setStatuses(
+ Arrays.asList(GAME_STATUS,
+ NEW_STORY_WITH_AVAILABILITY)).build();
+ RemoteViews views = new PeopleTileViewHelper(mContext,
+ tileWithStatusTemplate, 0, mOptions).getViews();
+ View result = views.apply(mContext, null);
+
+ TextView name = (TextView) result.findViewById(R.id.name);
+ assertEquals(name.getText(), NAME);
+ assertEquals(View.GONE, result.findViewById(R.id.subtext).getVisibility());
+ assertEquals(View.VISIBLE, result.findViewById(R.id.predefined_icon).getVisibility());
+ // Has availability.
+ assertEquals(View.VISIBLE, result.findViewById(R.id.availability).getVisibility());
+ // Has person icon.
+ assertEquals(View.VISIBLE, result.findViewById(R.id.person_icon).getVisibility());
+ // Has status.
+ TextView statusContent = (TextView) result.findViewById(R.id.text_content);
+ assertEquals(statusContent.getText(), GAME_DESCRIPTION);
+ assertThat(statusContent.getMaxLines()).isEqualTo(3);
+
+ mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ getSizeInDp(R.dimen.required_width_for_medium) - 1);
+ RemoteViews smallView = new PeopleTileViewHelper(mContext,
+ tileWithStatusTemplate, 0, mOptions).getViews();
+ View smallResult = smallView.apply(mContext, null);
+
+ // Show icon instead of name.
+ assertEquals(View.GONE, smallResult.findViewById(R.id.name).getVisibility());
+ assertEquals(View.VISIBLE,
+ smallResult.findViewById(R.id.predefined_icon).getVisibility());
+ // Has person icon.
+ assertEquals(View.VISIBLE,
+ smallResult.findViewById(R.id.person_icon).getVisibility());
+
+ mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ getSizeInDp(R.dimen.required_width_for_large));
+ mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ getSizeInDp(R.dimen.required_height_for_large));
+ RemoteViews largeView = new PeopleTileViewHelper(mContext,
+ tileWithStatusTemplate, 0, mOptions).getViews();
+ View largeResult = largeView.apply(mContext, null);
+
+ name = (TextView) largeResult.findViewById(R.id.name);
+ assertEquals(name.getText(), NAME);
+ assertEquals(View.GONE, largeResult.findViewById(R.id.subtext).getVisibility());
+ assertEquals(View.VISIBLE, largeResult.findViewById(R.id.predefined_icon).getVisibility());
+ // Has availability.
+ assertEquals(View.VISIBLE, largeResult.findViewById(R.id.availability).getVisibility());
+ // Has person icon.
+ View personIcon = largeResult.findViewById(R.id.person_icon);
+ assertEquals(View.VISIBLE, personIcon.getVisibility());
+ // Has notification content.
+ statusContent = (TextView) largeResult.findViewById(R.id.text_content);
+ assertEquals(View.VISIBLE, statusContent.getVisibility());
+ assertEquals(statusContent.getText(), GAME_DESCRIPTION);
+ assertThat(statusContent.getMaxLines()).isEqualTo(3);
+ }
+
+ @Test
+ public void testCreateRemoteViewsWithMissedCallNotification() {
+ PeopleSpaceTile tileWithMissedCallNotification = PERSON_TILE.toBuilder()
+ .setNotificationDataUri(null)
+ .setNotificationCategory(CATEGORY_MISSED_CALL)
+ .setNotificationContent(MISSED_CALL)
+ .build();
+ RemoteViews views = new PeopleTileViewHelper(mContext,
+ tileWithMissedCallNotification, 0, mOptions).getViews();
+ View result = views.apply(mContext, null);
+
+ TextView name = (TextView) result.findViewById(R.id.name);
+ assertEquals(name.getText(), NAME);
+ assertEquals(View.GONE, result.findViewById(R.id.subtext).getVisibility());
+ assertEquals(View.VISIBLE, result.findViewById(R.id.predefined_icon).getVisibility());
+ // Has availability.
+ assertEquals(View.GONE, result.findViewById(R.id.availability).getVisibility());
+ // Has person icon.
+ assertEquals(View.VISIBLE, result.findViewById(R.id.person_icon).getVisibility());
+ // Has missed call notification content.
+ TextView statusContent = (TextView) result.findViewById(R.id.text_content);
+ assertEquals(View.VISIBLE, statusContent.getVisibility());
+ assertEquals(statusContent.getText(), MISSED_CALL);
+ assertThat(statusContent.getMaxLines()).isEqualTo(3);
+
+ mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ getSizeInDp(R.dimen.required_width_for_medium) - 1);
+ RemoteViews smallView = new PeopleTileViewHelper(mContext,
+ tileWithMissedCallNotification, 0, mOptions).getViews();
+ View smallResult = smallView.apply(mContext, null);
+
+ // Show icon instead of name.
+ assertEquals(View.GONE, smallResult.findViewById(R.id.name).getVisibility());
+ assertEquals(View.VISIBLE,
+ smallResult.findViewById(R.id.predefined_icon).getVisibility());
+ // Has person icon.
+ assertEquals(View.VISIBLE, smallResult.findViewById(R.id.person_icon).getVisibility());
+
+ mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ getSizeInDp(R.dimen.required_width_for_large));
+ mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ getSizeInDp(R.dimen.required_height_for_large));
+ RemoteViews largeView = new PeopleTileViewHelper(mContext,
+ tileWithMissedCallNotification, 0, mOptions).getViews();
+ View largeResult = largeView.apply(mContext, null);
+
+ name = (TextView) largeResult.findViewById(R.id.name);
+ assertEquals(name.getText(), NAME);
+ assertEquals(View.GONE, largeResult.findViewById(R.id.subtext).getVisibility());
+ assertEquals(View.VISIBLE, largeResult.findViewById(R.id.predefined_icon).getVisibility());
+ // Has availability.
+ assertEquals(View.GONE, largeResult.findViewById(R.id.availability).getVisibility());
+ // Has person icon.
+ View personIcon = largeResult.findViewById(R.id.person_icon);
+ assertEquals(View.VISIBLE, personIcon.getVisibility());
+ // Has notification content.
+ statusContent = (TextView) largeResult.findViewById(R.id.text_content);
+ assertEquals(View.VISIBLE, statusContent.getVisibility());
+ assertEquals(statusContent.getText(), MISSED_CALL);
+ assertThat(statusContent.getMaxLines()).isEqualTo(3);
+ }
+
+ @Test
+ public void testCreateRemoteViewsWithNotificationTemplate() {
+ PeopleSpaceTile tileWithStatusAndNotification = PERSON_TILE.toBuilder()
+ .setNotificationDataUri(null)
+ .setStatuses(Arrays.asList(GAME_STATUS,
+ NEW_STORY_WITH_AVAILABILITY)).build();
+ RemoteViews views = new PeopleTileViewHelper(mContext,
+ tileWithStatusAndNotification, 0, mOptions).getViews();
+ View result = views.apply(mContext, null);
+
+ TextView name = (TextView) result.findViewById(R.id.name);
+ assertEquals(name.getText(), NAME);
+ assertEquals(View.GONE, result.findViewById(R.id.subtext).getVisibility());
+ assertEquals(View.GONE, result.findViewById(R.id.predefined_icon).getVisibility());
+ // Has availability.
+ assertEquals(View.VISIBLE, result.findViewById(R.id.availability).getVisibility());
+ // Has person icon.
+ assertEquals(View.VISIBLE, result.findViewById(R.id.person_icon).getVisibility());
+ // Has notification content.
+ TextView statusContent = (TextView) result.findViewById(R.id.text_content);
+ assertEquals(View.VISIBLE, statusContent.getVisibility());
+ assertEquals(statusContent.getText(), NOTIFICATION_CONTENT);
+ assertThat(statusContent.getMaxLines()).isEqualTo(3);
+
+ mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ getSizeInDp(R.dimen.required_width_for_medium) - 1);
+ RemoteViews smallView = new PeopleTileViewHelper(mContext,
+ tileWithStatusAndNotification, 0, mOptions).getViews();
+ View smallResult = smallView.apply(mContext, null);
+
+ // Show icon instead of name.
+ assertEquals(View.GONE, smallResult.findViewById(R.id.name).getVisibility());
+ assertEquals(View.VISIBLE,
+ smallResult.findViewById(R.id.predefined_icon).getVisibility());
+ // Has person icon.
+ assertEquals(View.VISIBLE,
+ smallResult.findViewById(R.id.person_icon).getVisibility());
+
+ mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ getSizeInDp(R.dimen.required_width_for_large));
+ mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+ getSizeInDp(R.dimen.required_height_for_large));
+ RemoteViews largeView = new PeopleTileViewHelper(mContext,
+ tileWithStatusAndNotification, 0, mOptions).getViews();
+ View largeResult = largeView.apply(mContext, null);
+
+ name = (TextView) largeResult.findViewById(R.id.name);
+ assertEquals(name.getText(), NAME);
+ assertEquals(View.GONE, largeResult.findViewById(R.id.subtext).getVisibility());
+ assertEquals(View.GONE, largeResult.findViewById(R.id.predefined_icon).getVisibility());
+ // Has availability.
+ assertEquals(View.VISIBLE, largeResult.findViewById(R.id.availability).getVisibility());
+ // Has person icon.
+ View personIcon = largeResult.findViewById(R.id.person_icon);
+ assertEquals(View.VISIBLE, personIcon.getVisibility());
+ // Has notification content.
+ statusContent = (TextView) largeResult.findViewById(R.id.text_content);
+ assertEquals(View.VISIBLE, statusContent.getVisibility());
+ assertEquals(statusContent.getText(), NOTIFICATION_CONTENT);
+ assertThat(statusContent.getMaxLines()).isEqualTo(3);
+ }
+
+ @Test
+ public void testGetBackgroundTextFromMessageNoPunctuation() {
+ String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage("test");
+
+ assertThat(backgroundText).isNull();
+ }
+
+ @Test
+ public void testGetBackgroundTextFromMessageSingleExclamation() {
+ String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage("test!");
+
+ assertThat(backgroundText).isNull();
+ }
+
+ @Test
+ public void testGetBackgroundTextFromMessageSingleQuestion() {
+ String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage("?test");
+
+ assertThat(backgroundText).isNull();
+ }
+
+ @Test
+ public void testGetBackgroundTextFromMessageSeparatedMarks() {
+ String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage("test! right!");
+
+ assertThat(backgroundText).isNull();
+ }
+
+ @Test
+ public void testGetBackgroundTextFromMessageDoubleExclamation() {
+ String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage("!!test");
+
+ assertThat(backgroundText).isEqualTo("!");
+ }
+
+ @Test
+ public void testGetBackgroundTextFromMessageDoubleQuestion() {
+ String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage("test??");
+
+ assertThat(backgroundText).isEqualTo("?");
+ }
+
+ @Test
+ public void testGetBackgroundTextFromMessageMixed() {
+ String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage("test?!");
+
+ assertThat(backgroundText).isEqualTo("!?");
+ }
+
+ @Test
+ public void testGetBackgroundTextFromMessageMixedInTheMiddle() {
+ String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage(
+ "test!? in the middle");
+
+ assertThat(backgroundText).isEqualTo("!?");
+ }
+
+ @Test
+ public void testGetBackgroundTextFromMessageMixedDifferentOrder() {
+ String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage(
+ "test!? in the middle");
+
+ assertThat(backgroundText).isEqualTo("!?");
+ }
+
+ @Test
+ public void testGetBackgroundTextFromMessageMultiple() {
+ String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage(
+ "test!?!!? in the middle");
+
+ assertThat(backgroundText).isEqualTo("!?");
+ }
+
+ @Test
+ public void testGetBackgroundTextFromMessageQuestionFirst() {
+ String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage(
+ "test?? in the middle!!");
+
+ assertThat(backgroundText).isEqualTo("?");
+ }
+
+ @Test
+ public void testGetBackgroundTextFromMessageExclamationFirst() {
+ String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage(
+ "test!! in the middle??");
+
+ assertThat(backgroundText).isEqualTo("!");
+ }
+
+ private int getSizeInDp(int dimenResourceId) {
+ return (int) (mContext.getResources().getDimension(dimenResourceId)
+ / mContext.getResources().getDisplayMetrics().density);
+ }
+}
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/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 999d282..8f36415 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -35,7 +35,6 @@
import static org.mockito.Mockito.when;
import android.app.Instrumentation;
-import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.LinkProperties;
import android.net.Network;
@@ -54,7 +53,6 @@
import android.telephony.SignalStrength;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyCallback;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager;
import android.testing.TestableLooper;
@@ -172,7 +170,7 @@
mMockNsm = mock(NetworkScoreManager.class);
mMockSubDefaults = mock(SubscriptionDefaults.class);
mNetCapabilities = new NetworkCapabilities();
- when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(true);
+ when(mMockTm.isDataCapable()).thenReturn(true);
when(mMockTm.createForSubscriptionId(anyInt())).thenReturn(mMockTm);
doAnswer(invocation -> {
int rssi = invocation.getArgument(0);
@@ -285,7 +283,7 @@
}
protected NetworkControllerImpl setUpNoMobileData() {
- when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(false);
+ when(mMockTm.isDataCapable()).thenReturn(false);
NetworkControllerImpl networkControllerNoMobile =
new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, mMockNsm, mMockSm,
mConfig, TestableLooper.get(this).getLooper(), mCallbackHandler,
@@ -308,27 +306,14 @@
TelephonyManager.NETWORK_TYPE_UMTS);
setConnectivityViaCallbackInNetworkController(
NetworkCapabilities.TRANSPORT_CELLULAR, true, true, null);
- setConnectivityViaBroadcast(
- NetworkCapabilities.TRANSPORT_CELLULAR, true, true);
}
- public void setConnectivityViaBroadcastForVcn(
+ public void setConnectivityViaCallbackInNetworkControllerForVcn(
int networkType, boolean validated, boolean isConnected, VcnTransportInfo info) {
mNetCapabilities.setTransportInfo(info);
setConnectivityCommon(networkType, validated, isConnected);
mDefaultCallbackInNetworkController.onCapabilitiesChanged(
mock(Network.class), new NetworkCapabilities(mNetCapabilities));
- Intent i = new Intent(ConnectivityManager.INET_CONDITION_ACTION);
- mNetworkController.onReceive(mContext, i);
- }
-
- public void setConnectivityViaBroadcast(
- int networkType, boolean validated, boolean isConnected) {
- setConnectivityCommon(networkType, validated, isConnected);
- mDefaultCallbackInNetworkController.onCapabilitiesChanged(
- mock(Network.class), new NetworkCapabilities(mNetCapabilities));
- Intent i = new Intent(ConnectivityManager.INET_CONDITION_ACTION);
- mNetworkController.onReceive(mContext, i);
}
public void setConnectivityViaCallbackInNetworkController(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index 37b6a5d..b108dd8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -126,7 +126,8 @@
when(mMockTm.isDataConnectionAllowed()).thenReturn(false);
setupDefaultSignal();
updateDataConnectionState(TelephonyManager.DATA_CONNECTED, 0);
- setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
+ setConnectivityViaCallbackInNetworkController(
+ NetworkCapabilities.TRANSPORT_CELLULAR, false, false, null);
// Verify that a SignalDrawable with a cut out is used to display data disabled.
verifyLastMobileDataIndicators(false, DEFAULT_SIGNAL_STRENGTH, 0,
@@ -140,7 +141,8 @@
when(mMockTm.isDataConnectionAllowed()).thenReturn(false);
setupDefaultSignal();
updateDataConnectionState(TelephonyManager.DATA_DISCONNECTED, 0);
- setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
+ setConnectivityViaCallbackInNetworkController(
+ NetworkCapabilities.TRANSPORT_CELLULAR, false, false, null);
// Verify that a SignalDrawable with a cut out is used to display data disabled.
verifyLastMobileDataIndicators(false, DEFAULT_SIGNAL_STRENGTH, 0,
@@ -155,7 +157,8 @@
setupDefaultSignal();
setDefaultSubId(mSubId + 1);
updateDataConnectionState(TelephonyManager.DATA_CONNECTED, 0);
- setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
+ setConnectivityViaCallbackInNetworkController(
+ NetworkCapabilities.TRANSPORT_CELLULAR, false, false, null);
// Verify that a SignalDrawable with a cut out is used to display data disabled.
verifyLastMobileDataIndicators(false, DEFAULT_SIGNAL_STRENGTH, 0,
@@ -170,7 +173,8 @@
setupDefaultSignal();
setDefaultSubId(mSubId + 1);
updateDataConnectionState(TelephonyManager.DATA_DISCONNECTED, 0);
- setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
+ setConnectivityViaCallbackInNetworkController(
+ NetworkCapabilities.TRANSPORT_CELLULAR, false, false, null);
// Verify that a SignalDrawable with a cut out is used to display data disabled.
verifyLastMobileDataIndicators(false, DEFAULT_SIGNAL_STRENGTH, 0,
@@ -184,7 +188,8 @@
when(mMockTm.isDataConnectionAllowed()).thenReturn(false);
setupDefaultSignal();
updateDataConnectionState(TelephonyManager.DATA_DISCONNECTED, 0);
- setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
+ setConnectivityViaCallbackInNetworkController(
+ NetworkCapabilities.TRANSPORT_CELLULAR, false, false, null);
when(mMockProvisionController.isUserSetup(anyInt())).thenReturn(false);
mUserCallback.onUserSetupChanged();
TestableLooper.get(this).processAllMessages();
@@ -206,7 +211,8 @@
mConfig.alwaysShowDataRatIcon = true;
mNetworkController.handleConfigurationChanged();
- setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
+ setConnectivityViaCallbackInNetworkController(
+ NetworkCapabilities.TRANSPORT_CELLULAR, false, false, null);
verifyLastMobileDataIndicators(false, DEFAULT_SIGNAL_STRENGTH, TelephonyIcons.ICON_G,
true, DEFAULT_QS_SIGNAL_STRENGTH, 0, false, false);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerEthernetTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerEthernetTest.java
index 93cf3e8..6aab9c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerEthernetTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerEthernetTest.java
@@ -37,7 +37,8 @@
}
protected void setEthernetState(boolean connected, boolean validated) {
- setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_ETHERNET, validated, connected);
+ setConnectivityViaCallbackInNetworkController(
+ NetworkCapabilities.TRANSPORT_ETHERNET, validated, connected, null);
}
protected void verifyLastEthernetIcon(boolean visible, int icon) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
index c0d9c3d..91e9f06 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
@@ -23,8 +23,8 @@
import static org.mockito.Mockito.when;
import android.content.Intent;
-import android.net.ConnectivityManager;
import android.net.NetworkCapabilities;
+import android.net.wifi.WifiInfo;
import android.os.Handler;
import android.os.Looper;
import android.telephony.CellSignalStrength;
@@ -59,7 +59,7 @@
@Test
public void testNoIconWithoutMobile() {
// Turn off mobile network support.
- when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(false);
+ when(mMockTm.isDataCapable()).thenReturn(false);
// Create a new NetworkController as this is currently handled in constructor.
mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm,
mMockNsm, mMockSm, mConfig, Looper.getMainLooper(), mCallbackHandler,
@@ -145,7 +145,7 @@
@Test
public void testNoSimlessIconWithoutMobile() {
// Turn off mobile network support.
- when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(false);
+ when(mMockTm.isDataCapable()).thenReturn(false);
// Create a new NetworkController as this is currently handled in constructor.
mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm,
mMockNsm, mMockSm, mConfig, Looper.getMainLooper(), mCallbackHandler,
@@ -172,7 +172,8 @@
testStrength, DEFAULT_ICON);
// Verify low inet number indexing.
- setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, true);
+ setConnectivityViaCallbackInNetworkController(
+ NetworkCapabilities.TRANSPORT_CELLULAR, false, true, null);
verifyLastMobileDataIndicators(true,
testStrength, DEFAULT_ICON, false, false);
}
@@ -259,8 +260,10 @@
@Test
public void testNoBangWithWifi() {
setupDefaultSignal();
- setConnectivityViaBroadcast(mMobileSignalController.mTransportType, false, false);
- setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
+ setConnectivityViaCallbackInNetworkController(
+ mMobileSignalController.mTransportType, false, false, null);
+ setConnectivityViaCallbackInNetworkController(
+ NetworkCapabilities.TRANSPORT_WIFI, true, true, mock(WifiInfo.class));
verifyLastMobileDataIndicators(false, DEFAULT_LEVEL, 0);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index 847030e..ab7cbf7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -3,7 +3,7 @@
import static junit.framework.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -41,7 +41,7 @@
@Before
public void setUp() throws Exception {
super.setUp();
- when(mWifiInfo.makeCopy(anyBoolean())).thenReturn(mWifiInfo);
+ when(mWifiInfo.makeCopy(anyLong())).thenReturn(mWifiInfo);
}
@Test
@@ -59,9 +59,11 @@
for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) {
setWifiLevel(testLevel);
- setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
+ setConnectivityViaCallbackInNetworkController(
+ NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]);
- setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, false, true);
+ setConnectivityViaCallbackInNetworkController(
+ NetworkCapabilities.TRANSPORT_WIFI, false, true, mWifiInfo);
// Icon does not show if not validated
verifyLastWifiIcon(false, WifiIcons.WIFI_SIGNAL_STRENGTH[0][testLevel]);
}
@@ -80,12 +82,14 @@
setWifiState(true, testSsid);
for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) {
setWifiLevel(testLevel);
- setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
+ setConnectivityViaCallbackInNetworkController(
+ NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
setConnectivityViaDefaultCallbackInWifiTracker(
NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
verifyLastQsWifiIcon(true, true, WifiIcons.QS_WIFI_SIGNAL_STRENGTH[1][testLevel],
testSsid);
- setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, false, true);
+ setConnectivityViaCallbackInNetworkController(
+ NetworkCapabilities.TRANSPORT_WIFI, false, true, mWifiInfo);
verifyLastQsWifiIcon(true, true, WifiIcons.QS_WIFI_SIGNAL_STRENGTH[0][testLevel],
testSsid);
}
@@ -99,7 +103,8 @@
setWifiEnabled(true);
setWifiState(true, testSsid);
setWifiLevel(testLevel);
- setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
+ setConnectivityViaCallbackInNetworkController(
+ NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
setConnectivityViaDefaultCallbackInWifiTracker(
NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
verifyLastQsWifiIcon(true, true,
@@ -126,14 +131,17 @@
setWifiEnabled(true);
setWifiState(true, testSsid);
setWifiLevel(testLevel);
- setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
+ setConnectivityViaCallbackInNetworkController(
+ NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]);
setupDefaultSignal();
setGsmRoaming(true);
// Still be on wifi though.
- setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
- setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
+ setConnectivityViaCallbackInNetworkController(
+ NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
+ setConnectivityViaCallbackInNetworkController(
+ NetworkCapabilities.TRANSPORT_CELLULAR, false, false, null);
verifyLastMobileDataIndicators(true, DEFAULT_LEVEL, 0, true);
}
@@ -145,7 +153,8 @@
setWifiEnabled(true);
setWifiState(true, testSsid);
setWifiLevel(testLevel);
- setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
+ setConnectivityViaCallbackInNetworkController(
+ NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]);
setConnectivityViaCallbackInNetworkController(
@@ -161,7 +170,8 @@
setWifiEnabled(true);
setWifiState(true, testSsid);
setWifiLevel(testLevel);
- setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
+ setConnectivityViaCallbackInNetworkController(
+ NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]);
setWifiState(false, testSsid);
@@ -234,11 +244,11 @@
for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) {
setWifiLevelForVcn(testLevel);
- setConnectivityViaBroadcastForVcn(
+ setConnectivityViaCallbackInNetworkControllerForVcn(
NetworkCapabilities.TRANSPORT_CELLULAR, true, true, mVcnTransportInfo);
verifyLastMobileDataIndicatorsForVcn(true, testLevel, TelephonyIcons.ICON_CWF, true);
- setConnectivityViaBroadcastForVcn(
+ setConnectivityViaCallbackInNetworkControllerForVcn(
NetworkCapabilities.TRANSPORT_CELLULAR, false, true, mVcnTransportInfo);
verifyLastMobileDataIndicatorsForVcn(true, testLevel, TelephonyIcons.ICON_CWF, false);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
index 6067b42..eb6fc2e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
@@ -23,7 +23,6 @@
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ICON_SETTINGS;
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ICON_SYSUI;
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ICON_THEME_PICKER;
-import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_NEUTRAL_PALETTE;
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_SHAPE;
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_SYSTEM_PALETTE;
import static com.android.systemui.theme.ThemeOverlayApplier.SETTINGS_PACKAGE;
@@ -116,8 +115,6 @@
ANDROID_PACKAGE, OVERLAY_CATEGORY_ACCENT_COLOR, false),
createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_SYSTEM_PALETTE,
ANDROID_PACKAGE, OVERLAY_CATEGORY_SYSTEM_PALETTE, false),
- createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_NEUTRAL_PALETTE,
- ANDROID_PACKAGE, OVERLAY_CATEGORY_NEUTRAL_PALETTE, false),
createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_FONT,
ANDROID_PACKAGE, OVERLAY_CATEGORY_FONT, false),
createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_SHAPE,
@@ -128,8 +125,6 @@
ANDROID_PACKAGE, OVERLAY_CATEGORY_ACCENT_COLOR, true),
createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_SYSTEM_PALETTE,
ANDROID_PACKAGE, OVERLAY_CATEGORY_SYSTEM_PALETTE, true),
- createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_NEUTRAL_PALETTE,
- ANDROID_PACKAGE, OVERLAY_CATEGORY_NEUTRAL_PALETTE, true),
createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_FONT,
ANDROID_PACKAGE, OVERLAY_CATEGORY_FONT, true),
createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_SHAPE,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index 8a0ac11..4e7e0a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -17,7 +17,6 @@
package com.android.systemui.theme;
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ACCENT_COLOR;
-import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_NEUTRAL_PALETTE;
import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_SYSTEM_PALETTE;
import static com.google.common.truth.Truth.assertThat;
@@ -147,8 +146,6 @@
// Assert that we received the colors that we were expecting
assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
.isEqualTo(new OverlayIdentifier("ffff0000"));
- assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_NEUTRAL_PALETTE))
- .isEqualTo(new OverlayIdentifier("ffff0000"));
assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_ACCENT_COLOR))
.isEqualTo(new OverlayIdentifier("ff0000ff"));
diff --git a/proto/src/camera.proto b/proto/src/camera.proto
new file mode 100644
index 0000000..d07a525
--- /dev/null
+++ b/proto/src/camera.proto
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package android.stats.camera;
+
+option java_package = "android.stats.camera";
+option java_outer_classname = "CameraProtos";
+
+/**
+ * CameraStreamProto from atoms.proto, duplicated here so that it's accessible from the
+ * logging code. Must be kept in sync with the definition in atoms.proto.
+ */
+message CameraStreamProto {
+ // The stream width (in pixels)
+ optional int32 width = 1;
+ // The stream height (in pixels)
+ optional int32 height = 2;
+ // The format of the stream
+ optional int32 format = 3;
+ // The dataspace of the stream
+ optional int32 data_space = 4;
+ // The usage flag of the stream
+ optional int64 usage = 5;
+
+ // The number of requests for this stream
+ optional int64 request_count = 6;
+ // The number of buffer error for this stream
+ optional int64 error_count = 7;
+ // The capture latency of first request for this stream
+ optional int32 first_capture_latency_millis = 8;
+
+ // The maximum number of hal buffers
+ optional int32 max_hal_buffers = 9;
+ // The maximum number of app buffers
+ optional int32 max_app_buffers = 10;
+
+ // Type of stream histogram
+ // 1: Capture latency: bin size in milliseconds
+ enum HistogramType {
+ UNKNOWN = 0;
+ CAPTURE_LATENCY = 1;
+ }
+ optional HistogramType histogram_type = 11;
+ // The boundary values between histogram bins
+ // Expected number of fields: 9
+ repeated float histogram_bins = 12;
+ // The frame counts for each histogram bins
+ // Expected number of fields: 10
+ repeated int64 histogram_counts = 13;
+}
diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java
index e9a099a..2c50389 100644
--- a/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java
+++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java
@@ -35,6 +35,7 @@
import android.content.pm.ParceledListSlice;
import android.os.Binder;
import android.os.IBinder;
+import android.os.Process;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.util.Slog;
@@ -179,7 +180,8 @@
Context ctx = getContext();
if (!(ctx.checkCallingPermission(PACKAGE_USAGE_STATS) == PERMISSION_GRANTED
|| mServiceNameResolver.isTemporary(userId)
- || mActivityTaskManagerInternal.isCallerRecents(Binder.getCallingUid()))) {
+ || mActivityTaskManagerInternal.isCallerRecents(Binder.getCallingUid())
+ || Binder.getCallingUid() == Process.SYSTEM_UID)) {
String msg = "Permission Denial: " + func + " from pid="
+ Binder.getCallingPid()
diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
index 735f420..cd332a6 100644
--- a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
+++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
@@ -40,6 +40,7 @@
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.infra.AbstractRemoteService;
import com.android.server.LocalServices;
import com.android.server.infra.AbstractPerUserSystemService;
@@ -55,6 +56,8 @@
private static final String TAG = AppPredictionPerUserService.class.getSimpleName();
private static final String PREDICT_USING_PEOPLE_SERVICE_PREFIX =
"predict_using_people_service_";
+ private static final String REMOTE_APP_PREDICTOR_KEY = "remote_app_predictor";
+
@Nullable
@GuardedBy("mLock")
@@ -112,8 +115,16 @@
@GuardedBy("mLock")
public void onCreatePredictionSessionLocked(@NonNull AppPredictionContext context,
@NonNull AppPredictionSessionId sessionId, @NonNull IBinder token) {
- final boolean usesPeopleService = DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI,
+ boolean usesPeopleService = DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI,
PREDICT_USING_PEOPLE_SERVICE_PREFIX + context.getUiSurface(), false);
+ if (context.getExtras() != null
+ && context.getExtras().getBoolean(REMOTE_APP_PREDICTOR_KEY, false)
+ && DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.DARK_LAUNCH_REMOTE_PREDICTION_SERVICE_ENABLED, false)
+ ) {
+ // connect with remote AppPredictionService instead for dark launch
+ usesPeopleService = false;
+ }
final boolean serviceExists = resolveService(sessionId, false,
usesPeopleService, s -> s.onCreatePredictionSession(context, sessionId));
if (serviceExists && !mSessionInfos.containsKey(sessionId)) {
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/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 0515eca..1985848 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -69,6 +69,9 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE;
import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.REDACT_FOR_ACCESS_FINE_LOCATION;
+import static android.net.NetworkCapabilities.REDACT_FOR_LOCAL_MAC_ADDRESS;
+import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_TEST;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
@@ -84,7 +87,6 @@
import static java.util.Map.Entry;
import android.Manifest;
-import android.annotation.BoolRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
@@ -211,6 +213,7 @@
import android.util.SparseArray;
import android.util.SparseIntArray;
+import com.android.connectivity.resources.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
@@ -817,8 +820,7 @@
private ArrayMap<Integer, Integer> loadRestoreTimers() {
final String[] configs = mService.mResources.get().getStringArray(
- com.android.connectivity.resources.R.array
- .config_legacy_networktype_restore_timers);
+ R.array.config_legacy_networktype_restore_timers);
final ArrayMap<Integer, Integer> ret = new ArrayMap<>(configs.length);
for (final String config : configs) {
final String[] splits = TextUtils.split(config, ",");
@@ -1256,8 +1258,7 @@
mLegacyTypeTracker.loadSupportedTypes(mContext, mTelephonyManager);
mProtectedNetworks = new ArrayList<>();
- int[] protectedNetworks = context.getResources().getIntArray(
- com.android.internal.R.array.config_protectedNetworks);
+ int[] protectedNetworks = mResources.get().getIntArray(R.array.config_protectedNetworks);
for (int p : protectedNetworks) {
if (mLegacyTypeTracker.isTypeSupported(p) && !mProtectedNetworks.contains(p)) {
mProtectedNetworks.add(p);
@@ -1387,7 +1388,7 @@
mHandler.sendEmptyMessage(EVENT_PRIVATE_DNS_SETTINGS_CHANGED);
}
- private void handleAlwaysOnNetworkRequest(NetworkRequest networkRequest, @BoolRes int id) {
+ private void handleAlwaysOnNetworkRequest(NetworkRequest networkRequest, int id) {
final boolean enable = mContext.getResources().getBoolean(id);
handleAlwaysOnNetworkRequest(networkRequest, enable);
}
@@ -1422,8 +1423,14 @@
ConnectivitySettingsManager.MOBILE_DATA_ALWAYS_ON, true /* defaultValue */);
handleAlwaysOnNetworkRequest(mDefaultWifiRequest,
ConnectivitySettingsManager.WIFI_ALWAYS_REQUESTED, false /* defaultValue */);
+ final boolean vehicleAlwaysRequested = mResources.get().getBoolean(
+ R.bool.config_vehicleInternalNetworkAlwaysRequested);
+ // TODO (b/183076074): remove legacy fallback after migrating overlays
+ final boolean legacyAlwaysRequested = mContext.getResources().getBoolean(
+ mContext.getResources().getIdentifier(
+ "config_vehicleInternalNetworkAlwaysRequested", "bool", "android"));
handleAlwaysOnNetworkRequest(mDefaultVehicleRequest,
- com.android.internal.R.bool.config_vehicleInternalNetworkAlwaysRequested);
+ vehicleAlwaysRequested || legacyAlwaysRequested);
}
private void registerSettingsCallbacks() {
@@ -1771,7 +1778,8 @@
nai.network,
createWithLocationInfoSanitizedIfNecessaryWhenParceled(
nc, false /* includeLocationSensitiveInfo */,
- mDeps.getCallingUid(), callingPackageName, callingAttributionTag));
+ getCallingPid(), mDeps.getCallingUid(), callingPackageName,
+ callingAttributionTag));
}
}
@@ -1786,7 +1794,7 @@
createWithLocationInfoSanitizedIfNecessaryWhenParceled(
nc,
false /* includeLocationSensitiveInfo */,
- mDeps.getCallingUid(), callingPackageName,
+ getCallingPid(), mDeps.getCallingUid(), callingPackageName,
callingAttributionTag));
}
}
@@ -1869,7 +1877,7 @@
return createWithLocationInfoSanitizedIfNecessaryWhenParceled(
getNetworkCapabilitiesInternal(network),
false /* includeLocationSensitiveInfo */,
- mDeps.getCallingUid(), callingPackageName, callingAttributionTag);
+ getCallingPid(), mDeps.getCallingUid(), callingPackageName, callingAttributionTag);
}
@VisibleForTesting
@@ -1888,40 +1896,137 @@
return newNc;
}
- private boolean hasLocationPermission(int callerUid, @NonNull String callerPkgName,
- @Nullable String callingAttributionTag) {
- final long token = Binder.clearCallingIdentity();
- try {
- return mLocationPermissionChecker.checkLocationPermission(
- callerPkgName, callingAttributionTag, callerUid, null /* message */);
- } finally {
- Binder.restoreCallingIdentity(token);
+ /**
+ * Wrapper used to cache the permission check results performed for the corresponding
+ * app. This avoid performing multiple permission checks for different fields in
+ * NetworkCapabilities.
+ * Note: This wrapper does not support any sort of invalidation and thus must not be
+ * persistent or long-lived. It may only be used for the time necessary to
+ * compute the redactions required by one particular NetworkCallback or
+ * synchronous call.
+ */
+ private class RedactionPermissionChecker {
+ private final int mCallingPid;
+ private final int mCallingUid;
+ @NonNull private final String mCallingPackageName;
+ @Nullable private final String mCallingAttributionTag;
+
+ private Boolean mHasLocationPermission = null;
+ private Boolean mHasLocalMacAddressPermission = null;
+ private Boolean mHasSettingsPermission = null;
+
+ RedactionPermissionChecker(int callingPid, int callingUid,
+ @NonNull String callingPackageName, @Nullable String callingAttributionTag) {
+ mCallingPid = callingPid;
+ mCallingUid = callingUid;
+ mCallingPackageName = callingPackageName;
+ mCallingAttributionTag = callingAttributionTag;
}
+
+ private boolean hasLocationPermissionInternal() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mLocationPermissionChecker.checkLocationPermission(
+ mCallingPackageName, mCallingAttributionTag, mCallingUid,
+ null /* message */);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * Returns whether the app holds location permission or not (might return cached result
+ * if the permission was already checked before).
+ */
+ public boolean hasLocationPermission() {
+ if (mHasLocationPermission == null) {
+ // If there is no cached result, perform the check now.
+ mHasLocationPermission = hasLocationPermissionInternal();
+ }
+ return mHasLocationPermission;
+ }
+
+ /**
+ * Returns whether the app holds local mac address permission or not (might return cached
+ * result if the permission was already checked before).
+ */
+ public boolean hasLocalMacAddressPermission() {
+ if (mHasLocalMacAddressPermission == null) {
+ // If there is no cached result, perform the check now.
+ mHasLocalMacAddressPermission =
+ checkLocalMacAddressPermission(mCallingPid, mCallingUid);
+ }
+ return mHasLocalMacAddressPermission;
+ }
+
+ /**
+ * Returns whether the app holds settings permission or not (might return cached
+ * result if the permission was already checked before).
+ */
+ public boolean hasSettingsPermission() {
+ if (mHasSettingsPermission == null) {
+ // If there is no cached result, perform the check now.
+ mHasSettingsPermission = checkSettingsPermission(mCallingPid, mCallingUid);
+ }
+ return mHasSettingsPermission;
+ }
+ }
+
+ private static boolean shouldRedact(@NetworkCapabilities.RedactionType long redactions,
+ @NetworkCapabilities.NetCapability long redaction) {
+ return (redactions & redaction) != 0;
+ }
+
+ /**
+ * Use the provided |applicableRedactions| to check the receiving app's
+ * permissions and clear/set the corresponding bit in the returned bitmask. The bitmask
+ * returned will be used to ensure the necessary redactions are performed by NetworkCapabilities
+ * before being sent to the corresponding app.
+ */
+ private @NetworkCapabilities.RedactionType long retrieveRequiredRedactions(
+ @NetworkCapabilities.RedactionType long applicableRedactions,
+ @NonNull RedactionPermissionChecker redactionPermissionChecker,
+ boolean includeLocationSensitiveInfo) {
+ long redactions = applicableRedactions;
+ if (shouldRedact(redactions, REDACT_FOR_ACCESS_FINE_LOCATION)) {
+ if (includeLocationSensitiveInfo
+ && redactionPermissionChecker.hasLocationPermission()) {
+ redactions &= ~REDACT_FOR_ACCESS_FINE_LOCATION;
+ }
+ }
+ if (shouldRedact(redactions, REDACT_FOR_LOCAL_MAC_ADDRESS)) {
+ if (redactionPermissionChecker.hasLocalMacAddressPermission()) {
+ redactions &= ~REDACT_FOR_LOCAL_MAC_ADDRESS;
+ }
+ }
+ if (shouldRedact(redactions, REDACT_FOR_NETWORK_SETTINGS)) {
+ if (redactionPermissionChecker.hasSettingsPermission()) {
+ redactions &= ~REDACT_FOR_NETWORK_SETTINGS;
+ }
+ }
+ return redactions;
}
@VisibleForTesting
@Nullable
NetworkCapabilities createWithLocationInfoSanitizedIfNecessaryWhenParceled(
@Nullable NetworkCapabilities nc, boolean includeLocationSensitiveInfo,
- int callerUid, @NonNull String callerPkgName, @Nullable String callingAttributionTag) {
+ int callingPid, int callingUid, @NonNull String callingPkgName,
+ @Nullable String callingAttributionTag) {
if (nc == null) {
return null;
}
- Boolean hasLocationPermission = null;
- final NetworkCapabilities newNc;
// Avoid doing location permission check if the transport info has no location sensitive
// data.
- if (includeLocationSensitiveInfo
- && nc.getTransportInfo() != null
- && nc.getTransportInfo().hasLocationSensitiveFields()) {
- hasLocationPermission =
- hasLocationPermission(callerUid, callerPkgName, callingAttributionTag);
- newNc = new NetworkCapabilities(nc, hasLocationPermission);
- } else {
- newNc = new NetworkCapabilities(nc, false /* parcelLocationSensitiveFields */);
- }
+ final RedactionPermissionChecker redactionPermissionChecker =
+ new RedactionPermissionChecker(callingPid, callingUid, callingPkgName,
+ callingAttributionTag);
+ final long redactions = retrieveRequiredRedactions(
+ nc.getApplicableRedactions(), redactionPermissionChecker,
+ includeLocationSensitiveInfo);
+ final NetworkCapabilities newNc = new NetworkCapabilities(nc, redactions);
// Reset owner uid if not destined for the owner app.
- if (callerUid != nc.getOwnerUid()) {
+ if (callingUid != nc.getOwnerUid()) {
newNc.setOwnerUid(INVALID_UID);
return newNc;
}
@@ -1930,23 +2035,17 @@
// Owner UIDs already checked above. No need to re-check.
return newNc;
}
- // If the caller does not want location sensitive data & target SDK >= S, then mask info.
- // Else include the owner UID iff the caller has location permission to provide backwards
+ // If the calling does not want location sensitive data & target SDK >= S, then mask info.
+ // Else include the owner UID iff the calling has location permission to provide backwards
// compatibility for older apps.
if (!includeLocationSensitiveInfo
&& isTargetSdkAtleast(
- Build.VERSION_CODES.S, callerUid, callerPkgName)) {
+ Build.VERSION_CODES.S, callingUid, callingPkgName)) {
newNc.setOwnerUid(INVALID_UID);
return newNc;
}
-
- if (hasLocationPermission == null) {
- // Location permission not checked yet, check now for masking owner UID.
- hasLocationPermission =
- hasLocationPermission(callerUid, callerPkgName, callingAttributionTag);
- }
// Reset owner uid if the app has no location permission.
- if (!hasLocationPermission) {
+ if (!redactionPermissionChecker.hasLocationPermission()) {
newNc.setOwnerUid(INVALID_UID);
}
return newNc;
@@ -2437,6 +2536,11 @@
mContext.enforceCallingOrSelfPermission(KeepaliveTracker.PERMISSION, "ConnectivityService");
}
+ private boolean checkLocalMacAddressPermission(int pid, int uid) {
+ return PERMISSION_GRANTED == mContext.checkPermission(
+ Manifest.permission.LOCAL_MAC_ADDRESS, pid, uid);
+ }
+
private void sendConnectedBroadcast(NetworkInfo info) {
sendGeneralBroadcast(info, CONNECTIVITY_ACTION);
}
@@ -4632,7 +4736,7 @@
mWakelockLogs.log("ACQUIRE for " + forWhom);
Message msg = mHandler.obtainMessage(EVENT_EXPIRE_NET_TRANSITION_WAKELOCK);
final int lockTimeout = mResources.get().getInteger(
- com.android.connectivity.resources.R.integer.config_networkTransitionTimeout);
+ R.integer.config_networkTransitionTimeout);
mHandler.sendMessageDelayed(msg, lockTimeout);
}
@@ -4985,7 +5089,7 @@
@Override
public void setRequireVpnForUids(boolean requireVpn, UidRange[] ranges) {
- PermissionUtils.enforceNetworkStackPermission(mContext);
+ enforceNetworkStackOrSettingsPermission();
mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_REQUIRE_VPN_FOR_UIDS,
encodeBool(requireVpn), 0 /* arg2 */, ranges));
}
@@ -5023,7 +5127,7 @@
@Override
public void setLegacyLockdownVpnEnabled(boolean enabled) {
- enforceSettingsPermission();
+ enforceNetworkStackOrSettingsPermission();
mHandler.post(() -> mLockdownEnabled = enabled);
}
@@ -6016,10 +6120,15 @@
private NetworkCapabilities copyDefaultNetworkCapabilitiesForUid(
@NonNull final NetworkCapabilities netCapToCopy, @NonNull final int requestorUid,
@NonNull final String requestorPackageName) {
+ // These capabilities are for a TRACK_DEFAULT callback, so:
+ // 1. Remove NET_CAPABILITY_VPN, because it's (currently!) the only difference between
+ // mDefaultRequest and a per-UID default request.
+ // TODO: stop depending on the fact that these two unrelated things happen to be the same
+ // 2. Always set the UIDs to mAsUid. restrictRequestUidsForCallerAndSetRequestorInfo will
+ // not do this in the case of a privileged application.
final NetworkCapabilities netCap = new NetworkCapabilities(netCapToCopy);
netCap.removeCapability(NET_CAPABILITY_NOT_VPN);
netCap.setSingleUid(requestorUid);
- netCap.setUids(new ArraySet<>());
restrictRequestUidsForCallerAndSetRequestorInfo(
netCap, requestorUid, requestorPackageName);
return netCap;
@@ -6362,10 +6471,16 @@
return;
}
- int mark = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_networkWakeupPacketMark);
- int mask = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_networkWakeupPacketMask);
+ int mark = mResources.get().getInteger(R.integer.config_networkWakeupPacketMark);
+ int mask = mResources.get().getInteger(R.integer.config_networkWakeupPacketMask);
+
+ // TODO (b/183076074): remove legacy fallback after migrating overlays
+ final int legacyMark = mContext.getResources().getInteger(mContext.getResources()
+ .getIdentifier("config_networkWakeupPacketMark", "integer", "android"));
+ final int legacyMask = mContext.getResources().getInteger(mContext.getResources()
+ .getIdentifier("config_networkWakeupPacketMask", "integer", "android"));
+ mark = mark == 0 ? legacyMark : mark;
+ mask = mask == 0 ? legacyMask : mask;
// Mask/mark of zero will not detect anything interesting.
// Don't install rules unless both values are nonzero.
@@ -6558,8 +6673,7 @@
private void updateWakeOnLan(@NonNull LinkProperties lp) {
if (mWolSupportedInterfaces == null) {
mWolSupportedInterfaces = new ArraySet<>(mResources.get().getStringArray(
- com.android.connectivity.resources.R.array
- .config_wakeonlan_supported_interfaces));
+ R.array.config_wakeonlan_supported_interfaces));
}
lp.setWakeOnLanSupported(mWolSupportedInterfaces.contains(lp.getInterfaceName()));
}
@@ -7143,7 +7257,7 @@
putParcelable(
bundle,
createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- nc, includeLocationSensitiveInfo, nri.mUid,
+ nc, includeLocationSensitiveInfo, nri.mPid, nri.mUid,
nrForCallback.getRequestorPackageName(),
nri.mCallingAttributionTag));
putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions(
@@ -7164,7 +7278,7 @@
putParcelable(
bundle,
createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- netCap, includeLocationSensitiveInfo, nri.mUid,
+ netCap, includeLocationSensitiveInfo, nri.mPid, nri.mUid,
nrForCallback.getRequestorPackageName(),
nri.mCallingAttributionTag));
break;
@@ -8123,7 +8237,7 @@
public String getCaptivePortalServerUrl() {
enforceNetworkStackOrSettingsPermission();
String settingUrl = mResources.get().getString(
- com.android.connectivity.resources.R.string.config_networkCaptivePortalServerUrl);
+ R.string.config_networkCaptivePortalServerUrl);
if (!TextUtils.isEmpty(settingUrl)) {
return settingUrl;
@@ -8300,7 +8414,7 @@
}
}
- private @VpnManager.VpnType int getVpnType(@Nullable NetworkAgentInfo vpn) {
+ private int getVpnType(@Nullable NetworkAgentInfo vpn) {
if (vpn == null) return VpnManager.TYPE_VPN_NONE;
final TransportInfo ti = vpn.networkCapabilities.getTransportInfo();
if (!(ti instanceof VpnTransportInfo)) return VpnManager.TYPE_VPN_NONE;
diff --git a/services/core/java/com/android/server/DropBoxManagerService.java b/services/core/java/com/android/server/DropBoxManagerService.java
index f04af8b..0c3d884 100644
--- a/services/core/java/com/android/server/DropBoxManagerService.java
+++ b/services/core/java/com/android/server/DropBoxManagerService.java
@@ -448,7 +448,7 @@
final GZIPOutputStream gzipOutputStream =
new GZIPOutputStream(new FileOutputStream(fd));
FileUtils.copy(in, gzipOutputStream);
- gzipOutputStream.finish();
+ gzipOutputStream.close();
} else {
FileUtils.copy(in, new FileOutputStream(fd));
}
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index a09dbc7..ceb12c8 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -21,6 +21,21 @@
}
],
"file_patterns": ["NotificationManagerService\\.java"]
+ },
+ {
+ "name": "CtsContentTestCases",
+ "options": [
+ {
+ "include-filter": "android.content.cts.ClipboardManagerTest"
+ },
+ {
+ "include-filter": "android.content.cts.ClipDataTest"
+ },
+ {
+ "include-filter": "android.content.cts.ClipDescriptionTest"
+ }
+ ],
+ "file_patterns": ["ClipboardService\\.java"]
}
],
"presubmit-large": [
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 1b352c7..d2fd8ff 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -5663,8 +5663,8 @@
}
if (ret == REASON_DENIED) {
- if (mAm.checkPermission(SYSTEM_ALERT_WINDOW, callingPid,
- callingUid) == PERMISSION_GRANTED) {
+ if (mAm.mAtmInternal.hasSystemAlertWindowPermission(callingUid, callingPid,
+ callingPackage)) {
ret = REASON_SYSTEM_ALERT_WINDOW_PERMISSION;
}
}
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/am/MeasuredEnergySnapshot.java b/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java
index 4c9ab63..d789bbb 100644
--- a/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java
+++ b/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java
@@ -241,7 +241,7 @@
/**
* For a consumer of type {@link EnergyConsumerType#OTHER}, updates
* {@link #mAttributionSnapshots} with freshly measured energies (per uid) and returns the
- * charge consumed (in microcouloumbs) between the previously stored values and the passed-in
+ * charge consumed (in microcoulombs) between the previously stored values and the passed-in
* values.
*
* @param consumerInfo a consumer of type {@link EnergyConsumerType#OTHER}.
@@ -341,11 +341,11 @@
return numOrdinals;
}
- /** Calculate charge consumption (in microcouloumbs) from a given energy and voltage */
+ /** Calculate charge consumption (in microcoulombs) from a given energy and voltage */
private long calculateChargeConsumedUC(long deltaEnergyUJ, int avgVoltageMV) {
// To overflow, a 3.7V 10000mAh battery would need to completely drain 69244 times
- // since the last snapshot. Round up to the nearest whole long.
- return (deltaEnergyUJ * MILLIVOLTS_PER_VOLT + (avgVoltageMV + 1) / 2) / avgVoltageMV;
+ // since the last snapshot. Round off to the nearest whole long.
+ return (deltaEnergyUJ * MILLIVOLTS_PER_VOLT + (avgVoltageMV / 2)) / avgVoltageMV;
}
}
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index ad5a65c..c6824d1 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -293,6 +293,35 @@
}
/**
+ * Get the hibernating packages for the given user. This is equivalent to the list of
+ * packages for the user that return true for {@link #isHibernatingForUser}.
+ */
+ @NonNull List<String> getHibernatingPackagesForUser(int userId) {
+ ArrayList<String> hibernatingPackages = new ArrayList<>();
+ if (!checkHibernationEnabled("getHibernatingPackagesForUser")) {
+ return hibernatingPackages;
+ }
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_APP_HIBERNATION,
+ "Caller does not have MANAGE_APP_HIBERNATION permission.");
+ userId = handleIncomingUser(userId, "getHibernatingPackagesForUser");
+ if (!mUserManager.isUserUnlockingOrUnlocked(userId)) {
+ Slog.w(TAG, "Attempt to get hibernating packages for a stopped or nonexistent user "
+ + userId);
+ return hibernatingPackages;
+ }
+ synchronized (mLock) {
+ Map<String, UserLevelState> userStates = mUserStates.get(userId);
+ for (UserLevelState state : userStates.values()) {
+ if (state.hibernated) {
+ hibernatingPackages.add(state.packageName);
+ }
+ }
+ return hibernatingPackages;
+ }
+ }
+
+ /**
* Put an app into hibernation for a given user, allowing user-level optimizations to occur.
*
* @param pkgState package hibernation state
@@ -610,6 +639,11 @@
}
@Override
+ public List<String> getHibernatingPackagesForUser(int userId) {
+ return mService.getHibernatingPackagesForUser(userId);
+ }
+
+ @Override
public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
@Nullable FileDescriptor err, @NonNull String[] args,
@Nullable ShellCallback callback, @NonNull ResultReceiver resultReceiver) {
diff --git a/services/core/java/com/android/server/appop/DiscreteRegistry.java b/services/core/java/com/android/server/appop/DiscreteRegistry.java
index 2b0157c..e16a6cc 100644
--- a/services/core/java/com/android/server/appop/DiscreteRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteRegistry.java
@@ -31,14 +31,18 @@
import static android.app.AppOpsManager.flagsToString;
import static android.app.AppOpsManager.getUidStateName;
+import static java.lang.Long.min;
import static java.lang.Math.max;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
+import android.os.AsyncTask;
+import android.os.Build;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Process;
+import android.provider.DeviceConfig;
import android.util.ArrayMap;
import android.util.Slog;
import android.util.TypedXmlPullParser;
@@ -82,8 +86,16 @@
static final String TIMELINE_FILE_SUFFIX = "tl";
private static final String TAG = DiscreteRegistry.class.getSimpleName();
- private static final long TIMELINE_HISTORY_CUTOFF = Duration.ofHours(24).toMillis();
- private static final long TIMELINE_QUANTIZATION = Duration.ofMinutes(1).toMillis();
+ private static final String PROPERTY_DISCRETE_HISTORY_CUTOFF = "discrete_history_cutoff_millis";
+ private static final String PROPERTY_DISCRETE_HISTORY_QUANTIZATION =
+ "discrete_history_quantization_millis";
+ private static final long DEFAULT_DISCRETE_HISTORY_CUTOFF = Duration.ofHours(24).toMillis();
+ private static final long MAXIMUM_DISCRETE_HISTORY_CUTOFF = Duration.ofDays(30).toMillis();
+ private static final long DEFAULT_DISCRETE_HISTORY_QUANTIZATION =
+ Duration.ofMinutes(1).toMillis();
+
+ private static long sDiscreteHistoryCutoff;
+ private static long sDiscreteHistoryQuantization;
private static final String TAG_HISTORY = "h";
private static final String ATTR_VERSION = "v";
@@ -116,7 +128,7 @@
private final @NonNull Object mInMemoryLock;
@GuardedBy("mOnDiskLock")
- private final File mDiscreteAccessDir;
+ private File mDiscreteAccessDir;
@GuardedBy("mInMemoryLock")
private DiscreteOps mDiscreteOps;
@@ -126,10 +138,42 @@
DiscreteRegistry(Object inMemoryLock) {
mInMemoryLock = inMemoryLock;
- mDiscreteAccessDir = new File(new File(Environment.getDataSystemDirectory(), "appops"),
- "discrete");
- createDiscreteAccessDir();
- mDiscreteOps = new DiscreteOps();
+ }
+
+ void systemReady() {
+ synchronized (mOnDiskLock) {
+ mDiscreteAccessDir = new File(new File(Environment.getDataSystemDirectory(), "appops"),
+ "discrete");
+ createDiscreteAccessDirLocked();
+ mDiscreteOps = new DiscreteOps();
+ }
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_PRIVACY,
+ AsyncTask.THREAD_POOL_EXECUTOR, (DeviceConfig.Properties p) -> {
+ setDiscreteHistoryParameters(p);
+ });
+ sDiscreteHistoryCutoff = DeviceConfig.getLong(DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_DISCRETE_HISTORY_CUTOFF, DEFAULT_DISCRETE_HISTORY_CUTOFF);
+ sDiscreteHistoryQuantization = DeviceConfig.getLong(DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_DISCRETE_HISTORY_QUANTIZATION, DEFAULT_DISCRETE_HISTORY_QUANTIZATION);
+ }
+
+ private void setDiscreteHistoryParameters(DeviceConfig.Properties p) {
+ if (p.getKeyset().contains(PROPERTY_DISCRETE_HISTORY_CUTOFF)) {
+ sDiscreteHistoryCutoff = p.getLong(PROPERTY_DISCRETE_HISTORY_CUTOFF,
+ DEFAULT_DISCRETE_HISTORY_CUTOFF);
+ if (!Build.IS_DEBUGGABLE) {
+ sDiscreteHistoryCutoff = min(MAXIMUM_DISCRETE_HISTORY_CUTOFF,
+ sDiscreteHistoryCutoff);
+ }
+ }
+ if (p.getKeyset().contains(PROPERTY_DISCRETE_HISTORY_QUANTIZATION)) {
+ sDiscreteHistoryQuantization = p.getLong(PROPERTY_DISCRETE_HISTORY_QUANTIZATION,
+ DEFAULT_DISCRETE_HISTORY_QUANTIZATION);
+ if (!Build.IS_DEBUGGABLE) {
+ sDiscreteHistoryQuantization = max(DEFAULT_DISCRETE_HISTORY_QUANTIZATION,
+ sDiscreteHistoryQuantization);
+ }
+ }
}
private void createDiscreteAccessDir() {
@@ -142,6 +186,7 @@
}
}
+ /* can be called only after HistoricalRegistry.isPersistenceInitialized() check */
void recordDiscreteAccess(int uid, String packageName, int op, @Nullable String attributionTag,
@AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState, long accessTime,
long accessDuration) {
@@ -156,6 +201,10 @@
void writeAndClearAccessHistory() {
synchronized (mOnDiskLock) {
+ if (mDiscreteAccessDir == null) {
+ Slog.e(TAG, "State not saved - persistence not initialized.");
+ return;
+ }
final File[] files = mDiscreteAccessDir.listFiles();
if (files != null && files.length > 0) {
for (File f : files) {
@@ -166,7 +215,7 @@
try {
long timestamp = Long.valueOf(fileName.substring(0,
fileName.length() - TIMELINE_FILE_SUFFIX.length()));
- if (Instant.now().minus(TIMELINE_HISTORY_CUTOFF,
+ if (Instant.now().minus(sDiscreteHistoryCutoff,
ChronoUnit.MILLIS).toEpochMilli() > timestamp) {
f.delete();
Slog.e(TAG, "Deleting file " + fileName);
@@ -229,7 +278,7 @@
private void readDiscreteOpsFromDisk(DiscreteOps discreteOps) {
synchronized (mOnDiskLock) {
- long beginTimeMillis = Instant.now().minus(TIMELINE_HISTORY_CUTOFF,
+ long beginTimeMillis = Instant.now().minus(sDiscreteHistoryCutoff,
ChronoUnit.MILLIS).toEpochMilli();
final File[] files = mDiscreteAccessDir.listFiles();
@@ -423,6 +472,16 @@
}
}
+ private void createDiscreteAccessDirLocked() {
+ if (!mDiscreteAccessDir.exists()) {
+ if (!mDiscreteAccessDir.mkdirs()) {
+ Slog.e(TAG, "Failed to create DiscreteRegistry directory");
+ }
+ FileUtils.setPermissions(mDiscreteAccessDir.getPath(),
+ FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IXOTH, -1, -1);
+ }
+ }
+
private final class DiscreteUidOps {
ArrayMap<String, DiscretePackageOps> mPackages;
@@ -663,7 +722,7 @@
long accessTime, long accessDuration) {
List<DiscreteOpEvent> attributedOps = getOrCreateDiscreteOpEventsList(
attributionTag);
- accessTime = accessTime / TIMELINE_QUANTIZATION * TIMELINE_QUANTIZATION;
+ accessTime = accessTime / sDiscreteHistoryQuantization * sDiscreteHistoryQuantization;
int nAttributedOps = attributedOps.size();
int i = nAttributedOps;
@@ -674,7 +733,7 @@
}
if (previousOp.mOpFlag == flags && previousOp.mUidState == uidState) {
if (accessDuration != previousOp.mNoteDuration
- && accessDuration > TIMELINE_QUANTIZATION) {
+ && accessDuration > sDiscreteHistoryQuantization) {
break;
} else {
return;
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index 4435c47..9ff5492 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -246,6 +246,7 @@
+ " by which to push history on next write");
}
}
+ mDiscreteRegistry.systemReady();
}
}
}
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/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 40d2073..00b39f1 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -38,7 +38,7 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserManager;
-import android.stats.camera.nano.CameraStreamProto;
+import android.stats.camera.nano.CameraProtos.CameraStreamProto;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 71565301..781bad7 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -106,16 +106,19 @@
return bits;
}
- private void openPipe() {
+ private boolean openPipe() {
try {
- mPipe = new RandomAccessFile(PIPE_DEVICE, "rw");
- mPipe.write(createOpenHandshake());
- } catch (IOException e) {
+ final RandomAccessFile pipe = new RandomAccessFile(PIPE_DEVICE, "rw");
try {
- if (mPipe != null) mPipe.close();
- } catch (IOException ee) {}
- mPipe = null;
+ pipe.write(createOpenHandshake());
+ mPipe = pipe;
+ return true;
+ } catch (IOException ignore) {
+ pipe.close();
+ }
+ } catch (IOException ignore) {
}
+ return false;
}
public HostClipboardMonitor(HostClipboardCallback cb) {
@@ -129,8 +132,7 @@
// There's no guarantee that QEMU pipes will be ready at the moment
// this method is invoked. We simply try to get the pipe open and
// retry on failure indefinitely.
- while (mPipe == null) {
- openPipe();
+ while ((mPipe == null) && !openPipe()) {
Thread.sleep(100);
}
int size = mPipe.readInt();
diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
index 7b20ded..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;
@@ -36,6 +37,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.net.ConnectivityResources;
import android.net.ISocketKeepaliveCallback;
import android.net.InetAddresses;
import android.net.InvalidPacketException;
@@ -56,7 +58,7 @@
import android.util.Log;
import android.util.Pair;
-import com.android.internal.R;
+import com.android.connectivity.resources.R;
import com.android.internal.util.IndentingPrintWriter;
import com.android.net.module.util.HexDump;
import com.android.net.module.util.IpUtils;
@@ -111,10 +113,19 @@
mTcpController = new TcpKeepaliveController(handler);
mContext = context;
mSupportedKeepalives = KeepaliveUtils.getSupportedKeepalives(mContext);
- mReservedPrivilegedSlots = mContext.getResources().getInteger(
- R.integer.config_reservedPrivilegedKeepaliveSlots);
- mAllowedUnprivilegedSlotsForUid = mContext.getResources().getInteger(
- R.integer.config_allowedUnprivilegedKeepalivePerUid);
+
+ // TODO (b/183076074): stop reading legacy resources after migrating overlays
+ final int legacyReservedSlots = mContext.getResources().getInteger(
+ mContext.getResources().getIdentifier(
+ "config_reservedPrivilegedKeepaliveSlots", "integer", "android"));
+ final int legacyAllowedSlots = mContext.getResources().getInteger(
+ mContext.getResources().getIdentifier(
+ "config_allowedUnprivilegedKeepalivePerUid", "integer", "android"));
+ final ConnectivityResources res = new ConnectivityResources(mContext);
+ mReservedPrivilegedSlots = Math.min(legacyReservedSlots, res.get().getInteger(
+ R.integer.config_reservedPrivilegedKeepaliveSlots));
+ mAllowedUnprivilegedSlotsForUid = Math.min(legacyAllowedSlots, res.get().getInteger(
+ R.integer.config_allowedUnprivilegedKeepalivePerUid));
}
/**
@@ -518,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/connectivity/LingerMonitor.java b/services/core/java/com/android/server/connectivity/LingerMonitor.java
index adec7ad..032612c 100644
--- a/services/core/java/com/android/server/connectivity/LingerMonitor.java
+++ b/services/core/java/com/android/server/connectivity/LingerMonitor.java
@@ -24,6 +24,8 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Resources;
+import android.net.ConnectivityResources;
import android.net.NetworkCapabilities;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -34,7 +36,7 @@
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
-import com.android.internal.R;
+import com.android.connectivity.resources.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.MessageUtils;
import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
@@ -72,6 +74,7 @@
new Class[] { LingerMonitor.class }, new String[]{ "NOTIFY_TYPE_" });
private final Context mContext;
+ final Resources mResources;
private final NetworkNotificationManager mNotifier;
private final int mDailyLimit;
private final long mRateLimitMillis;
@@ -89,6 +92,7 @@
public LingerMonitor(Context context, NetworkNotificationManager notifier,
int dailyLimit, long rateLimitMillis) {
mContext = context;
+ mResources = new ConnectivityResources(mContext).get();
mNotifier = notifier;
mDailyLimit = dailyLimit;
mRateLimitMillis = rateLimitMillis;
@@ -128,8 +132,7 @@
@VisibleForTesting
public boolean isNotificationEnabled(NetworkAgentInfo fromNai, NetworkAgentInfo toNai) {
// TODO: Evaluate moving to CarrierConfigManager.
- String[] notifySwitches =
- mContext.getResources().getStringArray(R.array.config_networkNotifySwitches);
+ String[] notifySwitches = mResources.getStringArray(R.array.config_networkNotifySwitches);
if (VDBG) {
Log.d(TAG, "Notify on network switches: " + Arrays.toString(notifySwitches));
@@ -178,8 +181,7 @@
// Notify the user of a network switch using a notification or a toast.
private void notify(NetworkAgentInfo fromNai, NetworkAgentInfo toNai, boolean forceToast) {
- int notifyType =
- mContext.getResources().getInteger(R.integer.config_networkNotifySwitchType);
+ int notifyType = mResources.getInteger(R.integer.config_networkNotifySwitchType);
if (notifyType == NOTIFY_TYPE_NOTIFICATION && forceToast) {
notifyType = NOTIFY_TYPE_TOAST;
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
index 0c0d459..b57ad5d8 100644
--- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -84,7 +84,7 @@
// The context is for the current user (system server)
private final Context mContext;
- private final Resources mResources;
+ private final ConnectivityResources mResources;
private final TelephonyManager mTelephonyManager;
// The notification manager is created from a context for User.ALL, so notifications
// will be sent to all users.
@@ -99,7 +99,7 @@
(NotificationManager) c.createContextAsUser(UserHandle.ALL, 0 /* flags */)
.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationTypeMap = new SparseIntArray();
- mResources = new ConnectivityResources(mContext).get();
+ mResources = new ConnectivityResources(mContext);
}
@VisibleForTesting
@@ -118,11 +118,11 @@
}
private String getTransportName(final int transportType) {
- String[] networkTypes = mResources.getStringArray(R.array.network_switch_type_name);
+ String[] networkTypes = mResources.get().getStringArray(R.array.network_switch_type_name);
try {
return networkTypes[transportType];
} catch (IndexOutOfBoundsException e) {
- return mResources.getString(R.string.network_switch_type_name_unknown);
+ return mResources.get().getString(R.string.network_switch_type_name_unknown);
}
}
@@ -197,10 +197,11 @@
tag, nameOf(eventId), getTransportName(transportType), name, highPriority));
}
- final Resources r = mResources;
+ final Resources r = mResources.get();
final CharSequence title;
final CharSequence details;
- Icon icon = Icon.createWithResource(r, getIcon(transportType));
+ Icon icon = Icon.createWithResource(
+ mResources.getResourcesContext(), getIcon(transportType));
if (notifyType == NotificationType.NO_INTERNET && transportType == TRANSPORT_WIFI) {
title = r.getString(R.string.wifi_no_internet, name);
details = r.getString(R.string.wifi_no_internet_detailed);
@@ -355,7 +356,7 @@
public void showToast(NetworkAgentInfo fromNai, NetworkAgentInfo toNai) {
String fromTransport = getTransportName(approximateTransportType(fromNai));
String toTransport = getTransportName(approximateTransportType(toNai));
- String text = mResources.getString(
+ String text = mResources.get().getString(
R.string.network_switch_metered_toast, fromTransport, toTransport);
Toast.makeText(mContext, text, Toast.LENGTH_LONG).show();
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index a4df43c..64173bb 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -17,7 +17,6 @@
package com.android.server.connectivity;
import static android.Manifest.permission.BIND_VPN_SERVICE;
-import static android.net.ConnectivityManager.NETID_UNSET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.RouteInfo.RTN_THROW;
import static android.net.RouteInfo.RTN_UNREACHABLE;
@@ -1129,17 +1128,17 @@
}
/**
- * Return netId of current running VPN network.
+ * Return Network of current running VPN network.
*
- * @return a netId if there is a running VPN network or NETID_UNSET if there is no running VPN
+ * @return a Network if there is a running VPN network or null if there is no running VPN
* network or network is null.
*/
- public synchronized int getNetId() {
+ public synchronized Network getNetwork() {
final NetworkAgent agent = mNetworkAgent;
- if (null == agent) return NETID_UNSET;
+ if (null == agent) return null;
final Network network = agent.getNetwork();
- if (null == network) return NETID_UNSET;
- return network.getNetId();
+ if (null == network) return null;
+ return network;
}
private LinkProperties makeLinkProperties() {
@@ -1288,7 +1287,6 @@
});
mNetworkAgent.setUnderlyingNetworks((mConfig.underlyingNetworks != null)
? Arrays.asList(mConfig.underlyingNetworks) : null);
- mNetworkInfo.setIsAvailable(true);
updateState(DetailedState.CONNECTED, "agentConnect");
}
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/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index ffb532e..d17c24c 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -256,7 +256,6 @@
static final int MSG_SET_ACTIVE = 3020;
static final int MSG_SET_INTERACTIVE = 3030;
static final int MSG_REPORT_FULLSCREEN_MODE = 3045;
- static final int MSG_APPLY_IME_VISIBILITY = 3070;
static final int MSG_HARD_KEYBOARD_SWITCH_CHANGED = 4000;
@@ -4603,18 +4602,6 @@
}
return true;
}
- case MSG_APPLY_IME_VISIBILITY: {
- final boolean setVisible = msg.arg1 != 0;
- final ClientState clientState = (ClientState) msg.obj;
- try {
- clientState.client.applyImeVisibility(setVisible);
- } catch (RemoteException e) {
- Slog.w(TAG, "Got RemoteException sending "
- + "applyImeVisibility(" + setVisible + ") notification to pid="
- + clientState.pid + " uid=" + clientState.uid);
- }
- return true;
- }
// --------------------------------------------------------------
case MSG_HARD_KEYBOARD_SWITCH_CHANGED:
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/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index bcdfed4..21946ca 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -81,7 +81,7 @@
mGnssNative = gnssNative;
mGnssMetrics = new GnssMetrics(mContext, IBatteryStats.Stub.asInterface(
- ServiceManager.getService(BatteryStats.SERVICE_NAME)));
+ ServiceManager.getService(BatteryStats.SERVICE_NAME)), mGnssNative);
mGnssLocationProvider = new GnssLocationProvider(mContext, injector, mGnssNative,
mGnssMetrics);
diff --git a/services/core/java/com/android/server/location/gnss/GnssMetrics.java b/services/core/java/com/android/server/location/gnss/GnssMetrics.java
index c7d8144..dbc903d 100644
--- a/services/core/java/com/android/server/location/gnss/GnssMetrics.java
+++ b/services/core/java/com/android/server/location/gnss/GnssMetrics.java
@@ -35,6 +35,7 @@
import com.android.internal.location.nano.GnssLogsProto.PowerMetrics;
import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.location.gnss.hal.GnssNative;
import java.util.ArrayList;
import java.util.Arrays;
@@ -52,11 +53,14 @@
/** Default time between location fixes (in millisecs) */
private static final int DEFAULT_TIME_BETWEEN_FIXES_MILLISECS = 1000;
+ private static final int CONVERT_MILLI_TO_MICRO = 1000;
+ private static final int VENDOR_SPECIFIC_POWER_MODES_SIZE = 10;
/** Frequency range of GPS L5, Galileo E5a, QZSS J5 frequency band */
private static final double L5_CARRIER_FREQ_RANGE_LOW_HZ = 1164 * 1e6;
private static final double L5_CARRIER_FREQ_RANGE_HIGH_HZ = 1189 * 1e6;
+
private long mLogStartInElapsedRealtimeMs;
GnssPowerMetrics mGnssPowerMetrics;
@@ -88,8 +92,10 @@
long mL5SvStatusReportsUsedInFix;
private final StatsManager mStatsManager;
+ private final GnssNative mGnssNative;
- public GnssMetrics(Context context, IBatteryStats stats) {
+ public GnssMetrics(Context context, IBatteryStats stats, GnssNative gnssNative) {
+ mGnssNative = gnssNative;
mGnssPowerMetrics = new GnssPowerMetrics(stats);
mLocationFailureStatistics = new Statistics();
mTimeToFirstFixSecStatistics = new Statistics();
@@ -189,8 +195,8 @@
}
/**
- * Logs sv status data
- */
+ * Logs sv status data
+ */
public void logSvStatus(GnssStatus status) {
boolean isL5;
// Calculate SvStatus Information
@@ -216,8 +222,8 @@
}
/**
- * Logs CN0 when at least 4 SVs are available L5 Only
- */
+ * Logs CN0 when at least 4 SVs are available L5 Only
+ */
private void logCn0L5(GnssStatus gnssStatus) {
if (gnssStatus.getSatelliteCount() == 0) {
return;
@@ -432,7 +438,8 @@
private double mSumSquare;
private long mLongSum;
- Statistics() {}
+ Statistics() {
+ }
/** Resets statistics */
public synchronized void reset() {
@@ -498,7 +505,7 @@
GnssPowerMetrics(IBatteryStats stats) {
mBatteryStats = stats;
// Used to initialize the variable to a very small value (unachievable in practice)
- // so that
+ // so that
// the first CNO report will trigger an update to BatteryStats
mLastAverageCn0 = -100.0;
mLastSignalLevel = GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN;
@@ -585,6 +592,10 @@
FrameworkStatsLog.GNSS_STATS,
null, // use default PullAtomMetadata values
ConcurrentUtils.DIRECT_EXECUTOR, pullAtomCallback);
+ mStatsManager.setPullAtomCallback(
+ FrameworkStatsLog.GNSS_POWER_STATS,
+ null, // use default PullAtomMetadata values
+ ConcurrentUtils.DIRECT_EXECUTOR, pullAtomCallback);
}
/**
@@ -593,26 +604,68 @@
*/
private class StatsPullAtomCallbackImpl implements StatsManager.StatsPullAtomCallback {
- StatsPullAtomCallbackImpl() {}
+ StatsPullAtomCallbackImpl() {
+ }
@Override
public int onPullAtom(int atomTag, List<StatsEvent> data) {
- if (atomTag != FrameworkStatsLog.GNSS_STATS) {
+ if (atomTag == FrameworkStatsLog.GNSS_STATS) {
+ data.add(FrameworkStatsLog.buildStatsEvent(atomTag,
+ mLocationFailureReportsStatistics.getCount(),
+ mLocationFailureReportsStatistics.getLongSum(),
+ mTimeToFirstFixMilliSReportsStatistics.getCount(),
+ mTimeToFirstFixMilliSReportsStatistics.getLongSum(),
+ mPositionAccuracyMetersReportsStatistics.getCount(),
+ mPositionAccuracyMetersReportsStatistics.getLongSum(),
+ mTopFourAverageCn0DbmHzReportsStatistics.getCount(),
+ mTopFourAverageCn0DbmHzReportsStatistics.getLongSum(),
+ mL5TopFourAverageCn0DbmHzReportsStatistics.getCount(),
+ mL5TopFourAverageCn0DbmHzReportsStatistics.getLongSum(), mSvStatusReports,
+ mSvStatusReportsUsedInFix, mL5SvStatusReports,
+ mL5SvStatusReportsUsedInFix));
+ } else if (atomTag == FrameworkStatsLog.GNSS_POWER_STATS) {
+ mGnssNative.requestPowerStats();
+ GnssPowerStats gnssPowerStats = mGnssNative.getPowerStats();
+ if (gnssPowerStats == null) {
+ return StatsManager.PULL_SKIP;
+ }
+ double[] otherModesEnergyMilliJoule = new double[VENDOR_SPECIFIC_POWER_MODES_SIZE];
+ double[] tempGnssPowerStatsOtherModes =
+ gnssPowerStats.getOtherModesEnergyMilliJoule();
+ if (tempGnssPowerStatsOtherModes.length < VENDOR_SPECIFIC_POWER_MODES_SIZE) {
+ System.arraycopy(tempGnssPowerStatsOtherModes, 0,
+ otherModesEnergyMilliJoule, 0,
+ tempGnssPowerStatsOtherModes.length);
+ } else {
+ System.arraycopy(tempGnssPowerStatsOtherModes, 0,
+ otherModesEnergyMilliJoule, 0,
+ VENDOR_SPECIFIC_POWER_MODES_SIZE);
+ }
+ data.add(FrameworkStatsLog.buildStatsEvent(atomTag,
+ (long) (gnssPowerStats.getElapsedRealtimeUncertaintyNanos()),
+ (long) (gnssPowerStats.getTotalEnergyMilliJoule() * CONVERT_MILLI_TO_MICRO),
+ (long) (gnssPowerStats.getSinglebandTrackingModeEnergyMilliJoule()
+ * CONVERT_MILLI_TO_MICRO),
+ (long) (gnssPowerStats.getMultibandTrackingModeEnergyMilliJoule()
+ * CONVERT_MILLI_TO_MICRO),
+ (long) (gnssPowerStats.getSinglebandAcquisitionModeEnergyMilliJoule()
+ * CONVERT_MILLI_TO_MICRO),
+ (long) (gnssPowerStats.getMultibandAcquisitionModeEnergyMilliJoule()
+ * CONVERT_MILLI_TO_MICRO),
+ (long) (otherModesEnergyMilliJoule[0] * CONVERT_MILLI_TO_MICRO),
+ (long) (otherModesEnergyMilliJoule[1] * CONVERT_MILLI_TO_MICRO),
+ (long) (otherModesEnergyMilliJoule[2] * CONVERT_MILLI_TO_MICRO),
+ (long) (otherModesEnergyMilliJoule[3] * CONVERT_MILLI_TO_MICRO),
+ (long) (otherModesEnergyMilliJoule[4] * CONVERT_MILLI_TO_MICRO),
+ (long) (otherModesEnergyMilliJoule[5] * CONVERT_MILLI_TO_MICRO),
+ (long) (otherModesEnergyMilliJoule[6] * CONVERT_MILLI_TO_MICRO),
+ (long) (otherModesEnergyMilliJoule[7] * CONVERT_MILLI_TO_MICRO),
+ (long) (otherModesEnergyMilliJoule[8] * CONVERT_MILLI_TO_MICRO),
+ (long) (otherModesEnergyMilliJoule[9] * CONVERT_MILLI_TO_MICRO)));
+ } else {
throw new UnsupportedOperationException("Unknown tagId = " + atomTag);
}
- data.add(FrameworkStatsLog.buildStatsEvent(atomTag,
- mLocationFailureReportsStatistics.getCount(),
- mLocationFailureReportsStatistics.getLongSum(),
- mTimeToFirstFixMilliSReportsStatistics.getCount(),
- mTimeToFirstFixMilliSReportsStatistics.getLongSum(),
- mPositionAccuracyMetersReportsStatistics.getCount(),
- mPositionAccuracyMetersReportsStatistics.getLongSum(),
- mTopFourAverageCn0DbmHzReportsStatistics.getCount(),
- mTopFourAverageCn0DbmHzReportsStatistics.getLongSum(),
- mL5TopFourAverageCn0DbmHzReportsStatistics.getCount(),
- mL5TopFourAverageCn0DbmHzReportsStatistics.getLongSum(), mSvStatusReports,
- mSvStatusReportsUsedInFix, mL5SvStatusReports, mL5SvStatusReportsUsedInFix));
return StatsManager.PULL_SUCCESS;
}
}
-}
+}
\ No newline at end of file
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/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 46ece74..db2e908 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -563,8 +563,8 @@
final PowerExemptionManager powerExemptionManager = userContext.getSystemService(
PowerExemptionManager.class);
powerExemptionManager.addToTemporaryAllowList(targetPackage,
- FGS_STARTS_TEMP_ALLOWLIST_DURATION_MS,
- PowerExemptionManager.REASON_MEDIA_SESSION_CALLBACK, reason);
+ PowerExemptionManager.REASON_MEDIA_SESSION_CALLBACK, reason,
+ FGS_STARTS_TEMP_ALLOWLIST_DURATION_MS);
}
} finally {
Binder.restoreCallingIdentity(token);
diff --git a/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java b/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
index 23195bb..1e0142c 100644
--- a/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
+++ b/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
@@ -75,6 +75,8 @@
.writeLong(metrics.getNetworkBytesRead())
.writeLong(metrics.getLocalBytesRead())
.writeLong(metrics.getNetworkTransferDurationMillis())
+ // Raw bytes type not allowed in atoms
+ .writeString(Base64.encodeToString(metrics.getDrmSessionId(), Base64.DEFAULT))
.usePooledBuffer()
.build();
StatsLog.write(statsEvent);
@@ -154,9 +156,10 @@
.writeString(event.getLanguage())
.writeString(event.getLanguageRegion())
.writeInt(event.getChannelCount())
- .writeInt(event.getSampleRate())
+ .writeInt(event.getAudioSampleRate())
.writeInt(event.getWidth())
.writeInt(event.getHeight())
+ .writeFloat(event.getVideoFrameRate())
.usePooledBuffer()
.build();
StatsLog.write(statsEvent);
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/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 350563f..16eac91 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -58,7 +58,9 @@
import static android.net.NetworkPolicy.LIMIT_DISABLED;
import static android.net.NetworkPolicy.SNOOZE_NEVER;
import static android.net.NetworkPolicy.WARNING_DISABLED;
+import static android.net.NetworkPolicyManager.ALLOWED_METERED_REASON_FOREGROUND;
import static android.net.NetworkPolicyManager.ALLOWED_METERED_REASON_MASK;
+import static android.net.NetworkPolicyManager.ALLOWED_METERED_REASON_SYSTEM;
import static android.net.NetworkPolicyManager.ALLOWED_METERED_REASON_USER_EXEMPTED;
import static android.net.NetworkPolicyManager.ALLOWED_REASON_FOREGROUND;
import static android.net.NetworkPolicyManager.ALLOWED_REASON_NONE;
@@ -178,7 +180,6 @@
import android.net.INetworkPolicyListener;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
-import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkIdentity;
@@ -1919,16 +1920,7 @@
* Collect all ifaces from a {@link NetworkStateSnapshot} into the given set.
*/
private static void collectIfaces(ArraySet<String> ifaces, NetworkStateSnapshot snapshot) {
- final String baseIface = snapshot.linkProperties.getInterfaceName();
- if (baseIface != null) {
- ifaces.add(baseIface);
- }
- for (LinkProperties stackedLink : snapshot.linkProperties.getStackedLinks()) {
- final String stackedIface = stackedLink.getInterfaceName();
- if (stackedIface != null) {
- ifaces.add(stackedIface);
- }
- }
+ ifaces.addAll(snapshot.linkProperties.getAllInterfaceNames());
}
/**
@@ -4634,8 +4626,8 @@
newBlockedReasons |= (mRestrictBackground ? BLOCKED_METERED_REASON_DATA_SAVER : 0);
newBlockedReasons |= (isDenied ? BLOCKED_METERED_REASON_USER_RESTRICTED : 0);
- newAllowedReasons |= (isSystem(uid) ? ALLOWED_REASON_SYSTEM : 0);
- newAllowedReasons |= (isForeground ? ALLOWED_REASON_FOREGROUND : 0);
+ newAllowedReasons |= (isSystem(uid) ? ALLOWED_METERED_REASON_SYSTEM : 0);
+ newAllowedReasons |= (isForeground ? ALLOWED_METERED_REASON_FOREGROUND : 0);
newAllowedReasons |= (isAllowed ? ALLOWED_METERED_REASON_USER_EXEMPTED : 0);
if (LOGV) {
@@ -4709,18 +4701,18 @@
// Dispatch changed rule to existing listeners.
mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRules).sendToTarget();
+ }
- final int oldEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons;
- uidBlockedState.blockedReasons = (uidBlockedState.blockedReasons
- & ~BLOCKED_METERED_REASON_MASK) | newBlockedReasons;
- uidBlockedState.allowedReasons = (uidBlockedState.allowedReasons
- & ~ALLOWED_METERED_REASON_MASK) | newAllowedReasons;
- uidBlockedState.updateEffectiveBlockedReasons();
- if (oldEffectiveBlockedReasons != uidBlockedState.effectiveBlockedReasons) {
- mHandler.obtainMessage(MSG_BLOCKED_REASON_CHANGED, uid,
- uidBlockedState.effectiveBlockedReasons, oldEffectiveBlockedReasons)
- .sendToTarget();
- }
+ final int oldEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons;
+ uidBlockedState.blockedReasons = (uidBlockedState.blockedReasons
+ & ~BLOCKED_METERED_REASON_MASK) | newBlockedReasons;
+ uidBlockedState.allowedReasons = (uidBlockedState.allowedReasons
+ & ~ALLOWED_METERED_REASON_MASK) | newAllowedReasons;
+ uidBlockedState.updateEffectiveBlockedReasons();
+ if (oldEffectiveBlockedReasons != uidBlockedState.effectiveBlockedReasons) {
+ mHandler.obtainMessage(MSG_BLOCKED_REASON_CHANGED, uid,
+ uidBlockedState.effectiveBlockedReasons, oldEffectiveBlockedReasons)
+ .sendToTarget();
}
}
@@ -5868,12 +5860,17 @@
return;
}
if ((allowedReasons & ALLOWED_REASON_SYSTEM) != 0) {
- effectiveBlockedReasons = BLOCKED_REASON_NONE;
+ effectiveBlockedReasons = (blockedReasons & ALLOWED_METERED_REASON_MASK);
+ }
+ if ((allowedReasons & ALLOWED_METERED_REASON_SYSTEM) != 0) {
+ effectiveBlockedReasons = (blockedReasons & ~ALLOWED_METERED_REASON_MASK);
}
if ((allowedReasons & ALLOWED_REASON_FOREGROUND) != 0) {
effectiveBlockedReasons &= ~BLOCKED_REASON_BATTERY_SAVER;
effectiveBlockedReasons &= ~BLOCKED_REASON_DOZE;
effectiveBlockedReasons &= ~BLOCKED_REASON_APP_STANDBY;
+ }
+ if ((allowedReasons & ALLOWED_METERED_REASON_FOREGROUND) != 0) {
effectiveBlockedReasons &= ~BLOCKED_METERED_REASON_DATA_SAVER;
effectiveBlockedReasons &= ~BLOCKED_METERED_REASON_USER_RESTRICTED;
}
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 7b376847..fe43c31 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -92,7 +92,6 @@
import android.net.INetworkManagementEventObserver;
import android.net.INetworkStatsService;
import android.net.INetworkStatsSession;
-import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkIdentity;
@@ -131,6 +130,7 @@
import android.service.NetworkStatsServiceDumpProto;
import android.telephony.PhoneStateListener;
import android.telephony.SubscriptionPlan;
+import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -1358,17 +1358,18 @@
// (or non eBPF offloaded) TX they would appear on both, however egress interface
// accounting is explicitly bypassed for traffic from the clat uid.
//
- final List<LinkProperties> stackedLinks = snapshot.linkProperties.getStackedLinks();
- for (LinkProperties stackedLink : stackedLinks) {
- final String stackedIface = stackedLink.getInterfaceName();
- if (stackedIface != null) {
- findOrCreateNetworkIdentitySet(mActiveIfaces, stackedIface).add(ident);
- findOrCreateNetworkIdentitySet(mActiveUidIfaces, stackedIface).add(ident);
+ // TODO: This code might be combined to above code.
+ for (String iface : snapshot.linkProperties.getAllInterfaceNames()) {
+ // baseIface has been handled, so ignore it.
+ if (TextUtils.equals(baseIface, iface)) continue;
+ if (iface != null) {
+ findOrCreateNetworkIdentitySet(mActiveIfaces, iface).add(ident);
+ findOrCreateNetworkIdentitySet(mActiveUidIfaces, iface).add(ident);
if (isMobile) {
- mobileIfaces.add(stackedIface);
+ mobileIfaces.add(iface);
}
- mStatsFactory.noteStackedIface(stackedIface, baseIface);
+ mStatsFactory.noteStackedIface(iface, baseIface);
}
}
}
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index 78c1a95..3238f1f 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -25,6 +25,7 @@
import android.net.Uri;
import android.os.IBinder;
import android.os.IInterface;
+import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
@@ -41,8 +42,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.notification.NotificationManagerService.DumpFilter;
-import org.xmlpull.v1.XmlSerializer;
-
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -78,8 +77,8 @@
public void addSystemProvider(SystemConditionProviderService service) {
mSystemConditionProviders.add(service);
service.attachBase(mContext);
- registerSystemService(
- service.asInterface(), service.getComponent(), UserHandle.USER_SYSTEM);
+ registerSystemService(service.asInterface(), service.getComponent(), UserHandle.USER_SYSTEM,
+ Process.SYSTEM_UID);
}
public Iterable<SystemConditionProviderService> getSystemProviders() {
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index bbdcac2..3007515 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -317,9 +317,9 @@
private ManagedServiceInfo newServiceInfo(IInterface service,
ComponentName component, int userId, boolean isSystem, ServiceConnection connection,
- int targetSdkVersion) {
+ int targetSdkVersion, int uid) {
return new ManagedServiceInfo(service, component, userId, isSystem, connection,
- targetSdkVersion);
+ targetSdkVersion, uid);
}
public void onBootPhaseAppsCanStart() {}
@@ -974,10 +974,11 @@
unregisterServiceImpl(service, userid);
}
- public void registerSystemService(IInterface service, ComponentName component, int userid) {
+ public void registerSystemService(IInterface service, ComponentName component, int userid,
+ int uid) {
checkNotNull(service);
ManagedServiceInfo info = registerServiceImpl(
- service, component, userid, Build.VERSION_CODES.CUR_DEVELOPMENT);
+ service, component, userid, Build.VERSION_CODES.CUR_DEVELOPMENT, uid);
if (info != null) {
onServiceAdded(info);
}
@@ -1441,6 +1442,7 @@
}
final int targetSdkVersion =
appInfo != null ? appInfo.targetSdkVersion : Build.VERSION_CODES.BASE;
+ final int uid = appInfo != null ? appInfo.uid : -1;
try {
Slog.v(TAG, "binding: " + intent);
@@ -1457,7 +1459,7 @@
try {
mService = asInterface(binder);
info = newServiceInfo(mService, name,
- userid, isSystem, this, targetSdkVersion);
+ userid, isSystem, this, targetSdkVersion, uid);
binder.linkToDeath(info, 0);
added = mServices.add(info);
} catch (RemoteException e) {
@@ -1576,9 +1578,9 @@
}
private ManagedServiceInfo registerServiceImpl(final IInterface service,
- final ComponentName component, final int userid, int targetSdk) {
+ final ComponentName component, final int userid, int targetSdk, int uid) {
ManagedServiceInfo info = newServiceInfo(service, component, userid,
- true /*isSystem*/, null /*connection*/, targetSdk);
+ true /*isSystem*/, null /*connection*/, targetSdk, uid);
return registerServiceImpl(info);
}
@@ -1624,15 +1626,18 @@
public ServiceConnection connection;
public int targetSdkVersion;
public Pair<ComponentName, Integer> mKey;
+ public int uid;
public ManagedServiceInfo(IInterface service, ComponentName component,
- int userid, boolean isSystem, ServiceConnection connection, int targetSdkVersion) {
+ int userid, boolean isSystem, ServiceConnection connection, int targetSdkVersion,
+ int uid) {
this.service = service;
this.component = component;
this.userid = userid;
this.isSystem = isSystem;
this.connection = connection;
this.targetSdkVersion = targetSdkVersion;
+ this.uid = uid;
mKey = Pair.create(component, userid);
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index eb4f9d3..08dbd77 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -60,7 +60,6 @@
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.media.AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
@@ -85,6 +84,8 @@
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
import static android.service.notification.NotificationListenerService.REASON_CHANNEL_BANNED;
+import static android.service.notification.NotificationListenerService.REASON_CHANNEL_REMOVED;
+import static android.service.notification.NotificationListenerService.REASON_CLEAR_DATA;
import static android.service.notification.NotificationListenerService.REASON_CLICK;
import static android.service.notification.NotificationListenerService.REASON_ERROR;
import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
@@ -456,6 +457,13 @@
private static final long NOTIFICATION_TRAMPOLINE_BLOCK = 167676448L;
/**
+ * Whether a notification listeners can understand new, more specific, cancellation reasons.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
+ private static final long NOTIFICATION_CANCELLATION_REASONS = 175319604L;
+
+ /**
* Rate limit showing toasts, on a per package basis.
*
* It limits the number of {@link android.widget.Toast#show()} calls to prevent overburdening
@@ -3599,7 +3607,7 @@
}
enforceDeletingChannelHasNoFgService(pkg, callingUser, channelId);
cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true,
- callingUser, REASON_CHANNEL_BANNED, null);
+ callingUser, REASON_CHANNEL_REMOVED, null);
mPreferencesHelper.deleteNotificationChannel(pkg, callingUid, channelId);
mListeners.notifyNotificationChannelChanged(pkg,
UserHandle.getUserHandleForUid(callingUid),
@@ -3623,7 +3631,7 @@
final String channelId = nc.getId();
mAmi.stopForegroundServicesForChannel(pkg, appUserId, channelId);
cancelAllNotificationsInt(MY_UID, MY_PID, pkg, nc.getId(), 0, 0, true,
- appUserId, REASON_CHANNEL_BANNED, null);
+ appUserId, REASON_CHANNEL_REMOVED, null);
mPreferencesHelper.deleteNotificationChannel(pkg, uid, channelId);
mListeners.notifyNotificationChannelChanged(pkg,
UserHandle.getUserHandleForUid(uid),
@@ -3672,7 +3680,7 @@
final NotificationChannel deletedChannel = deletedChannels.get(i);
cancelAllNotificationsInt(MY_UID, MY_PID, pkg, deletedChannel.getId(), 0, 0,
true,
- userId, REASON_CHANNEL_BANNED,
+ userId, REASON_CHANNEL_REMOVED,
null);
mListeners.notifyNotificationChannelChanged(pkg,
UserHandle.getUserHandleForUid(callingUid),
@@ -3852,7 +3860,7 @@
// Cancel posted notifications
final int userId = UserHandle.getUserId(uid);
cancelAllNotificationsInt(MY_UID, MY_PID, packageName, null, 0, 0, true,
- UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED, null);
+ UserHandle.getUserId(Binder.getCallingUid()), REASON_CLEAR_DATA, null);
// Zen
packagesChanged |=
@@ -4128,7 +4136,7 @@
public void registerListener(final INotificationListener listener,
final ComponentName component, final int userid) {
enforceSystemOrSystemUI("INotificationManager.registerListener");
- mListeners.registerSystemService(listener, component, userid);
+ mListeners.registerSystemService(listener, component, userid, Binder.getCallingUid());
}
/**
@@ -6075,7 +6083,7 @@
mPreferencesHelper.deleteConversations(pkg, uid, shortcuts);
for (String channelId : deletedChannelIds) {
cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true,
- UserHandle.getUserId(uid), REASON_CHANNEL_BANNED,
+ UserHandle.getUserId(uid), REASON_CHANNEL_REMOVED,
null);
}
handleSavePolicyFile();
@@ -7482,9 +7490,11 @@
boolean rateLimitingEnabled =
!mToastRateLimitingDisabledUids.contains(record.uid);
boolean isWithinQuota =
- mToastRateLimiter.isWithinQuota(userId, record.pkg, TOAST_QUOTA_TAG);
+ mToastRateLimiter.isWithinQuota(userId, record.pkg, TOAST_QUOTA_TAG)
+ || isExemptFromRateLimiting(record.pkg, userId);
- if (tryShowToast(record, rateLimitingEnabled, isWithinQuota)) {
+ if (tryShowToast(
+ record, rateLimitingEnabled, isWithinQuota)) {
scheduleDurationReachedLocked(record, lastToastWasTextRecord);
mIsCurrentToastShown = true;
if (rateLimitingEnabled) {
@@ -7518,6 +7528,18 @@
return record.show();
}
+ private boolean isExemptFromRateLimiting(String pkg, int userId) {
+ boolean isExemptFromRateLimiting = false;
+ try {
+ isExemptFromRateLimiting = mPackageManager.checkPermission(
+ android.Manifest.permission.UNLIMITED_TOASTS, pkg, userId)
+ == PackageManager.PERMISSION_GRANTED;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to connect with package manager");
+ }
+ return isExemptFromRateLimiting;
+ }
+
/** Reports rate limiting toasts compat change (used when the toast was blocked). */
private void reportCompatRateLimitingToastsChange(int uid) {
final long id = Binder.clearCallingIdentity();
@@ -10324,6 +10346,10 @@
final INotificationListener listener = (INotificationListener) info.service;
StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
try {
+ if (!CompatChanges.isChangeEnabled(NOTIFICATION_CANCELLATION_REASONS, info.uid)
+ && (reason == REASON_CHANNEL_REMOVED || reason == REASON_CLEAR_DATA)) {
+ reason = REASON_CHANNEL_BANNED;
+ }
listener.onNotificationRemoved(sbnHolder, rankingUpdate, stats, reason);
} catch (RemoteException ex) {
Slog.e(TAG, "unable to notify listener (removed): " + info, ex);
diff --git a/services/core/java/com/android/server/os/NativeTombstoneManager.java b/services/core/java/com/android/server/os/NativeTombstoneManager.java
index 9c4c510..cc6a824 100644
--- a/services/core/java/com/android/server/os/NativeTombstoneManager.java
+++ b/services/core/java/com/android/server/os/NativeTombstoneManager.java
@@ -411,8 +411,13 @@
processName = stream.readString(Tombstone.PROCESS_NAME);
break;
- case (int) Tombstone.CAUSE:
- long token = stream.start(Tombstone.CAUSE);
+ case (int) Tombstone.CAUSES:
+ if (!crashReason.equals("")) {
+ // Causes appear in decreasing order of likelihood. For now we only
+ // want the most likely crash reason here, so ignore all others.
+ break;
+ }
+ long token = stream.start(Tombstone.CAUSES);
cause:
while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
switch (stream.getFieldNumber()) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 43eeb2a5..b27c0bd 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -22903,9 +22903,14 @@
@Override
public String getWellbeingPackageName() {
- return CollectionUtils.firstOrNull(
- mContext.getSystemService(RoleManager.class).getRoleHolders(
- RoleManager.ROLE_SYSTEM_WELLBEING));
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return CollectionUtils.firstOrNull(
+ mContext.getSystemService(RoleManager.class).getRoleHolders(
+ RoleManager.ROLE_SYSTEM_WELLBEING));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/pm/permission/Permission.java b/services/core/java/com/android/server/pm/permission/Permission.java
index b421cfc..cda4806 100644
--- a/services/core/java/com/android/server/pm/permission/Permission.java
+++ b/services/core/java/com/android/server/pm/permission/Permission.java
@@ -435,11 +435,12 @@
}
}
}
+ boolean wasNonRuntime = permission != null && permission.mType != TYPE_CONFIG
+ && !permission.isRuntime();
if (permission == null) {
permission = new Permission(permissionInfo.name, permissionInfo.packageName,
TYPE_MANIFEST);
}
- boolean wasNonRuntime = !permission.isRuntime();
StringBuilder r = null;
if (!permission.mReconciled) {
if (permission.mPermissionInfo.packageName == null
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 616058f..3bb5c16 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -2937,7 +2937,8 @@
>= Build.VERSION_CODES.M;
for (String permission : ps.getGrantedPermissions()) {
- if (!pkg.getImplicitPermissions().contains(permission)) {
+ if (pkg.getRequestedPermissions().contains(permission)
+ && !pkg.getImplicitPermissions().contains(permission)) {
Permission bp = mRegistry.getPermission(permission);
if (bp != null && bp.isRuntime()) {
int flags = ps.getPermissionFlags(permission);
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
index cb3b5c9..44ff3eb 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java
@@ -22,7 +22,6 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.Binder;
import com.android.internal.util.CollectionUtils;
import com.android.server.compat.PlatformCompat;
@@ -77,9 +76,7 @@
static boolean isChangeEnabled(PlatformCompat platformCompat, AndroidPackage pkg,
long changeId) {
- //noinspection ConstantConditions
- return Binder.withCleanCallingIdentity(
- () -> platformCompat.isChangeEnabled(changeId, buildMockAppInfo(pkg)));
+ return platformCompat.isChangeEnabledInternalNoLogging(changeId, buildMockAppInfo(pkg));
}
/**
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/rotationresolver/RotationResolverManagerService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
index a7f3cdb..c029bf5 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
@@ -28,6 +28,7 @@
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.Context;
+import android.hardware.SensorPrivacyManager;
import android.os.Binder;
import android.os.CancellationSignal;
import android.os.ResultReceiver;
@@ -79,6 +80,7 @@
FrameworkStatsLog.AUTO_ROTATE_REPORTED__PROPOSED_ORIENTATION__FAILURE;
private final Context mContext;
+ private final SensorPrivacyManager mPrivacyManager;
boolean mIsServiceEnabled;
public RotationResolverManagerService(Context context) {
@@ -89,6 +91,7 @@
PACKAGE_UPDATE_POLICY_REFRESH_EAGER
| /*To avoid high rotation latency*/ PACKAGE_RESTART_POLICY_REFRESH_EAGER);
mContext = context;
+ mPrivacyManager = SensorPrivacyManager.getInstance(context);
}
@Override
@@ -156,15 +159,22 @@
Objects.requireNonNull(callbackInternal);
Objects.requireNonNull(cancellationSignalInternal);
synchronized (mLock) {
- if (mIsServiceEnabled) {
- final RotationResolverManagerPerUserService service = getServiceForUserLocked(
- UserHandle.getCallingUserId());
+ final boolean isCameraAvailable = !mPrivacyManager.isSensorPrivacyEnabled(
+ SensorPrivacyManager.Sensors.CAMERA);
+ if (mIsServiceEnabled && isCameraAvailable) {
+ final RotationResolverManagerPerUserService service =
+ getServiceForUserLocked(
+ UserHandle.getCallingUserId());
final RotationResolutionRequest request = new RotationResolutionRequest("",
currentRotation, proposedRotation, true, timeout);
service.resolveRotationLocked(callbackInternal, request,
cancellationSignalInternal);
} else {
- Slog.w(TAG, "Rotation Resolver service is disabled.");
+ if (isCameraAvailable) {
+ Slog.w(TAG, "Rotation Resolver service is disabled.");
+ } else {
+ Slog.w(TAG, "Camera is locked by a toggle.");
+ }
callbackInternal.onFailure(ROTATION_RESULT_FAILURE_CANCELLED);
logRotationStats(proposedRotation, currentRotation, RESOLUTION_DISABLED);
}
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/ServiceConfigAccessor.java b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
index 2452c8d..222e852 100644
--- a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
+++ b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
@@ -18,8 +18,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
-import android.net.ConnectivityManager;
import android.os.SystemProperties;
import android.util.ArraySet;
@@ -124,9 +124,7 @@
* device.
*/
public boolean isTelephonyTimeZoneDetectionFeatureSupported() {
- // TODO b/150583524 Avoid the use of a deprecated API.
- return mContext.getSystemService(ConnectivityManager.class)
- .isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+ return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
}
/**
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/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index e6d37b6..b947c88 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1511,10 +1511,15 @@
IBinder topFocusedWindowToken = null;
synchronized (mService.mGlobalLock) {
- // Do not send the windows if there is no top focus as
- // the window manager is still looking for where to put it.
- // We will do the work when we get a focus change callback.
- final WindowState topFocusedWindowState = getTopFocusWindow();
+ // If there is a recents animation running, then use the animation target as the
+ // top window state. Otherwise,do not send the windows if there is no top focus as
+ // the window manager is still looking for where to put it. We will do the work when
+ // we get a focus change callback.
+ final RecentsAnimationController controller =
+ mService.getRecentsAnimationController();
+ final WindowState topFocusedWindowState = controller != null
+ ? controller.getTargetAppMainWindow()
+ : getTopFocusWindow();
if (topFocusedWindowState == null) {
if (DEBUG) {
Slog.d(LOG_TAG, "top focused window is null, compute it again later");
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 3a0eb39..dc69175 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -287,6 +287,11 @@
if (mLastLaunchedActivity == r) {
return;
}
+ if (mLastLaunchedActivity != null) {
+ // Transfer the launch cookie because it is a consecutive launch event.
+ r.mLaunchCookie = mLastLaunchedActivity.mLaunchCookie;
+ mLastLaunchedActivity.mLaunchCookie = null;
+ }
mLastLaunchedActivity = r;
if (!r.noDisplay && !r.isReportedDrawn()) {
if (DEBUG_METRICS) Slog.i(TAG, "Add pending draw " + r);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 7c23661..eadfbe2 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -6790,7 +6790,12 @@
// The app bounds hasn't been computed yet.
return false;
}
- final Configuration parentConfig = getParent().getConfiguration();
+ final WindowContainer parent = getParent();
+ if (parent == null) {
+ // The parent of detached Activity can be null.
+ return false;
+ }
+ final Configuration parentConfig = parent.getConfiguration();
// Although colorMode, screenLayout, smallestScreenWidthDp are also fixed, generally these
// fields should be changed with density and bounds, so here only compares the most
// significant field.
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 060323c..12c67bb 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -607,4 +607,10 @@
*/
void commit() throws RemoteException;
}
+
+ /**
+ * A utility method to check AppOps and PackageManager for SYSTEM_ALERT_WINDOW permission.
+ */
+ public abstract boolean hasSystemAlertWindowPermission(int callingUid, int callingPid,
+ String callingPackage);
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 09f5c93..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;
@@ -933,7 +934,8 @@
return getUserManager().hasUserRestriction(restriction, userId);
}
- boolean hasSystemAlertWindowPermission(int callingUid, int callingPid, String callingPackage) {
+ boolean hasSystemAlertWindowPermission(int callingUid, int callingPid,
+ String callingPackage) {
final int mode = getAppOpsManager().noteOpNoThrow(AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
callingUid, callingPackage, /* featureId */ null, "");
if (mode == AppOpsManager.MODE_DEFAULT) {
@@ -2827,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
@@ -2844,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;
@@ -3047,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()");
@@ -3064,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) {
@@ -6370,6 +6407,13 @@
return new PackageConfigurationUpdaterImpl(Binder.getCallingPid());
}
}
+
+ @Override
+ public boolean hasSystemAlertWindowPermission(int callingUid, int callingPid,
+ String callingPackage) {
+ return ActivityTaskManagerService.this.hasSystemAlertWindowPermission(callingUid,
+ callingPid, callingPackage);
+ }
}
final class PackageConfigurationUpdaterImpl implements
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 6d24105..6072a06 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -516,7 +516,7 @@
/** The delay to avoid toggling the animation quickly. */
private static final long FIXED_ROTATION_HIDE_ANIMATION_DEBOUNCE_DELAY_MS = 250;
- private FixedRotationAnimationController mFixedRotationAnimationController;
+ private FadeRotationAnimationController mFadeRotationAnimationController;
final FixedRotationTransitionListener mFixedRotationTransitionListener =
new FixedRotationTransitionListener();
@@ -1590,8 +1590,8 @@
}
@VisibleForTesting
- @Nullable FixedRotationAnimationController getFixedRotationAnimationController() {
- return mFixedRotationAnimationController;
+ @Nullable FadeRotationAnimationController getFadeRotationAnimationController() {
+ return mFadeRotationAnimationController;
}
void setFixedRotationLaunchingAppUnchecked(@Nullable ActivityRecord r) {
@@ -1601,13 +1601,13 @@
void setFixedRotationLaunchingAppUnchecked(@Nullable ActivityRecord r, int rotation) {
if (mFixedRotationLaunchingApp == null && r != null) {
mWmService.mDisplayNotificationController.dispatchFixedRotationStarted(this, rotation);
- startFixedRotationAnimation(
+ startFadeRotationAnimation(
// Delay the hide animation to avoid blinking by clicking navigation bar that
// may toggle fixed rotation in a short time.
r == mFixedRotationTransitionListener.mAnimatingRecents /* shouldDebounce */);
} else if (mFixedRotationLaunchingApp != null && r == null) {
mWmService.mDisplayNotificationController.dispatchFixedRotationFinished(this);
- finishFixedRotationAnimationIfPossible();
+ finishFadeRotationAnimationIfPossible();
}
mFixedRotationLaunchingApp = r;
}
@@ -1714,12 +1714,12 @@
*
* @return {@code true} if the animation is executed right now.
*/
- private boolean startFixedRotationAnimation(boolean shouldDebounce) {
+ private boolean startFadeRotationAnimation(boolean shouldDebounce) {
if (shouldDebounce) {
mWmService.mH.postDelayed(() -> {
synchronized (mWmService.mGlobalLock) {
if (mFixedRotationLaunchingApp != null
- && startFixedRotationAnimation(false /* shouldDebounce */)) {
+ && startFadeRotationAnimation(false /* shouldDebounce */)) {
// Apply the transaction so the animation leash can take effect immediately.
getPendingTransaction().apply();
}
@@ -1727,23 +1727,41 @@
}, FIXED_ROTATION_HIDE_ANIMATION_DEBOUNCE_DELAY_MS);
return false;
}
- if (mFixedRotationAnimationController == null) {
- mFixedRotationAnimationController = new FixedRotationAnimationController(this);
- mFixedRotationAnimationController.hide();
+ if (mFadeRotationAnimationController == null) {
+ mFadeRotationAnimationController = new FadeRotationAnimationController(this);
+ mFadeRotationAnimationController.hide();
return true;
}
return false;
}
/** Re-show the previously hidden windows if all seamless rotated windows are done. */
- void finishFixedRotationAnimationIfPossible() {
- final FixedRotationAnimationController controller = mFixedRotationAnimationController;
+ void finishFadeRotationAnimationIfPossible() {
+ final FadeRotationAnimationController controller = mFadeRotationAnimationController;
if (controller != null && !mDisplayRotation.hasSeamlessRotatingWindow()) {
controller.show();
- mFixedRotationAnimationController = null;
+ mFadeRotationAnimationController = null;
}
}
+ /** Shows the given window which may be hidden for screen frozen. */
+ void finishFadeRotationAnimation(WindowState w) {
+ final FadeRotationAnimationController controller = mFadeRotationAnimationController;
+ if (controller != null && controller.show(w.mToken)) {
+ mFadeRotationAnimationController = null;
+ }
+ }
+
+ /** Returns {@code true} if the display should wait for the given window to stop freezing. */
+ boolean waitForUnfreeze(WindowState w) {
+ if (w.mForceSeamlesslyRotate) {
+ // The window should look no different before and after rotation.
+ return false;
+ }
+ final FadeRotationAnimationController controller = mFadeRotationAnimationController;
+ return controller == null || !controller.isTargetToken(w.mToken);
+ }
+
void notifyInsetsChanged(Consumer<WindowState> dispatchInsetsChanged) {
if (mFixedRotationLaunchingApp != null) {
// The insets state of fixed rotation app is a rotated copy. Make sure the visibilities
@@ -2964,6 +2982,13 @@
mScreenRotationAnimation.kill();
}
mScreenRotationAnimation = screenRotationAnimation;
+
+ // Hide the windows which are not significant in rotation animation. So that the windows
+ // don't need to block the unfreeze time.
+ if (screenRotationAnimation != null && screenRotationAnimation.hasScreenshot()
+ && mFadeRotationAnimationController == null) {
+ startFadeRotationAnimation(false /* shouldDebounce */);
+ }
}
public ScreenRotationAnimation getRotationAnimation() {
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index d0e4c40..6046cc6 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -626,7 +626,7 @@
}, true /* traverseTopToBottom */);
mSeamlessRotationCount = 0;
mRotatingSeamlessly = false;
- mDisplayContent.finishFixedRotationAnimationIfPossible();
+ mDisplayContent.finishFadeRotationAnimationIfPossible();
}
private void prepareSeamlessRotation() {
@@ -717,7 +717,7 @@
"Performing post-rotate rotation after seamless rotation");
// Finish seamless rotation.
mRotatingSeamlessly = false;
- mDisplayContent.finishFixedRotationAnimationIfPossible();
+ mDisplayContent.finishFadeRotationAnimationIfPossible();
updateRotationAndSendNewConfigIfChanged();
}
diff --git a/services/core/java/com/android/server/wm/FadeRotationAnimationController.java b/services/core/java/com/android/server/wm/FadeRotationAnimationController.java
new file mode 100644
index 0000000..5ee6928
--- /dev/null
+++ b/services/core/java/com/android/server/wm/FadeRotationAnimationController.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_FIXED_TRANSFORM;
+
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+
+import com.android.internal.R;
+
+import java.util.ArrayList;
+
+/**
+ * Controller to fade out and in windows when the display is changing rotation. It can be used for
+ * both fixed rotation and normal rotation to hide some non-activity windows. The caller should show
+ * the windows until they are drawn with the new rotation.
+ */
+public class FadeRotationAnimationController extends FadeAnimationController {
+
+ private final ArrayList<WindowToken> mTargetWindowTokens = new ArrayList<>();
+ private final WindowManagerService mService;
+ /** If non-null, it usually indicates that there will be a screen rotation animation. */
+ private final Runnable mFrozenTimeoutRunnable;
+ private final WindowToken mNavBarToken;
+
+ public FadeRotationAnimationController(DisplayContent displayContent) {
+ super(displayContent);
+ mService = displayContent.mWmService;
+ mFrozenTimeoutRunnable = mService.mDisplayFrozen ? () -> {
+ synchronized (mService.mGlobalLock) {
+ displayContent.finishFadeRotationAnimationIfPossible();
+ mService.mWindowPlacerLocked.performSurfacePlacement();
+ }
+ } : null;
+ final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
+ final WindowState navigationBar = displayPolicy.getNavigationBar();
+ if (navigationBar != null) {
+ mNavBarToken = navigationBar.mToken;
+ final RecentsAnimationController controller = mService.getRecentsAnimationController();
+ final boolean navBarControlledByRecents =
+ controller != null && controller.isNavigationBarAttachedToApp();
+ // Do not animate movable navigation bar (e.g. non-gesture mode) or when the navigation
+ // bar is currently controlled by recents animation.
+ if (!displayPolicy.navigationBarCanMove() && !navBarControlledByRecents) {
+ mTargetWindowTokens.add(mNavBarToken);
+ }
+ } else {
+ mNavBarToken = null;
+ }
+ displayContent.forAllWindows(w -> {
+ if (w.mActivityRecord == null && w.mHasSurface && !w.mForceSeamlesslyRotate
+ && !w.mIsWallpaper && !w.mIsImWindow && w != navigationBar) {
+ mTargetWindowTokens.add(w.mToken);
+ }
+ }, true /* traverseTopToBottom */);
+ }
+
+ /** Applies show animation on the previously hidden window tokens. */
+ void show() {
+ for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
+ final WindowToken windowToken = mTargetWindowTokens.get(i);
+ fadeWindowToken(true /* show */, windowToken, ANIMATION_TYPE_FIXED_TRANSFORM);
+ }
+ mTargetWindowTokens.clear();
+ if (mFrozenTimeoutRunnable != null) {
+ mService.mH.removeCallbacks(mFrozenTimeoutRunnable);
+ }
+ }
+
+ /**
+ * Returns {@code true} if all target windows are shown. It only takes effects if this
+ * controller is created for normal rotation.
+ */
+ boolean show(WindowToken token) {
+ if (mFrozenTimeoutRunnable != null && mTargetWindowTokens.remove(token)) {
+ fadeWindowToken(true /* show */, token, ANIMATION_TYPE_FIXED_TRANSFORM);
+ if (mTargetWindowTokens.isEmpty()) {
+ mService.mH.removeCallbacks(mFrozenTimeoutRunnable);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** Applies hide animation on the window tokens which may be seamlessly rotated later. */
+ void hide() {
+ for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
+ final WindowToken windowToken = mTargetWindowTokens.get(i);
+ fadeWindowToken(false /* show */, windowToken, ANIMATION_TYPE_FIXED_TRANSFORM);
+ }
+ if (mFrozenTimeoutRunnable != null) {
+ mService.mH.postDelayed(mFrozenTimeoutRunnable,
+ WindowManagerService.WINDOW_FREEZE_TIMEOUT_DURATION);
+ }
+ }
+
+ /** Returns {@code true} if the window is handled by this controller. */
+ boolean isTargetToken(WindowToken token) {
+ return token == mNavBarToken || mTargetWindowTokens.contains(token);
+ }
+
+ @Override
+ public Animation getFadeInAnimation() {
+ if (mFrozenTimeoutRunnable != null) {
+ // Use a shorter animation so it is easier to align with screen rotation animation.
+ return AnimationUtils.loadAnimation(mContext, R.anim.screen_rotate_0_enter);
+ }
+ return super.getFadeInAnimation();
+ }
+
+ @Override
+ public Animation getFadeOutAnimation() {
+ if (mFrozenTimeoutRunnable != null) {
+ // Hide the window immediately because screen should have been covered by screenshot.
+ return new AlphaAnimation(0 /* fromAlpha */, 0 /* toAlpha */);
+ }
+ return super.getFadeOutAnimation();
+ }
+}
diff --git a/services/core/java/com/android/server/wm/FixedRotationAnimationController.java b/services/core/java/com/android/server/wm/FixedRotationAnimationController.java
deleted file mode 100644
index aa73170..0000000
--- a/services/core/java/com/android/server/wm/FixedRotationAnimationController.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_FIXED_TRANSFORM;
-
-import java.util.ArrayList;
-
-/**
- * Controller to fade out and in system ui when applying a fixed rotation transform to a window
- * token.
- *
- * The system bars will be fade out when the fixed rotation transform starts and will be fade in
- * once all surfaces have been rotated.
- */
-public class FixedRotationAnimationController extends FadeAnimationController {
-
- private final WindowState mStatusBar;
- private final WindowState mNavigationBar;
- private final ArrayList<WindowToken> mAnimatedWindowToken = new ArrayList<>(2);
-
- public FixedRotationAnimationController(DisplayContent displayContent) {
- super(displayContent);
- final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
- mStatusBar = displayPolicy.getStatusBar();
-
- final RecentsAnimationController controller =
- displayContent.mWmService.getRecentsAnimationController();
- final boolean navBarControlledByRecents =
- controller != null && controller.isNavigationBarAttachedToApp();
- // Do not animate movable navigation bar (e.g. non-gesture mode) or when the navigation bar
- // is currently controlled by recents animation.
- mNavigationBar = !displayPolicy.navigationBarCanMove()
- && !navBarControlledByRecents ? displayPolicy.getNavigationBar() : null;
- }
-
- /** Applies show animation on the previously hidden window tokens. */
- void show() {
- for (int i = mAnimatedWindowToken.size() - 1; i >= 0; i--) {
- final WindowToken windowToken = mAnimatedWindowToken.get(i);
- fadeWindowToken(true /* show */, windowToken, ANIMATION_TYPE_FIXED_TRANSFORM);
- }
- }
-
- /** Applies hide animation on the window tokens which may be seamlessly rotated later. */
- void hide() {
- if (mNavigationBar != null) {
- fadeWindowToken(false /* show */, mNavigationBar.mToken,
- ANIMATION_TYPE_FIXED_TRANSFORM);
- }
- if (mStatusBar != null) {
- fadeWindowToken(false /* show */, mStatusBar.mToken,
- ANIMATION_TYPE_FIXED_TRANSFORM);
- }
- }
-
- @Override
- public void fadeWindowToken(boolean show, WindowToken windowToken, int animationType) {
- super.fadeWindowToken(show, windowToken, animationType);
- mAnimatedWindowToken.add(windowToken);
- }
-}
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 465042d..cda3fda 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -25,6 +25,7 @@
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.SyncRtSurfaceTransactionApplier.applyParams;
+import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
@@ -458,10 +459,8 @@
InsetsPolicyAnimationControlCallbacks mControlCallbacks;
InsetsPolicyAnimationControlListener(boolean show, Runnable finishCallback, int types) {
-
- super(show, false /* hasCallbacks */, types, false /* disable */,
- (int) (mDisplayContent.getDisplayMetrics().density * FLOATING_IME_BOTTOM_INSET
- + 0.5f));
+ super(show, false /* hasCallbacks */, types, BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE,
+ false /* disable */, 0 /* floatingImeBottomInsets */);
mFinishCallback = finishCallback;
mControlCallbacks = new InsetsPolicyAnimationControlCallbacks(this);
}
@@ -497,7 +496,7 @@
// the controlled types should be animated regardless of the frame.
mAnimationControl = new InsetsAnimationControlImpl(controls,
null /* frame */, state, mListener, typesReady, this,
- mListener.getDurationMs(), InsetsController.SYSTEM_BARS_INTERPOLATOR,
+ mListener.getDurationMs(), getInsetsInterpolator(),
show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE, null /* translator */);
SurfaceAnimationThread.getHandler().post(
() -> mListener.onReady(mAnimationControl, typesReady));
diff --git a/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
index 4ab5cd6..b1e12b6 100644
--- a/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
@@ -77,7 +77,7 @@
final boolean shouldAttachNavBarToApp =
displayContent.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition()
&& service.getRecentsAnimationController() == null
- && displayContent.getFixedRotationAnimationController() == null;
+ && displayContent.getFadeRotationAnimationController() == null;
if (shouldAttachNavBarToApp) {
startNavigationBarWindowAnimation(
displayContent, durationHint, statusBarTransitionDelay, targets,
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 64ff108..bd84854 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -568,8 +568,9 @@
? mMinimizedHomeBounds
: null;
final Rect contentInsets;
- if (mTargetActivityRecord != null && mTargetActivityRecord.findMainWindow() != null) {
- contentInsets = mTargetActivityRecord.findMainWindow()
+ final WindowState targetAppMainWindow = getTargetAppMainWindow();
+ if (targetAppMainWindow != null) {
+ contentInsets = targetAppMainWindow
.getInsetsStateWithVisibilityOverride()
.calculateInsets(mTargetActivityRecord.getBounds(), Type.systemBars(),
false /* ignoreVisibility */);
@@ -607,8 +608,8 @@
private void attachNavigationBarToApp() {
if (!mShouldAttachNavBarToAppDuringTransition
- // Skip the case where the nav bar is controlled by fixed rotation.
- || mDisplayContent.getFixedRotationAnimationController() != null) {
+ // Skip the case where the nav bar is controlled by fade rotation.
+ || mDisplayContent.getFadeRotationAnimationController() != null) {
return;
}
ActivityRecord topActivity = null;
@@ -1004,9 +1005,7 @@
boolean updateInputConsumerForApp(InputWindowHandle inputWindowHandle) {
// Update the input consumer touchable region to match the target app main window
- final WindowState targetAppMainWindow = mTargetActivityRecord != null
- ? mTargetActivityRecord.findMainWindow()
- : null;
+ final WindowState targetAppMainWindow = getTargetAppMainWindow();
if (targetAppMainWindow != null) {
targetAppMainWindow.getBounds(mTmpRect);
inputWindowHandle.touchableRegion.set(mTmpRect);
@@ -1026,6 +1025,13 @@
return mTargetActivityRecord.windowsCanBeWallpaperTarget();
}
+ WindowState getTargetAppMainWindow() {
+ if (mTargetActivityRecord == null) {
+ return null;
+ }
+ return mTargetActivityRecord.findMainWindow();
+ }
+
boolean isAnimatingTask(Task task) {
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
if (task == mPendingAnimations.get(i).mTask) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 57394d6..6b1071c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -433,11 +433,6 @@
public static boolean sEnableRemoteKeyguardAnimation =
SystemProperties.getBoolean(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, false);
- private static final String DISABLE_TRIPLE_BUFFERING_PROPERTY =
- "ro.sf.disable_triple_buffer";
-
- static boolean sEnableTripleBuffering = !SystemProperties.getBoolean(
- DISABLE_TRIPLE_BUFFERING_PROPERTY, false);
/**
* Allows a fullscreen windowing mode activity to launch in its desired orientation directly
@@ -1747,9 +1742,6 @@
if (mUseBLAST) {
res |= WindowManagerGlobal.ADD_FLAG_USE_BLAST;
}
- if (sEnableTripleBuffering) {
- res |= WindowManagerGlobal.ADD_FLAG_USE_TRIPLE_BUFFERING;
- }
if (displayContent.mCurrentFocus == null) {
displayContent.mWinAddedSinceNullFocus.add(win);
diff --git a/services/core/java/com/android/server/wm/WindowOrientationListener.java b/services/core/java/com/android/server/wm/WindowOrientationListener.java
index 5ef9420..49e704f 100644
--- a/services/core/java/com/android/server/wm/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/wm/WindowOrientationListener.java
@@ -1150,7 +1150,8 @@
FrameworkStatsLog.write(
FrameworkStatsLog.DEVICE_ROTATED,
event.timestamp,
- rotationToLogEnum(reportedRotation));
+ rotationToLogEnum(reportedRotation),
+ FrameworkStatsLog.DEVICE_ROTATED__ROTATION_EVENT_TYPE__ACTUAL_EVENT);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 6d88387..6da350b 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1514,11 +1514,20 @@
}
void setOrientationChanging(boolean changing) {
- mOrientationChanging = changing;
mOrientationChangeTimedOut = false;
+ if (mOrientationChanging == changing) {
+ return;
+ }
+ mOrientationChanging = changing;
if (changing) {
mLastFreezeDuration = 0;
- mWmService.mRoot.mOrientationChangeComplete = false;
+ if (mWmService.mRoot.mOrientationChangeComplete
+ && mDisplayContent.waitForUnfreeze(this)) {
+ mWmService.mRoot.mOrientationChangeComplete = false;
+ }
+ } else {
+ // The orientation change is completed. If it was hidden by the animation, reshow it.
+ mDisplayContent.finishFadeRotationAnimation(this);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index ebbebbb..0c80f86 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -656,8 +656,10 @@
if (w.getOrientationChanging()) {
if (!w.isDrawn()) {
- w.mWmService.mRoot.mOrientationChangeComplete = false;
- mAnimator.mLastWindowFreezeSource = w;
+ if (w.mDisplayContent.waitForUnfreeze(w)) {
+ w.mWmService.mRoot.mOrientationChangeComplete = false;
+ mAnimator.mLastWindowFreezeSource = w;
+ }
ProtoLog.v(WM_DEBUG_ORIENTATION,
"Orientation continue waiting for draw in %s", w);
} else {
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 5276d9c8..5163a43 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -758,11 +758,15 @@
/** @see WindowState#freezeInsetsState() */
void setInsetsFrozen(boolean freeze) {
- if (freeze) {
- forAllWindows(WindowState::freezeInsetsState, true /* traverseTopToBottom */);
- } else {
- forAllWindows(WindowState::clearFrozenInsetsState, true /* traverseTopToBottom */);
- }
+ forAllWindows(w -> {
+ if (w.mToken == this) {
+ if (freeze) {
+ w.freezeInsetsState();
+ } else {
+ w.clearFrozenInsetsState();
+ }
+ }
+ }, true /* traverseTopToBottom */);
}
@Override
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index a88f2b4..932e997 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -377,6 +377,7 @@
dprintf(fd, "Mounts (%d): {\n", int(mMounts.size()));
for (auto&& [id, ifs] : mMounts) {
+ std::unique_lock ll(ifs->lock);
const IncFsMount& mnt = *ifs;
dprintf(fd, " [%d]: {\n", id);
if (id != mnt.mountId) {
@@ -422,6 +423,9 @@
}
bool IncrementalService::needStartDataLoaderLocked(IncFsMount& ifs) {
+ if (!ifs.dataLoaderStub) {
+ return false;
+ }
if (ifs.dataLoaderStub->isSystemDataLoader()) {
return true;
}
@@ -439,6 +443,8 @@
std::lock_guard l(mLock);
mounts.reserve(mMounts.size());
for (auto&& [id, ifs] : mMounts) {
+ std::unique_lock ll(ifs->lock);
+
if (ifs->mountId != id) {
continue;
}
@@ -456,7 +462,10 @@
std::thread([this, mounts = std::move(mounts)]() {
mJni->initializeForCurrentThread();
for (auto&& ifs : mounts) {
- ifs->dataLoaderStub->requestStart();
+ std::unique_lock l(ifs->lock);
+ if (ifs->dataLoaderStub) {
+ ifs->dataLoaderStub->requestStart();
+ }
}
}).detach();
}
@@ -671,23 +680,36 @@
setUidReadTimeouts(storageId, std::move(perUidReadTimeouts));
}
- // Re-initialize DataLoader.
- std::unique_lock l(mLock);
- const auto ifs = getIfsLocked(storageId);
- if (!ifs) {
- return false;
- }
- if (ifs->dataLoaderStub) {
- ifs->dataLoaderStub->cleanupResources();
- ifs->dataLoaderStub = {};
- }
- l.unlock();
+ IfsMountPtr ifs;
+ DataLoaderStubPtr dataLoaderStub;
- // DataLoader.
- auto dataLoaderStub =
- prepareDataLoader(*ifs, std::move(dataLoaderParams), std::move(statusListener),
- healthCheckParams, std::move(healthListener));
- CHECK(dataLoaderStub);
+ // Re-initialize DataLoader.
+ {
+ ifs = getIfs(storageId);
+ if (!ifs) {
+ return false;
+ }
+
+ std::unique_lock l(ifs->lock);
+ dataLoaderStub = std::exchange(ifs->dataLoaderStub, nullptr);
+ }
+
+ if (dataLoaderStub) {
+ dataLoaderStub->cleanupResources();
+ dataLoaderStub = {};
+ }
+
+ {
+ std::unique_lock l(ifs->lock);
+ if (ifs->dataLoaderStub) {
+ LOG(INFO) << "Skipped data loader stub creation because it already exists";
+ return false;
+ }
+ prepareDataLoaderLocked(*ifs, std::move(dataLoaderParams), std::move(statusListener),
+ healthCheckParams, std::move(healthListener));
+ CHECK(ifs->dataLoaderStub);
+ dataLoaderStub = ifs->dataLoaderStub;
+ }
if (dataLoaderStub->isSystemDataLoader()) {
// Readlogs from system dataloader (adb) can always be collected.
@@ -705,13 +727,14 @@
<< storageId;
return;
}
+ std::unique_lock l(ifs->lock);
if (ifs->startLoadingTs != startLoadingTs) {
LOG(INFO) << "Can't disable the readlogs, timestamp mismatch (new "
"installation?): "
<< storageId;
return;
}
- setStorageParams(*ifs, storageId, /*enableReadLogs=*/false);
+ disableReadLogsLocked(*ifs);
});
}
@@ -733,17 +756,17 @@
}
void IncrementalService::disallowReadLogs(StorageId storageId) {
- std::unique_lock l(mLock);
- const auto ifs = getIfsLocked(storageId);
+ const auto ifs = getIfs(storageId);
if (!ifs) {
LOG(ERROR) << "disallowReadLogs failed, invalid storageId: " << storageId;
return;
}
+
+ std::unique_lock l(ifs->lock);
if (!ifs->readLogsAllowed()) {
return;
}
ifs->disallowReadLogs();
- l.unlock();
const auto metadata = constants().readLogsDisabledMarkerName;
if (auto err = mIncFs->makeFile(ifs->control,
@@ -755,7 +778,7 @@
return;
}
- setStorageParams(storageId, /*enableReadLogs=*/false);
+ disableReadLogsLocked(*ifs);
}
int IncrementalService::setStorageParams(StorageId storageId, bool enableReadLogs) {
@@ -764,61 +787,66 @@
LOG(ERROR) << "setStorageParams failed, invalid storageId: " << storageId;
return -EINVAL;
}
- return setStorageParams(*ifs, storageId, enableReadLogs);
-}
-int IncrementalService::setStorageParams(IncFsMount& ifs, StorageId storageId,
- bool enableReadLogs) {
- const auto& params = ifs.dataLoaderStub->params();
- if (enableReadLogs) {
- if (!ifs.readLogsAllowed()) {
- LOG(ERROR) << "setStorageParams failed, readlogs disallowed for storageId: "
- << storageId;
- return -EPERM;
- }
-
- // Check loader usage stats permission and apop.
- if (auto status = mAppOpsManager->checkPermission(kLoaderUsageStats, kOpUsage,
- params.packageName.c_str());
- !status.isOk()) {
- LOG(ERROR) << " Permission: " << kLoaderUsageStats
- << " check failed: " << status.toString8();
- return fromBinderStatus(status);
- }
-
- // Check multiuser permission.
- if (auto status = mAppOpsManager->checkPermission(kInteractAcrossUsers, nullptr,
- params.packageName.c_str());
- !status.isOk()) {
- LOG(ERROR) << " Permission: " << kInteractAcrossUsers
- << " check failed: " << status.toString8();
- return fromBinderStatus(status);
- }
-
- // Check installation time.
- const auto now = mClock->now();
- const auto startLoadingTs = ifs.startLoadingTs;
- if (startLoadingTs <= now && now - startLoadingTs > Constants::readLogsMaxInterval) {
- LOG(ERROR) << "setStorageParams failed, readlogs can't be enabled at this time, "
- "storageId: "
- << storageId;
- return -EPERM;
- }
+ std::unique_lock l(ifs->lock);
+ if (!enableReadLogs) {
+ return disableReadLogsLocked(*ifs);
}
- if (auto status = applyStorageParams(ifs, enableReadLogs); !status.isOk()) {
- LOG(ERROR) << "applyStorageParams failed: " << status.toString8();
+ if (!ifs->readLogsAllowed()) {
+ LOG(ERROR) << "enableReadLogs failed, readlogs disallowed for storageId: " << storageId;
+ return -EPERM;
+ }
+
+ if (!ifs->dataLoaderStub) {
+ // This should never happen - only DL can call enableReadLogs.
+ LOG(ERROR) << "enableReadLogs failed: invalid state";
+ return -EPERM;
+ }
+
+ // Check installation time.
+ const auto now = mClock->now();
+ const auto startLoadingTs = ifs->startLoadingTs;
+ if (startLoadingTs <= now && now - startLoadingTs > Constants::readLogsMaxInterval) {
+ LOG(ERROR) << "enableReadLogs failed, readlogs can't be enabled at this time, storageId: "
+ << storageId;
+ return -EPERM;
+ }
+
+ const auto& packageName = ifs->dataLoaderStub->params().packageName;
+
+ // Check loader usage stats permission and apop.
+ if (auto status =
+ mAppOpsManager->checkPermission(kLoaderUsageStats, kOpUsage, packageName.c_str());
+ !status.isOk()) {
+ LOG(ERROR) << " Permission: " << kLoaderUsageStats
+ << " check failed: " << status.toString8();
return fromBinderStatus(status);
}
- if (enableReadLogs) {
- registerAppOpsCallback(params.packageName);
+ // Check multiuser permission.
+ if (auto status =
+ mAppOpsManager->checkPermission(kInteractAcrossUsers, nullptr, packageName.c_str());
+ !status.isOk()) {
+ LOG(ERROR) << " Permission: " << kInteractAcrossUsers
+ << " check failed: " << status.toString8();
+ return fromBinderStatus(status);
}
+ if (auto status = applyStorageParamsLocked(*ifs, /*enableReadLogs=*/true); status != 0) {
+ return status;
+ }
+
+ registerAppOpsCallback(packageName);
+
return 0;
}
-binder::Status IncrementalService::applyStorageParams(IncFsMount& ifs, bool enableReadLogs) {
+int IncrementalService::disableReadLogsLocked(IncFsMount& ifs) {
+ return applyStorageParamsLocked(ifs, /*enableReadLogs=*/false);
+}
+
+int IncrementalService::applyStorageParamsLocked(IncFsMount& ifs, bool enableReadLogs) {
os::incremental::IncrementalFileSystemControlParcel control;
control.cmd.reset(dup(ifs.control.cmd()));
control.pendingReads.reset(dup(ifs.control.pendingReads()));
@@ -832,8 +860,10 @@
if (status.isOk()) {
// Store enabled state.
ifs.setReadLogsEnabled(enableReadLogs);
+ } else {
+ LOG(ERROR) << "applyStorageParams failed: " << status.toString8();
}
- return status;
+ return status.isOk() ? 0 : fromBinderStatus(status);
}
void IncrementalService::deleteStorage(StorageId storageId) {
@@ -1224,9 +1254,14 @@
return;
}
- const auto timeout = std::chrono::duration_cast<milliseconds>(maxPendingTimeUs) -
- Constants::perUidTimeoutOffset;
- updateUidReadTimeouts(storage, Clock::now() + timeout);
+ const auto timeout = Clock::now() + maxPendingTimeUs - Constants::perUidTimeoutOffset;
+ addIfsStateCallback(storage, [this, timeout](StorageId storageId, IfsState state) -> bool {
+ if (checkUidReadTimeouts(storageId, state, timeout)) {
+ return true;
+ }
+ clearUidReadTimeouts(storageId);
+ return false;
+ });
}
void IncrementalService::clearUidReadTimeouts(StorageId storage) {
@@ -1234,39 +1269,32 @@
if (!ifs) {
return;
}
-
mIncFs->setUidReadTimeouts(ifs->control, {});
}
-void IncrementalService::updateUidReadTimeouts(StorageId storage, Clock::time_point timeLimit) {
- // Reached maximum timeout.
+bool IncrementalService::checkUidReadTimeouts(StorageId storage, IfsState state,
+ Clock::time_point timeLimit) {
if (Clock::now() >= timeLimit) {
- return clearUidReadTimeouts(storage);
+ // Reached maximum timeout.
+ return false;
+ }
+ if (state.error) {
+ // Something is wrong, abort.
+ return false;
}
// Still loading?
- const auto state = isMountFullyLoaded(storage);
- if (int(state) < 0) {
- // Something is wrong, abort.
- return clearUidReadTimeouts(storage);
- }
-
- if (state == incfs::LoadingState::Full) {
- // Fully loaded, check readLogs collection.
- const auto ifs = getIfs(storage);
- if (!ifs->readLogsEnabled()) {
- return clearUidReadTimeouts(storage);
- }
+ if (state.fullyLoaded && !state.readLogsEnabled) {
+ return false;
}
const auto timeLeft = timeLimit - Clock::now();
if (timeLeft < Constants::progressUpdateInterval) {
// Don't bother.
- return clearUidReadTimeouts(storage);
+ return false;
}
- addTimedJob(*mTimedQueue, storage, Constants::progressUpdateInterval,
- [this, storage, timeLimit]() { updateUidReadTimeouts(storage, timeLimit); });
+ return true;
}
std::unordered_set<std::string_view> IncrementalService::adoptMountedInstances() {
@@ -1533,7 +1561,7 @@
dataLoaderParams.arguments = loader.arguments();
}
- prepareDataLoader(*ifs, std::move(dataLoaderParams));
+ prepareDataLoaderLocked(*ifs, std::move(dataLoaderParams));
CHECK(ifs->dataLoaderStub);
std::vector<std::pair<std::string, metadata::BindPoint>> bindPoints;
@@ -1615,24 +1643,10 @@
}
}
-IncrementalService::DataLoaderStubPtr IncrementalService::prepareDataLoader(
- IncFsMount& ifs, DataLoaderParamsParcel&& params, DataLoaderStatusListener&& statusListener,
- const StorageHealthCheckParams& healthCheckParams, StorageHealthListener&& healthListener) {
- std::unique_lock l(ifs.lock);
- prepareDataLoaderLocked(ifs, std::move(params), std::move(statusListener), healthCheckParams,
- std::move(healthListener));
- return ifs.dataLoaderStub;
-}
-
void IncrementalService::prepareDataLoaderLocked(IncFsMount& ifs, DataLoaderParamsParcel&& params,
DataLoaderStatusListener&& statusListener,
const StorageHealthCheckParams& healthCheckParams,
StorageHealthListener&& healthListener) {
- if (ifs.dataLoaderStub) {
- LOG(INFO) << "Skipped data loader preparation because it already exists";
- return;
- }
-
FileSystemControlParcel fsControlParcel;
fsControlParcel.incremental = std::make_optional<IncrementalFileSystemControlParcel>();
fsControlParcel.incremental->cmd.reset(dup(ifs.control.cmd()));
@@ -1647,6 +1661,29 @@
new DataLoaderStub(*this, ifs.mountId, std::move(params), std::move(fsControlParcel),
std::move(statusListener), healthCheckParams,
std::move(healthListener), path::join(ifs.root, constants().mount));
+
+ addIfsStateCallback(ifs.mountId, [this](StorageId storageId, IfsState state) -> bool {
+ if (!state.fullyLoaded || state.readLogsEnabled) {
+ return true;
+ }
+
+ DataLoaderStubPtr dataLoaderStub;
+ {
+ const auto ifs = getIfs(storageId);
+ if (!ifs) {
+ return false;
+ }
+
+ std::unique_lock l(ifs->lock);
+ dataLoaderStub = std::exchange(ifs->dataLoaderStub, nullptr);
+ }
+
+ if (dataLoaderStub) {
+ dataLoaderStub->cleanupResources();
+ }
+
+ return false;
+ });
}
template <class Duration>
@@ -2070,11 +2107,11 @@
StorageHealthListener healthListener) {
DataLoaderStubPtr dataLoaderStub;
{
- std::unique_lock l(mLock);
- const auto& ifs = getIfsLocked(storage);
+ const auto& ifs = getIfs(storage);
if (!ifs) {
return false;
}
+ std::unique_lock l(ifs->lock);
dataLoaderStub = ifs->dataLoaderStub;
if (!dataLoaderStub) {
return false;
@@ -2160,13 +2197,16 @@
std::lock_guard l(mLock);
affected.reserve(mMounts.size());
for (auto&& [id, ifs] : mMounts) {
- if (ifs->mountId == id && ifs->dataLoaderStub->params().packageName == packageName) {
+ std::unique_lock ll(ifs->lock);
+
+ if (ifs->mountId == id && ifs->dataLoaderStub &&
+ ifs->dataLoaderStub->params().packageName == packageName) {
affected.push_back(ifs);
}
}
}
for (auto&& ifs : affected) {
- applyStorageParams(*ifs, false);
+ applyStorageParamsLocked(*ifs, /*enableReadLogs=*/false);
}
}
@@ -2187,6 +2227,101 @@
return true;
}
+void IncrementalService::addIfsStateCallback(StorageId storageId, IfsStateCallback callback) {
+ bool wasEmpty;
+ {
+ std::lock_guard l(mIfsStateCallbacksLock);
+ wasEmpty = mIfsStateCallbacks.empty();
+ mIfsStateCallbacks[storageId].emplace_back(std::move(callback));
+ }
+ if (wasEmpty) {
+ addTimedJob(*mTimedQueue, kMaxStorageId, Constants::progressUpdateInterval,
+ [this]() { processIfsStateCallbacks(); });
+ }
+}
+
+void IncrementalService::processIfsStateCallbacks() {
+ StorageId storageId = kInvalidStorageId;
+ std::vector<IfsStateCallback> local;
+ while (true) {
+ {
+ std::lock_guard l(mIfsStateCallbacksLock);
+ if (mIfsStateCallbacks.empty()) {
+ return;
+ }
+ IfsStateCallbacks::iterator it;
+ if (storageId == kInvalidStorageId) {
+ // First entry, initialize the it.
+ it = mIfsStateCallbacks.begin();
+ } else {
+ // Subsequent entries, update the storageId, and shift to the new one.
+ it = mIfsStateCallbacks.find(storageId);
+ if (it == mIfsStateCallbacks.end()) {
+ // Was removed while processing, too bad.
+ break;
+ }
+
+ auto& callbacks = it->second;
+ if (callbacks.empty()) {
+ std::swap(callbacks, local);
+ } else {
+ callbacks.insert(callbacks.end(), local.begin(), local.end());
+ }
+ if (callbacks.empty()) {
+ it = mIfsStateCallbacks.erase(it);
+ if (mIfsStateCallbacks.empty()) {
+ return;
+ }
+ } else {
+ ++it;
+ }
+ }
+
+ if (it == mIfsStateCallbacks.end()) {
+ break;
+ }
+
+ storageId = it->first;
+ auto& callbacks = it->second;
+ if (callbacks.empty()) {
+ // Invalid case, one extra lookup should be ok.
+ continue;
+ }
+ std::swap(callbacks, local);
+ }
+
+ processIfsStateCallbacks(storageId, local);
+ }
+
+ addTimedJob(*mTimedQueue, kMaxStorageId, Constants::progressUpdateInterval,
+ [this]() { processIfsStateCallbacks(); });
+}
+
+void IncrementalService::processIfsStateCallbacks(StorageId storageId,
+ std::vector<IfsStateCallback>& callbacks) {
+ const auto state = isMountFullyLoaded(storageId);
+ IfsState storageState = {};
+ storageState.error = int(state) < 0;
+ storageState.fullyLoaded = state == incfs::LoadingState::Full;
+ if (storageState.fullyLoaded) {
+ const auto ifs = getIfs(storageId);
+ storageState.readLogsEnabled = ifs && ifs->readLogsEnabled();
+ }
+
+ for (auto cur = callbacks.begin(); cur != callbacks.end();) {
+ if ((*cur)(storageId, storageState)) {
+ ++cur;
+ } else {
+ cur = callbacks.erase(cur);
+ }
+ }
+}
+
+void IncrementalService::removeIfsStateCallbacks(StorageId storageId) {
+ std::lock_guard l(mIfsStateCallbacksLock);
+ mIfsStateCallbacks.erase(storageId);
+}
+
void IncrementalService::getMetrics(StorageId storageId, android::os::PersistableBundle* result) {
const auto duration = getMillsSinceOldestPendingRead(storageId);
if (duration >= 0) {
@@ -2197,12 +2332,12 @@
}
long IncrementalService::getMillsSinceOldestPendingRead(StorageId storageId) {
- std::unique_lock l(mLock);
- const auto ifs = getIfsLocked(storageId);
+ const auto ifs = getIfs(storageId);
if (!ifs) {
LOG(ERROR) << "getMillsSinceOldestPendingRead failed, invalid storageId: " << storageId;
return -EINVAL;
}
+ std::unique_lock l(ifs->lock);
if (!ifs->dataLoaderStub) {
LOG(ERROR) << "getMillsSinceOldestPendingRead failed, no data loader: " << storageId;
return -EINVAL;
@@ -2248,6 +2383,7 @@
resetHealthControl();
mService.removeTimedJobs(*mService.mTimedQueue, mId);
}
+ mService.removeIfsStateCallbacks(mId);
requestDestroy();
@@ -2758,7 +2894,7 @@
mService.mLooper->addFd(
pendingReadsFd, android::Looper::POLL_CALLBACK, android::Looper::EVENT_INPUT,
[](int, int, void* data) -> int {
- auto&& self = (DataLoaderStub*)data;
+ auto self = (DataLoaderStub*)data;
self->updateHealthStatus(/*baseline=*/true);
return 0;
},
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index 4a5db06..a8f32de 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -74,6 +74,17 @@
using PerUidReadTimeouts = ::android::os::incremental::PerUidReadTimeouts;
+struct IfsState {
+ // If mount is fully loaded.
+ bool fullyLoaded = false;
+ // If read logs are enabled on this mount. Populated only if fullyLoaded == true.
+ bool readLogsEnabled = false;
+ // If there was an error fetching any of the above.
+ bool error = false;
+};
+// Returns true if wants to be called again.
+using IfsStateCallback = std::function<bool(StorageId, IfsState)>;
+
class IncrementalService final {
public:
explicit IncrementalService(ServiceManagerWrapper&& sm, std::string_view rootDir);
@@ -366,7 +377,7 @@
void setUidReadTimeouts(StorageId storage,
std::vector<PerUidReadTimeouts>&& perUidReadTimeouts);
void clearUidReadTimeouts(StorageId storage);
- void updateUidReadTimeouts(StorageId storage, Clock::time_point timeLimit);
+ bool checkUidReadTimeouts(StorageId storage, IfsState state, Clock::time_point timeLimit);
std::unordered_set<std::string_view> adoptMountedInstances();
void mountExistingImages(const std::unordered_set<std::string_view>& mountedRootNames);
@@ -387,11 +398,6 @@
bool needStartDataLoaderLocked(IncFsMount& ifs);
- DataLoaderStubPtr prepareDataLoader(IncFsMount& ifs,
- content::pm::DataLoaderParamsParcel&& params,
- DataLoaderStatusListener&& statusListener = {},
- const StorageHealthCheckParams& healthCheckParams = {},
- StorageHealthListener&& healthListener = {});
void prepareDataLoaderLocked(IncFsMount& ifs, content::pm::DataLoaderParamsParcel&& params,
DataLoaderStatusListener&& statusListener = {},
const StorageHealthCheckParams& healthCheckParams = {},
@@ -410,8 +416,8 @@
std::string_view path) const;
int makeDirs(const IncFsMount& ifs, StorageId storageId, std::string_view path, int mode);
- int setStorageParams(IncFsMount& ifs, StorageId storageId, bool enableReadLogs);
- binder::Status applyStorageParams(IncFsMount& ifs, bool enableReadLogs);
+ int disableReadLogsLocked(IncFsMount& ifs);
+ int applyStorageParamsLocked(IncFsMount& ifs, bool enableReadLogs);
LoadingProgress getLoadingProgressFromPath(const IncFsMount& ifs, std::string_view path) const;
@@ -431,6 +437,12 @@
bool addTimedJob(TimedQueueWrapper& timedQueue, MountId id, Milliseconds after, Job what);
bool removeTimedJobs(TimedQueueWrapper& timedQueue, MountId id);
+
+ void addIfsStateCallback(StorageId storageId, IfsStateCallback callback);
+ void removeIfsStateCallbacks(StorageId storageId);
+ void processIfsStateCallbacks();
+ void processIfsStateCallbacks(StorageId storageId, std::vector<IfsStateCallback>& callbacks);
+
bool updateLoadingProgress(int32_t storageId,
StorageLoadingProgressListener&& progressListener);
long getMillsSinceOldestPendingRead(StorageId storage);
@@ -456,6 +468,10 @@
std::mutex mCallbacksLock;
std::unordered_map<std::string, sp<AppOpsListener>> mCallbackRegistered;
+ using IfsStateCallbacks = std::unordered_map<StorageId, std::vector<IfsStateCallback>>;
+ std::mutex mIfsStateCallbacksLock;
+ IfsStateCallbacks mIfsStateCallbacks;
+
std::atomic_bool mSystemReady = false;
StorageId mNextId = 0;
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index 54bc95d..de8822d 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -140,9 +140,7 @@
const content::pm::FileSystemControlParcel& control,
const sp<content::pm::IDataLoaderStatusListener>& listener) {
createOkNoStatus(id, params, control, listener);
- if (mListener) {
- mListener->onStatusChanged(id, IDataLoaderStatusListener::DATA_LOADER_CREATED);
- }
+ reportStatus(id);
return binder::Status::ok();
}
binder::Status createOkNoStatus(int32_t id, const content::pm::DataLoaderParamsParcel& params,
@@ -150,33 +148,26 @@
const sp<content::pm::IDataLoaderStatusListener>& listener) {
mServiceConnector = control.service;
mListener = listener;
+ mStatus = IDataLoaderStatusListener::DATA_LOADER_CREATED;
return binder::Status::ok();
}
binder::Status startOk(int32_t id) {
- if (mListener) {
- mListener->onStatusChanged(id, IDataLoaderStatusListener::DATA_LOADER_STARTED);
- }
+ setAndReportStatus(id, IDataLoaderStatusListener::DATA_LOADER_STARTED);
return binder::Status::ok();
}
binder::Status stopOk(int32_t id) {
- if (mListener) {
- mListener->onStatusChanged(id, IDataLoaderStatusListener::DATA_LOADER_STOPPED);
- }
+ setAndReportStatus(id, IDataLoaderStatusListener::DATA_LOADER_STOPPED);
return binder::Status::ok();
}
binder::Status destroyOk(int32_t id) {
- if (mListener) {
- mListener->onStatusChanged(id, IDataLoaderStatusListener::DATA_LOADER_DESTROYED);
- }
+ setAndReportStatus(id, IDataLoaderStatusListener::DATA_LOADER_DESTROYED);
mListener = nullptr;
return binder::Status::ok();
}
binder::Status prepareImageOk(int32_t id,
const ::std::vector<content::pm::InstallationFileParcel>&,
const ::std::vector<::std::string>&) {
- if (mListener) {
- mListener->onStatusChanged(id, IDataLoaderStatusListener::DATA_LOADER_IMAGE_READY);
- }
+ setAndReportStatus(id, IDataLoaderStatusListener::DATA_LOADER_IMAGE_READY);
return binder::Status::ok();
}
binder::Status storageError(int32_t id) {
@@ -197,10 +188,22 @@
EXPECT_TRUE(mServiceConnector->setStorageParams(enableReadLogs, &result).isOk());
return result;
}
+ int status() const { return mStatus; }
private:
+ void setAndReportStatus(int id, int status) {
+ mStatus = status;
+ reportStatus(id);
+ }
+ void reportStatus(int id) {
+ if (mListener) {
+ mListener->onStatusChanged(id, mStatus);
+ }
+ }
+
sp<IIncrementalServiceConnector> mServiceConnector;
sp<IDataLoaderStatusListener> mListener;
+ int mStatus = IDataLoaderStatusListener::DATA_LOADER_DESTROYED;
};
class MockDataLoaderManager : public DataLoaderManagerWrapper {
@@ -915,7 +918,7 @@
EXPECT_CALL(*mDataLoader, start(_)).Times(6);
EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
- EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(3);
+ EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(4);
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
@@ -1126,7 +1129,7 @@
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
EXPECT_CALL(*mLooper, addFd(MockIncFs::kPendingReadsFd, _, _, _, _)).Times(2);
EXPECT_CALL(*mLooper, removeFd(MockIncFs::kPendingReadsFd)).Times(2);
- EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(5);
+ EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(6);
sp<NiceMock<MockStorageHealthListener>> listener{new NiceMock<MockStorageHealthListener>};
NiceMock<MockStorageHealthListener>* listenerMock = listener.get();
@@ -1314,7 +1317,7 @@
EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
// Not expecting callback removal.
EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
- EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(1);
+ EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(2);
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
@@ -1355,7 +1358,7 @@
EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
// Not expecting callback removal.
EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
- EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(0);
+ EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(2);
// System data loader.
mDataLoaderParcel.packageName = "android";
TemporaryDir tempDir;
@@ -1366,9 +1369,9 @@
ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
{}, {}));
- // No readlogs callback.
- ASSERT_EQ(mTimedQueue->mAfter, 0ms);
- ASSERT_EQ(mTimedQueue->mWhat, nullptr);
+ // IfsState callback.
+ auto callback = mTimedQueue->mWhat;
+ mTimedQueue->clearJob(storageId);
ASSERT_GE(mDataLoader->setStorageParams(true), 0);
// Now advance clock for 1hr.
@@ -1376,6 +1379,8 @@
ASSERT_GE(mDataLoader->setStorageParams(true), 0);
// Now advance clock for 2hrs.
mClock->advance(readLogsMaxInterval);
+ // IfsStorage callback should not affect anything.
+ callback();
ASSERT_EQ(mDataLoader->setStorageParams(true), 0);
}
@@ -1394,7 +1399,7 @@
EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
// Not expecting callback removal.
EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
- EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(2);
+ EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(4);
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
@@ -1685,6 +1690,144 @@
mIncrementalService->registerLoadingProgressListener(storageId, listener);
}
+TEST_F(IncrementalServiceTest, testStartDataLoaderUnbindOnAllDone) {
+ mFs->hasFiles();
+
+ const auto stateUpdateInterval = 1s;
+
+ EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(1);
+ // No unbinding just yet.
+ EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(0);
+ EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1);
+ EXPECT_CALL(*mDataLoader, start(_)).Times(1);
+ EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
+ EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
+ // System data loader to get rid of readlog timeout callback.
+ mDataLoaderParcel.packageName = "android";
+ TemporaryDir tempDir;
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
+ ASSERT_GE(storageId, 0);
+ ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
+ {}, {}));
+
+ // Started.
+ ASSERT_EQ(mDataLoader->status(), IDataLoaderStatusListener::DATA_LOADER_STARTED);
+
+ // IfsState callback present.
+ ASSERT_EQ(IncrementalService::kMaxStorageId, mTimedQueue->mId);
+ ASSERT_EQ(mTimedQueue->mAfter, stateUpdateInterval);
+ auto callback = mTimedQueue->mWhat;
+ mTimedQueue->clearJob(IncrementalService::kMaxStorageId);
+
+ // Not loaded yet.
+ EXPECT_CALL(*mIncFs, isEverythingFullyLoaded(_))
+ .WillOnce(Return(incfs::LoadingState::MissingBlocks));
+
+ // Send the callback, should not do anything.
+ callback();
+
+ // Still started.
+ ASSERT_EQ(mDataLoader->status(), IDataLoaderStatusListener::DATA_LOADER_STARTED);
+
+ // Still present.
+ ASSERT_EQ(IncrementalService::kMaxStorageId, mTimedQueue->mId);
+ ASSERT_EQ(mTimedQueue->mAfter, stateUpdateInterval);
+ callback = mTimedQueue->mWhat;
+ mTimedQueue->clearJob(IncrementalService::kMaxStorageId);
+
+ // Fully loaded.
+ EXPECT_CALL(*mIncFs, isEverythingFullyLoaded(_)).WillOnce(Return(incfs::LoadingState::Full));
+ // Expect the unbind.
+ EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
+
+ callback();
+
+ // Destroyed.
+ ASSERT_EQ(mDataLoader->status(), IDataLoaderStatusListener::DATA_LOADER_DESTROYED);
+}
+
+TEST_F(IncrementalServiceTest, testStartDataLoaderUnbindOnAllDoneWithReadlogs) {
+ mFs->hasFiles();
+
+ // Readlogs.
+ mVold->setIncFsMountOptionsSuccess();
+ mAppOpsManager->checkPermissionSuccess();
+
+ const auto stateUpdateInterval = 1s;
+
+ EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(1);
+ // No unbinding just yet.
+ EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(0);
+ EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1);
+ EXPECT_CALL(*mDataLoader, start(_)).Times(1);
+ EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
+ EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
+ // System data loader to get rid of readlog timeout callback.
+ mDataLoaderParcel.packageName = "android";
+ TemporaryDir tempDir;
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
+ ASSERT_GE(storageId, 0);
+ ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
+ {}, {}));
+
+ // Started.
+ ASSERT_EQ(mDataLoader->status(), IDataLoaderStatusListener::DATA_LOADER_STARTED);
+
+ // IfsState callback present.
+ ASSERT_EQ(IncrementalService::kMaxStorageId, mTimedQueue->mId);
+ ASSERT_EQ(mTimedQueue->mAfter, stateUpdateInterval);
+ auto callback = mTimedQueue->mWhat;
+ mTimedQueue->clearJob(IncrementalService::kMaxStorageId);
+
+ // Not loaded yet.
+ EXPECT_CALL(*mIncFs, isEverythingFullyLoaded(_))
+ .WillOnce(Return(incfs::LoadingState::MissingBlocks));
+
+ // Send the callback, should not do anything.
+ callback();
+
+ // Still started.
+ ASSERT_EQ(mDataLoader->status(), IDataLoaderStatusListener::DATA_LOADER_STARTED);
+
+ // Still present.
+ ASSERT_EQ(IncrementalService::kMaxStorageId, mTimedQueue->mId);
+ ASSERT_EQ(mTimedQueue->mAfter, stateUpdateInterval);
+ callback = mTimedQueue->mWhat;
+ mTimedQueue->clearJob(IncrementalService::kMaxStorageId);
+
+ // Fully loaded.
+ EXPECT_CALL(*mIncFs, isEverythingFullyLoaded(_))
+ .WillOnce(Return(incfs::LoadingState::Full))
+ .WillOnce(Return(incfs::LoadingState::Full));
+ // But with readlogs.
+ ASSERT_GE(mDataLoader->setStorageParams(true), 0);
+
+ // Send the callback, still nothing.
+ callback();
+
+ // Still started.
+ ASSERT_EQ(mDataLoader->status(), IDataLoaderStatusListener::DATA_LOADER_STARTED);
+
+ // Still present.
+ ASSERT_EQ(IncrementalService::kMaxStorageId, mTimedQueue->mId);
+ ASSERT_EQ(mTimedQueue->mAfter, stateUpdateInterval);
+ callback = mTimedQueue->mWhat;
+ mTimedQueue->clearJob(IncrementalService::kMaxStorageId);
+
+ // Disable readlogs and expect the unbind.
+ EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
+ ASSERT_GE(mDataLoader->setStorageParams(false), 0);
+
+ callback();
+
+ // Destroyed.
+ ASSERT_EQ(mDataLoader->status(), IDataLoaderStatusListener::DATA_LOADER_DESTROYED);
+}
+
TEST_F(IncrementalServiceTest, testRegisterStorageHealthListenerSuccess) {
mIncFs->openMountSuccess();
sp<NiceMock<MockStorageHealthListener>> listener{new NiceMock<MockStorageHealthListener>};
@@ -1801,7 +1944,7 @@
EXPECT_CALL(*mDataLoader, start(_)).Times(1);
EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
EXPECT_CALL(*mIncFs, setUidReadTimeouts(_, _)).Times(0);
- EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(1);
+ EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(2);
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
TemporaryDir tempDir;
int storageId =
@@ -1829,7 +1972,6 @@
EXPECT_CALL(*mIncFs, isEverythingFullyLoaded(_))
.WillOnce(Return(incfs::LoadingState::MissingBlocks))
.WillOnce(Return(incfs::LoadingState::MissingBlocks))
- .WillOnce(Return(incfs::LoadingState::Full))
.WillOnce(Return(incfs::LoadingState::Full));
// Mark DataLoader as 'system' so that readlogs don't pollute the timed queue.
@@ -1846,10 +1988,10 @@
{
// Timed callback present -> 0 progress.
- ASSERT_EQ(storageId, mTimedQueue->mId);
+ ASSERT_EQ(IncrementalService::kMaxStorageId, mTimedQueue->mId);
ASSERT_GE(mTimedQueue->mAfter, std::chrono::seconds(1));
const auto timedCallback = mTimedQueue->mWhat;
- mTimedQueue->clearJob(storageId);
+ mTimedQueue->clearJob(IncrementalService::kMaxStorageId);
// Call it again.
timedCallback();
@@ -1857,10 +1999,10 @@
{
// Still present -> some progress.
- ASSERT_EQ(storageId, mTimedQueue->mId);
+ ASSERT_EQ(IncrementalService::kMaxStorageId, mTimedQueue->mId);
ASSERT_GE(mTimedQueue->mAfter, std::chrono::seconds(1));
const auto timedCallback = mTimedQueue->mWhat;
- mTimedQueue->clearJob(storageId);
+ mTimedQueue->clearJob(IncrementalService::kMaxStorageId);
// Fully loaded but readlogs collection enabled.
ASSERT_GE(mDataLoader->setStorageParams(true), 0);
@@ -1871,10 +2013,10 @@
{
// Still present -> fully loaded + readlogs.
- ASSERT_EQ(storageId, mTimedQueue->mId);
+ ASSERT_EQ(IncrementalService::kMaxStorageId, mTimedQueue->mId);
ASSERT_GE(mTimedQueue->mAfter, std::chrono::seconds(1));
const auto timedCallback = mTimedQueue->mWhat;
- mTimedQueue->clearJob(storageId);
+ mTimedQueue->clearJob(IncrementalService::kMaxStorageId);
// Now disable readlogs.
ASSERT_GE(mDataLoader->setStorageParams(false), 0);
diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java
index e7d0121..eab3b77 100644
--- a/services/people/java/com/android/server/people/PeopleService.java
+++ b/services/people/java/com/android/server/people/PeopleService.java
@@ -388,9 +388,11 @@
private Map<AppPredictionSessionId, SessionInfo> mSessions = new ArrayMap<>();
@Override
- public void onCreatePredictionSession(AppPredictionContext context,
+ public void onCreatePredictionSession(AppPredictionContext appPredictionContext,
AppPredictionSessionId sessionId) {
- mSessions.put(sessionId, new SessionInfo(context, mDataManager, sessionId.getUserId()));
+ mSessions.put(sessionId,
+ new SessionInfo(appPredictionContext, mDataManager, sessionId.getUserId(),
+ getContext()));
}
@Override
diff --git a/services/people/java/com/android/server/people/SessionInfo.java b/services/people/java/com/android/server/people/SessionInfo.java
index 28612f1..d256d9c 100644
--- a/services/people/java/com/android/server/people/SessionInfo.java
+++ b/services/people/java/com/android/server/people/SessionInfo.java
@@ -20,6 +20,7 @@
import android.app.prediction.AppPredictionContext;
import android.app.prediction.AppTarget;
import android.app.prediction.IPredictionCallback;
+import android.content.Context;
import android.content.pm.ParceledListSlice;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
@@ -40,9 +41,9 @@
new RemoteCallbackList<>();
SessionInfo(AppPredictionContext predictionContext, DataManager dataManager,
- @UserIdInt int callingUserId) {
+ @UserIdInt int callingUserId, Context context) {
mAppTargetPredictor = AppTargetPredictor.create(predictionContext,
- this::updatePredictions, dataManager, callingUserId);
+ this::updatePredictions, dataManager, callingUserId, context);
}
void addCallback(IPredictionCallback callback) {
diff --git a/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java b/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java
index c89dadc..e191081 100644
--- a/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java
+++ b/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java
@@ -24,6 +24,7 @@
import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetEvent;
import android.app.prediction.AppTargetId;
+import android.content.Context;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.people.data.DataManager;
@@ -43,10 +44,10 @@
/** Creates a {@link AppTargetPredictor} instance based on the prediction context. */
public static AppTargetPredictor create(@NonNull AppPredictionContext predictionContext,
@NonNull Consumer<List<AppTarget>> updatePredictionsMethod,
- @NonNull DataManager dataManager, @UserIdInt int callingUserId) {
+ @NonNull DataManager dataManager, @UserIdInt int callingUserId, Context context) {
if (UI_SURFACE_SHARE.equals(predictionContext.getUiSurface())) {
- return new ShareTargetPredictor(
- predictionContext, updatePredictionsMethod, dataManager, callingUserId);
+ return new ShareTargetPredictor(predictionContext, updatePredictionsMethod, dataManager,
+ callingUserId, context);
}
return new AppTargetPredictor(
predictionContext, updatePredictionsMethod, dataManager, callingUserId);
@@ -124,6 +125,11 @@
callback.accept(targets);
}
+ /** To be overridden by the subclass to recycle resources. */
+ @WorkerThread
+ void destroy() {
+ }
+
AppPredictionContext getPredictionContext() {
return mPredictionContext;
}
diff --git a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
index 236ac84..368b737 100644
--- a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
+++ b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
@@ -16,6 +16,8 @@
package com.android.server.people.prediction;
+import static android.provider.DeviceConfig.NAMESPACE_SYSTEMUI;
+
import static java.util.Collections.reverseOrder;
import android.annotation.NonNull;
@@ -23,17 +25,23 @@
import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
import android.app.prediction.AppPredictionContext;
+import android.app.prediction.AppPredictionManager;
+import android.app.prediction.AppPredictor;
import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetEvent;
import android.app.prediction.AppTargetId;
+import android.content.Context;
import android.content.IntentFilter;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager.ShareShortcutInfo;
+import android.os.UserHandle;
+import android.provider.DeviceConfig;
import android.util.Log;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ChooserActivity;
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.server.people.data.ConversationInfo;
import com.android.server.people.data.DataManager;
import com.android.server.people.data.EventHistory;
@@ -52,14 +60,27 @@
private static final String TAG = "ShareTargetPredictor";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final String REMOTE_APP_PREDICTOR_KEY = "remote_app_predictor";
private final IntentFilter mIntentFilter;
+ private final AppPredictor mRemoteAppPredictor;
ShareTargetPredictor(@NonNull AppPredictionContext predictionContext,
@NonNull Consumer<List<AppTarget>> updatePredictionsMethod,
- @NonNull DataManager dataManager, @UserIdInt int callingUserId) {
+ @NonNull DataManager dataManager,
+ @UserIdInt int callingUserId, @NonNull Context context) {
super(predictionContext, updatePredictionsMethod, dataManager, callingUserId);
mIntentFilter = predictionContext.getExtras().getParcelable(
ChooserActivity.APP_PREDICTION_INTENT_FILTER_KEY);
+ if (DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.DARK_LAUNCH_REMOTE_PREDICTION_SERVICE_ENABLED,
+ false)) {
+ predictionContext.getExtras().putBoolean(REMOTE_APP_PREDICTOR_KEY, true);
+ mRemoteAppPredictor = context.createContextAsUser(UserHandle.of(callingUserId), 0)
+ .getSystemService(AppPredictionManager.class)
+ .createAppPredictionSession(predictionContext);
+ } else {
+ mRemoteAppPredictor = null;
+ }
}
/** Reports chosen history of direct/app share targets. */
@@ -72,6 +93,9 @@
if (mIntentFilter != null) {
getDataManager().reportShareTargetEvent(event, mIntentFilter);
}
+ if (mRemoteAppPredictor != null) {
+ mRemoteAppPredictor.notifyAppTargetEvent(event);
+ }
}
/** Provides prediction on direct share targets */
@@ -129,6 +153,15 @@
callback.accept(appTargetList);
}
+ /** Recycles resources. */
+ @WorkerThread
+ @Override
+ void destroy() {
+ if (mRemoteAppPredictor != null) {
+ mRemoteAppPredictor.destroy();
+ }
+ }
+
private List<ShareTarget> getDirectShareTargets() {
List<ShareTarget> shareTargets = new ArrayList<>();
List<ShareShortcutInfo> shareShortcuts =
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
index d5eda20..dce853a 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt
@@ -41,7 +41,12 @@
}
private val platformCompat: PlatformCompat = mockThrowOnUnmocked {
- whenever(isChangeEnabled(eq(DomainVerificationCollector.RESTRICT_DOMAINS), any())) {
+ whenever(
+ isChangeEnabledInternalNoLogging(
+ eq(DomainVerificationCollector.RESTRICT_DOMAINS),
+ any()
+ )
+ ) {
(arguments[1] as ApplicationInfo).targetSdkVersion >= Build.VERSION_CODES.S
}
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
index 7e25901..1b0a305 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
@@ -114,12 +114,7 @@
it,
mockThrowOnUnmocked { whenever(linkedApps) { ArraySet<String>() } },
mockThrowOnUnmocked {
- whenever(
- isChangeEnabled(
- anyLong(),
- any()
- )
- ) { true }
+ whenever(isChangeEnabledInternalNoLogging(anyLong(), any())) { true }
}).apply {
setConnection(connection)
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
index 0e74b65..ef79b08 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
@@ -310,7 +310,7 @@
}, mockThrowOnUnmocked {
whenever(linkedApps) { ArraySet<String>() }
}, mockThrowOnUnmocked {
- whenever(isChangeEnabled(anyLong(), any())) { true }
+ whenever(isChangeEnabledInternalNoLogging(anyLong(), any())) { true }
}).apply {
setConnection(mockThrowOnUnmocked {
whenever(filterAppAccess(anyString(), anyInt(), anyInt())) { false }
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
index fe3672d..0ce16e6 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
@@ -43,6 +43,7 @@
import org.mockito.ArgumentMatchers
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyLong
import org.mockito.ArgumentMatchers.anyString
import java.util.UUID
@@ -366,7 +367,7 @@
}, mockThrowOnUnmocked {
whenever(linkedApps) { ArraySet<String>() }
}, mockThrowOnUnmocked {
- whenever(isChangeEnabled(ArgumentMatchers.anyLong(), any())) { true }
+ whenever(isChangeEnabledInternalNoLogging(anyLong(), any())) { true }
}).apply {
setConnection(mockThrowOnUnmocked {
whenever(filterAppAccess(anyString(), anyInt(), anyInt())) { false }
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
index 377bae1..b7c6922 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
@@ -98,7 +98,7 @@
context,
mockThrowOnUnmocked { whenever(linkedApps) { ArraySet<String>() } },
mockThrowOnUnmocked {
- whenever(isChangeEnabled(anyLong(),any())) { true }
+ whenever(isChangeEnabledInternalNoLogging(anyLong(), any())) { true }
}).apply {
setConnection(connection)
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
index 44c1b8f..54648ab 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
@@ -71,7 +71,7 @@
}, mockThrowOnUnmocked {
whenever(linkedApps) { ArraySet<String>() }
}, mockThrowOnUnmocked {
- whenever(isChangeEnabled(anyLong(), any())) { true }
+ whenever(isChangeEnabledInternalNoLogging(anyLong(), any())) { true }
}).apply {
setConnection(mockThrowOnUnmocked {
whenever(filterAppAccess(anyString(), anyInt(), anyInt())) { false }
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/apphibernation/AppHibernationServiceTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
index 1c96838..1b8ab21 100644
--- a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
@@ -18,6 +18,7 @@
import static android.content.pm.PackageManager.MATCH_ANY_USER;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.AdditionalAnswers.returnsArgAt;
import static org.mockito.ArgumentMatchers.any;
@@ -67,6 +68,7 @@
private static final String PACKAGE_SCHEME = "package";
private static final String PACKAGE_NAME_1 = "package1";
private static final String PACKAGE_NAME_2 = "package2";
+ private static final String PACKAGE_NAME_3 = "package3";
private static final int USER_ID_1 = 1;
private static final int USER_ID_2 = 2;
@@ -107,6 +109,8 @@
List<PackageInfo> packages = new ArrayList<>();
packages.add(makePackageInfo(PACKAGE_NAME_1));
+ packages.add(makePackageInfo(PACKAGE_NAME_2));
+ packages.add(makePackageInfo(PACKAGE_NAME_3));
doReturn(new ParceledListSlice<>(packages)).when(mIPackageManager).getInstalledPackages(
intThat(arg -> (arg & MATCH_ANY_USER) != 0), anyInt());
mAppHibernationService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
@@ -179,6 +183,26 @@
assertTrue(mAppHibernationService.isHibernatingGlobally(PACKAGE_NAME_1));
}
+ @Test
+ public void testGetHibernatingPackagesForUser_returnsCorrectPackages() throws RemoteException {
+ // GIVEN an unlocked user with all packages installed
+ UserInfo userInfo =
+ addUser(USER_ID_2, new String[]{PACKAGE_NAME_1, PACKAGE_NAME_2, PACKAGE_NAME_3});
+ mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(userInfo));
+ doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_2);
+
+ // WHEN packages are hibernated for the user
+ mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_2, true);
+ mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_2, USER_ID_2, true);
+
+ // THEN the hibernating packages returned matches
+ List<String> hibernatingPackages =
+ mAppHibernationService.getHibernatingPackagesForUser(USER_ID_2);
+ assertEquals(2, hibernatingPackages.size());
+ assertTrue(hibernatingPackages.contains(PACKAGE_NAME_1));
+ assertTrue(hibernatingPackages.contains(PACKAGE_NAME_2));
+ }
+
/**
* Add a mock user with one package.
*/
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/people/PeopleServiceTest.java b/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java
index ecff409..5066240 100644
--- a/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java
@@ -47,12 +47,14 @@
import android.os.Bundle;
import android.os.RemoteException;
import android.os.test.TestLooper;
+import android.provider.DeviceConfig;
import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
import android.testing.TestableLooper;
import androidx.test.InstrumentationRegistry;
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.server.LocalServices;
import org.junit.After;
@@ -126,6 +128,10 @@
.setPredictedTargetCount(APP_PREDICTION_TARGET_COUNT)
.setExtras(new Bundle())
.build();
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.DARK_LAUNCH_REMOTE_PREDICTION_SERVICE_ENABLED,
+ Boolean.toString(false),
+ true /* makeDefault*/);
}
@After
diff --git a/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java b/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
index b09a3c3..fac5c1f 100644
--- a/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
@@ -22,13 +22,17 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anySet;
+import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.prediction.AppPredictionContext;
+import android.app.prediction.AppPredictionManager;
import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetEvent;
import android.app.prediction.AppTargetId;
import android.content.ComponentName;
import android.content.Context;
@@ -38,9 +42,11 @@
import android.content.pm.ShortcutManager.ShareShortcutInfo;
import android.os.Bundle;
import android.os.UserHandle;
+import android.provider.DeviceConfig;
import android.util.Range;
import com.android.internal.app.ChooserActivity;
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.server.people.data.ConversationInfo;
import com.android.server.people.data.DataManager;
import com.android.server.people.data.EventHistory;
@@ -71,6 +77,14 @@
private static final String PACKAGE_3 = "pkg3";
private static final String CLASS_1 = "cls1";
private static final String CLASS_2 = "cls2";
+ private static final AppTargetEvent APP_TARGET_EVENT =
+ new AppTargetEvent.Builder(
+ new AppTarget.Builder(
+ new AppTargetId("cls1#pkg1"), PACKAGE_1, UserHandle.of(USER_ID)).build(),
+ AppTargetEvent.ACTION_LAUNCH)
+ .setLaunchLocation(ChooserActivity.LAUNCH_LOCATION_DIRECT_SHARE)
+ .build();
+ private static final IntentFilter INTENT_FILTER = IntentFilter.create("SEND", "text/plain");
@Mock private Context mContext;
@Mock private DataManager mDataManager;
@@ -102,17 +116,33 @@
when(mDataManager.getShareShortcuts(any(), anyInt())).thenReturn(mShareShortcuts);
when(mDataManager.getPackage(PACKAGE_1, USER_ID)).thenReturn(mPackageData1);
when(mDataManager.getPackage(PACKAGE_2, USER_ID)).thenReturn(mPackageData2);
+ when(mContext.createContextAsUser(any(), any())).thenReturn(mContext);
+ when(mContext.getSystemServiceName(AppPredictionManager.class)).thenReturn(
+ Context.APP_PREDICTION_SERVICE);
+ when(mContext.getSystemService(AppPredictionManager.class))
+ .thenReturn(new AppPredictionManager(mContext));
Bundle bundle = new Bundle();
- bundle.putObject(ChooserActivity.APP_PREDICTION_INTENT_FILTER_KEY,
- IntentFilter.create("SEND", "text/plain"));
+ bundle.putObject(ChooserActivity.APP_PREDICTION_INTENT_FILTER_KEY, INTENT_FILTER);
AppPredictionContext predictionContext = new AppPredictionContext.Builder(mContext)
.setUiSurface(UI_SURFACE_SHARE)
.setPredictedTargetCount(NUM_PREDICTED_TARGETS)
.setExtras(bundle)
.build();
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.DARK_LAUNCH_REMOTE_PREDICTION_SERVICE_ENABLED,
+ Boolean.toString(true),
+ true /* makeDefault*/);
mPredictor = new ShareTargetPredictor(
- predictionContext, mUpdatePredictionsMethod, mDataManager, USER_ID);
+ predictionContext, mUpdatePredictionsMethod, mDataManager, USER_ID, mContext);
+ }
+
+ @Test
+ public void testReportAppTargetEvent() {
+ mPredictor.reportAppTargetEvent(APP_TARGET_EVENT);
+
+ verify(mDataManager, times(1))
+ .reportShareTargetEvent(eq(APP_TARGET_EVENT), eq(INTENT_FILTER));
}
@Test
@@ -240,7 +270,7 @@
.setExtras(new Bundle())
.build();
mPredictor = new ShareTargetPredictor(
- predictionContext, mUpdatePredictionsMethod, mDataManager, USER_ID);
+ predictionContext, mUpdatePredictionsMethod, mDataManager, USER_ID, mContext);
mPredictor.predictTargets();
@@ -349,7 +379,7 @@
.setExtras(new Bundle())
.build();
mPredictor = new ShareTargetPredictor(
- predictionContext, mUpdatePredictionsMethod, mDataManager, USER_ID);
+ predictionContext, mUpdatePredictionsMethod, mDataManager, USER_ID, mContext);
AppTarget appTarget1 = new AppTarget.Builder(
new AppTargetId("cls1#pkg1"), PACKAGE_1, UserHandle.of(USER_ID))
.build();
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/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index 07475e9..be6e801 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -1111,7 +1111,7 @@
when(service.asBinder()).thenReturn(mock(IBinder.class));
ManagedServices services = new TestManagedServices(getContext(), mLock, mUserProfiles,
mIpm, APPROVAL_BY_PACKAGE);
- services.registerSystemService(service, null, 10);
+ services.registerSystemService(service, null, 10, 1000);
ManagedServices.ManagedServiceInfo info = services.checkServiceTokenLocked(service);
info.isSystem = true;
@@ -1163,10 +1163,10 @@
ManagedServices.ManagedServiceInfo service0 = service.new ManagedServiceInfo(
iInterface, ComponentName.unflattenFromString("a/a"), 0, false,
- mock(ServiceConnection.class), 26);
+ mock(ServiceConnection.class), 26, 34);
ManagedServices.ManagedServiceInfo service10 = service.new ManagedServiceInfo(
iInterface, ComponentName.unflattenFromString("b/b"), 10, false,
- mock(ServiceConnection.class), 26);
+ mock(ServiceConnection.class), 26, 345);
Set<ManagedServices.ManagedServiceInfo> removableBoundServices = new ArraySet<>();
removableBoundServices.add(service0);
removableBoundServices.add(service10);
@@ -1199,13 +1199,13 @@
ManagedServices.ManagedServiceInfo service0 = service.new ManagedServiceInfo(
iInterface, ComponentName.unflattenFromString("a/a"), 0, false,
- mock(ServiceConnection.class), 26);
+ mock(ServiceConnection.class), 26, 345);
ManagedServices.ManagedServiceInfo service0a = service.new ManagedServiceInfo(
iInterface, ComponentName.unflattenFromString("c/c"), 0, false,
- mock(ServiceConnection.class), 26);
+ mock(ServiceConnection.class), 26, 3456);
ManagedServices.ManagedServiceInfo service10 = service.new ManagedServiceInfo(
iInterface, ComponentName.unflattenFromString("b/b"), 10, false,
- mock(ServiceConnection.class), 26);
+ mock(ServiceConnection.class), 26, 34567);
Set<ManagedServices.ManagedServiceInfo> removableBoundServices = new ArraySet<>();
removableBoundServices.add(service0);
removableBoundServices.add(service0a);
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/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index ec3a1af..aa9feeaa 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -469,7 +469,7 @@
when(mListeners.getNotificationListenerFilter(any())).thenReturn(mNlf);
mListener = mListeners.new ManagedServiceInfo(
null, new ComponentName(PKG, "test_class"),
- UserHandle.getUserId(mUid), true, null, 0);
+ UserHandle.getUserId(mUid), true, null, 0, 123);
ComponentName defaultComponent = ComponentName.unflattenFromString("config/device");
ArraySet<ComponentName> components = new ArraySet<>();
components.add(defaultComponent);
@@ -2869,7 +2869,7 @@
snoozeNotificationRunnable.run();
ManagedServices.ManagedServiceInfo listener = mListeners.new ManagedServiceInfo(
- null, new ComponentName(PKG, "test_class"), mUid, true, null, 0);
+ null, new ComponentName(PKG, "test_class"), mUid, true, null, 0, 234);
listener.isSystem = true;
when(mListeners.checkServiceTokenLocked(any())).thenReturn(listener);
@@ -4910,6 +4910,7 @@
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
setToastRateIsWithinQuota(true);
+ setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
// package is not suspended
when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -4933,6 +4934,7 @@
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
setToastRateIsWithinQuota(true);
+ setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
// package is not suspended
when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -4952,6 +4954,7 @@
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
setToastRateIsWithinQuota(true);
+ setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
// package is not suspended
when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -4974,11 +4977,12 @@
}
@Test
- public void testToastRateLimiterCanPreventsShowCallForCustomToast() throws Exception {
+ public void testToastRateLimiterCanPreventShowCallForCustomToast() throws Exception {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
setToastRateIsWithinQuota(false); // rate limit reached
+ setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
// package is not suspended
when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -4995,12 +4999,36 @@
}
@Test
+ public void testCustomToastRateLimiterAllowsLimitAvoidanceWithPermission() throws Exception {
+ final String testPackage = "testPackageName";
+ assertEquals(0, mService.mToastQueue.size());
+ mService.isSystemUid = false;
+ setToastRateIsWithinQuota(false); // rate limit reached
+ // Avoids rate limiting.
+ setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, true);
+
+ // package is not suspended
+ when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
+ .thenReturn(false);
+
+ setAppInForegroundForToasts(mUid, true);
+
+ Binder token = new Binder();
+ ITransientNotification callback = mock(ITransientNotification.class);
+ INotificationManager nmService = (INotificationManager) mService.mService;
+
+ nmService.enqueueToast(testPackage, token, callback, 2000, 0);
+ verify(callback).show(any());
+ }
+
+ @Test
public void testCustomToastPostedWhileInForeground_blockedIfAppGoesToBackground()
throws Exception {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
setToastRateIsWithinQuota(true);
+ setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
// package is not suspended
when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5034,6 +5062,7 @@
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
setToastRateIsWithinQuota(true);
+ setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
// package is not suspended
when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5053,6 +5082,7 @@
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
setToastRateIsWithinQuota(true);
+ setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
// package is not suspended
when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5072,6 +5102,7 @@
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
setToastRateIsWithinQuota(true);
+ setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
// package is not suspended
when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5095,11 +5126,12 @@
}
@Test
- public void testToastRateLimiterCanPreventsShowCallForTextToast() throws Exception {
+ public void testToastRateLimiterCanPreventShowCallForTextToast() throws Exception {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
setToastRateIsWithinQuota(false); // rate limit reached
+ setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
// package is not suspended
when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5114,12 +5146,32 @@
}
@Test
+ public void testTextToastRateLimiterAllowsLimitAvoidanceWithPermission() throws Exception {
+ final String testPackage = "testPackageName";
+ assertEquals(0, mService.mToastQueue.size());
+ mService.isSystemUid = false;
+ setToastRateIsWithinQuota(false); // rate limit reached
+ setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, true);
+
+ // package is not suspended
+ when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
+ .thenReturn(false);
+
+ Binder token = new Binder();
+ INotificationManager nmService = (INotificationManager) mService.mService;
+
+ nmService.enqueueTextToast(testPackage, token, "Text", 2000, 0, null);
+ verify(mStatusBar).showToast(anyInt(), any(), any(), any(), any(), anyInt(), any());
+ }
+
+ @Test
public void backgroundSystemCustomToast_callsSetProcessImportantAsForegroundForToast() throws
Exception {
final String testPackage = "testPackageName";
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = true;
setToastRateIsWithinQuota(true);
+ setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
// package is not suspended
when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5145,6 +5197,7 @@
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
setToastRateIsWithinQuota(true);
+ setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
// package is not suspended
when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5166,6 +5219,7 @@
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
setToastRateIsWithinQuota(true);
+ setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
// package is not suspended
when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5186,6 +5240,7 @@
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
setToastRateIsWithinQuota(true);
+ setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
// package is not suspended
when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5203,6 +5258,7 @@
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
setToastRateIsWithinQuota(true);
+ setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
// package is suspended
when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5224,6 +5280,7 @@
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
setToastRateIsWithinQuota(true);
+ setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
// package is not suspended
when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5247,6 +5304,7 @@
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = true;
setToastRateIsWithinQuota(true);
+ setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
// package is suspended
when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5270,6 +5328,7 @@
assertEquals(0, mService.mToastQueue.size());
mService.isSystemUid = false;
setToastRateIsWithinQuota(true);
+ setIfPackageHasPermissionToAvoidToastRateLimiting(testPackage, false);
// package is not suspended
when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
@@ -5305,6 +5364,13 @@
.thenReturn(isWithinQuota);
}
+ private void setIfPackageHasPermissionToAvoidToastRateLimiting(
+ String pkg, boolean hasPermission) throws Exception {
+ when(mPackageManager.checkPermission(android.Manifest.permission.UNLIMITED_TOASTS,
+ pkg, UserHandle.getUserId(mUid)))
+ .thenReturn(hasPermission ? PERMISSION_GRANTED : PERMISSION_DENIED);
+ }
+
@Test
public void testOnPanelRevealedAndHidden() {
int items = 5;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
index 3b15376..1116204 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
@@ -316,9 +316,10 @@
mScheduleCalendar.setSchedule(mScheduleInfo);
mScheduleInfo.nextAlarm = 2000;
+ // next alarm updated to 3000 (alarm for 2000 was changed to 3000)
mScheduleCalendar.maybeSetNextAlarm(1000, 3000);
- assertEquals(2000, mScheduleInfo.nextAlarm);
+ assertEquals(3000, mScheduleInfo.nextAlarm);
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleConditionProviderTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleConditionProviderTest.java
index 5a527a2..7446e9e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleConditionProviderTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleConditionProviderTest.java
@@ -279,17 +279,17 @@
conditionId, cal, now.getTimeInMillis(), now.getTimeInMillis() + 500);
assertEquals(Condition.STATE_TRUE, condition.state);
- // in schedule, update with later alarm time, should be in dnd
+ // in schedule, update with nextAlarm = later alarm time (1000), should be in dnd
condition = mService.evaluateSubscriptionLocked(
conditionId, cal, now.getTimeInMillis() + 250, now.getTimeInMillis() + 1000);
assertEquals(Condition.STATE_TRUE, condition.state);
- // at earliest alarm fire time, should exit dnd
- assertTrue(cal.isInSchedule(now.getTimeInMillis() + 500));
+ // at next alarm fire time (1000), should exit dnd
+ assertTrue(cal.isInSchedule(now.getTimeInMillis() + 1000));
assertTrue("" + info.nextAlarm + " " + now.getTimeInMillis(),
- cal.shouldExitForAlarm(now.getTimeInMillis() + 500));
+ cal.shouldExitForAlarm(now.getTimeInMillis() + 1000));
condition = mService.evaluateSubscriptionLocked(
- conditionId, cal, now.getTimeInMillis() + 500, 0);
+ conditionId, cal, now.getTimeInMillis() + 1000, 0);
assertEquals(Condition.STATE_FALSE, condition.state);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 002859e..7c9ff79 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -38,6 +38,7 @@
import android.app.ActivityOptions.SourceInfo;
import android.app.WaitResult;
import android.content.Intent;
+import android.os.IBinder;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
import android.util.ArrayMap;
@@ -402,6 +403,26 @@
}
@Test
+ public void testConsecutiveLaunchNewTask() {
+ final IBinder launchCookie = mock(IBinder.class);
+ mTrampolineActivity.noDisplay = true;
+ mTrampolineActivity.mLaunchCookie = launchCookie;
+ onActivityLaunched(mTrampolineActivity);
+ final ActivityRecord activityOnNewTask = new ActivityBuilder(mAtm)
+ .setCreateTask(true)
+ .build();
+ mActivityMetricsLogger.notifyActivityLaunching(activityOnNewTask.intent,
+ mTrampolineActivity /* caller */);
+ notifyActivityLaunched(START_SUCCESS, activityOnNewTask);
+
+ transitToDrawnAndVerifyOnLaunchFinished(activityOnNewTask);
+ assertWithMessage("Trampoline's cookie must be transferred").that(
+ mTrampolineActivity.mLaunchCookie).isNull();
+ assertWithMessage("The last launch task has the transferred cookie").that(
+ activityOnNewTask.mLaunchCookie).isEqualTo(launchCookie);
+ }
+
+ @Test
public void testConsecutiveLaunchOnDifferentDisplay() {
onActivityLaunched(mTopActivity);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 32e5200..adf8fa4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -46,6 +46,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
@@ -1267,6 +1268,42 @@
is(Configuration.ORIENTATION_PORTRAIT));
}
+ @Test
+ public void testHybridRotationAnimation() {
+ final DisplayContent displayContent = mDefaultDisplay;
+ final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar");
+ final WindowState navBar = createWindow(null, TYPE_NAVIGATION_BAR, "navBar");
+ final WindowState app = createWindow(null, TYPE_BASE_APPLICATION, "app");
+ final WindowState[] windows = { statusBar, navBar, app };
+ makeWindowVisible(windows);
+ final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
+ displayPolicy.addWindowLw(statusBar, statusBar.mAttrs);
+ displayPolicy.addWindowLw(navBar, navBar.mAttrs);
+ final ScreenRotationAnimation rotationAnim = new ScreenRotationAnimation(displayContent,
+ displayContent.getRotation());
+ spyOn(rotationAnim);
+ // Assume that the display rotation is changed so it is frozen in preparation for animation.
+ doReturn(true).when(rotationAnim).hasScreenshot();
+ mWm.mDisplayFrozen = true;
+ displayContent.setRotationAnimation(rotationAnim);
+ // The fade rotation animation also starts to hide some non-app windows.
+ assertNotNull(displayContent.getFadeRotationAnimationController());
+ assertTrue(statusBar.isAnimating(PARENTS, ANIMATION_TYPE_FIXED_TRANSFORM));
+
+ for (WindowState w : windows) {
+ w.setOrientationChanging(true);
+ }
+ // The display only waits for the app window to unfreeze.
+ assertFalse(displayContent.waitForUnfreeze(statusBar));
+ assertFalse(displayContent.waitForUnfreeze(navBar));
+ assertTrue(displayContent.waitForUnfreeze(app));
+ // If all windows animated by fade rotation animation have done the orientation change,
+ // the animation controller should be cleared.
+ statusBar.setOrientationChanging(false);
+ navBar.setOrientationChanging(false);
+ assertNull(displayContent.getFadeRotationAnimationController());
+ }
+
@UseTestDisplay(addWindows = { W_ACTIVITY, W_WALLPAPER, W_STATUS_BAR, W_NAVIGATION_BAR })
@Test
public void testApplyTopFixedRotationTransform() {
@@ -1275,6 +1312,7 @@
doReturn(false).when(displayPolicy).navigationBarCanMove();
displayPolicy.addWindowLw(mStatusBarWindow, mStatusBarWindow.mAttrs);
displayPolicy.addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs);
+ makeWindowVisible(mStatusBarWindow, mNavBarWindow);
final Configuration config90 = new Configuration();
mDisplayContent.computeScreenConfiguration(config90, ROTATION_90);
@@ -1297,7 +1335,7 @@
ROTATION_0 /* oldRotation */, ROTATION_90 /* newRotation */,
false /* forceUpdate */));
- assertNotNull(mDisplayContent.getFixedRotationAnimationController());
+ assertNotNull(mDisplayContent.getFadeRotationAnimationController());
assertTrue(mStatusBarWindow.getParent().isAnimating(WindowContainer.AnimationFlags.PARENTS,
ANIMATION_TYPE_FIXED_TRANSFORM));
assertTrue(mNavBarWindow.getParent().isAnimating(WindowContainer.AnimationFlags.PARENTS,
@@ -1381,7 +1419,7 @@
assertFalse(app.hasFixedRotationTransform());
assertFalse(app2.hasFixedRotationTransform());
assertEquals(config90.orientation, mDisplayContent.getConfiguration().orientation);
- assertNull(mDisplayContent.getFixedRotationAnimationController());
+ assertNull(mDisplayContent.getFadeRotationAnimationController());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 7a4ad74..f97e794 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -556,11 +556,11 @@
}
@Test
- public void testNotAttachNavigationBar_controlledByFixedRotationAnimation() {
+ public void testNotAttachNavigationBar_controlledByFadeRotationAnimation() {
setupForShouldAttachNavBarDuringTransition();
- FixedRotationAnimationController mockController =
- mock(FixedRotationAnimationController.class);
- doReturn(mockController).when(mDefaultDisplay).getFixedRotationAnimationController();
+ FadeRotationAnimationController mockController =
+ mock(FadeRotationAnimationController.class);
+ doReturn(mockController).when(mDefaultDisplay).getFadeRotationAnimationController();
final ActivityRecord homeActivity = createHomeActivity();
initializeRecentsAnimationController(mController, homeActivity);
assertFalse(mController.isNavigationBarAttachedToApp());
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 956c277..37da529 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -578,10 +578,10 @@
}
@Test
- public void testNonAppTarget_notSendNavBar_controlledByFixedRotation() throws Exception {
- final FixedRotationAnimationController mockController =
- mock(FixedRotationAnimationController.class);
- doReturn(mockController).when(mDisplayContent).getFixedRotationAnimationController();
+ public void testNonAppTarget_notSendNavBar_controlledByFadeRotation() throws Exception {
+ final FadeRotationAnimationController mockController =
+ mock(FadeRotationAnimationController.class);
+ doReturn(mockController).when(mDisplayContent).getFadeRotationAnimationController();
final int transit = TRANSIT_OLD_TASK_OPEN;
setupForNonAppTargetNavBar(transit, true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
index e2585e5..16e0d90 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.view.InsetsState.ITYPE_IME;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
@@ -250,4 +251,29 @@
verify(selectFunc).apply(token2.windowType, options);
}
+
+ /**
+ * Test that {@link WindowToken#setInsetsFrozen(boolean)} will set the frozen insets
+ * states for its children windows and by default it shouldn't let IME window setting
+ * the frozen insets state even the window of the window token is the IME layering target.
+ */
+ @UseTestDisplay(addWindows = W_INPUT_METHOD)
+ @Test
+ public void testSetInsetsFrozen_notAffectImeWindowState() {
+ // Pre-condition: make the IME window be controlled by IME insets provider.
+ mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_IME).setWindow(
+ mDisplayContent.mInputMethodWindow, null, null);
+
+ // Simulate an app window to be the IME layering target, assume the app window has no
+ // frozen insets state by default.
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ mDisplayContent.setImeLayeringTarget(app);
+ assertNull(app.getFrozenInsetsState());
+ assertTrue(app.isImeLayeringTarget());
+
+ // Verify invoking setInsetsFrozen shouldn't let IME window setting the frozen insets state.
+ app.mToken.setInsetsFrozen(true);
+ assertNotNull(app.getFrozenInsetsState());
+ assertNull(mDisplayContent.mInputMethodWindow.getFrozenInsetsState());
+ }
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 05573f1..df3ca99 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -27,6 +27,7 @@
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
+import android.app.AppGlobals;
import android.app.IActivityManager;
import android.app.IActivityTaskManager;
import android.content.BroadcastReceiver;
@@ -35,7 +36,9 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
import android.os.Bundle;
import android.os.Handler;
@@ -167,12 +170,8 @@
mSessionComponentName = new ComponentName(service.getPackageName(),
mInfo.getSessionService());
final String hotwordDetectionServiceName = mInfo.getHotwordDetectionService();
- if (hotwordDetectionServiceName != null) {
- mHotwordDetectionComponentName = new ComponentName(service.getPackageName(),
- hotwordDetectionServiceName);
- } else {
- mHotwordDetectionComponentName = null;
- }
+ mHotwordDetectionComponentName = hotwordDetectionServiceName != null
+ ? new ComponentName(service.getPackageName(), hotwordDetectionServiceName) : null;
mIWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService(Context.WINDOW_SERVICE));
IntentFilter filter = new IntentFilter();
@@ -456,8 +455,22 @@
}
boolean isIsolatedProcessLocked(ComponentName componentName) {
- // TODO : Need to make sure this component exists and is :isolated.
- return true;
+ IPackageManager pm = AppGlobals.getPackageManager();
+ try {
+ ServiceInfo serviceInfo = pm.getServiceInfo(componentName,
+ PackageManager.GET_META_DATA
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, mUser);
+ if (serviceInfo != null) {
+ return (serviceInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0
+ && (serviceInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) == 0;
+ }
+ } catch (RemoteException e) {
+ if (DEBUG) {
+ Slog.w(TAG, "isIsolatedProcess RemoteException : " + e);
+ }
+ }
+ return false;
}
public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -476,6 +489,7 @@
pw.println(" Service info:");
mInfo.getServiceInfo().dump(new PrintWriterPrinter(pw), " ");
pw.print(" Recognition service="); pw.println(mInfo.getRecognitionService());
+ pw.print(" Hotword detection service="); pw.println(mInfo.getHotwordDetectionService());
pw.print(" Settings activity="); pw.println(mInfo.getSettingsActivity());
pw.print(" Supports assist="); pw.println(mInfo.getSupportsAssist());
pw.print(" Supports launch from keyguard=");
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 2a5ddfd..4e64838 100755
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -138,6 +138,27 @@
public static final int STATE_SIMULATED_RINGING = 13;
/**
+ * @hide
+ */
+ @IntDef(prefix = { "STATE_" },
+ value = {
+ STATE_NEW,
+ STATE_DIALING,
+ STATE_RINGING,
+ STATE_HOLDING,
+ STATE_ACTIVE,
+ STATE_DISCONNECTED,
+ STATE_SELECT_PHONE_ACCOUNT,
+ STATE_CONNECTING,
+ STATE_DISCONNECTING,
+ STATE_PULLING_CALL,
+ STATE_AUDIO_PROCESSING,
+ STATE_SIMULATED_RINGING
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CallState {};
+
+ /**
* The key to retrieve the optional {@code PhoneAccount}s Telecom can bundle with its Call
* extras. Used to pass the phone accounts to display on the front end to the user in order to
* select phone accounts to (for example) place a call.
@@ -674,6 +695,7 @@
// Next PROPERTY value: 0x00004000
//******************************************************************************************
+ private final @CallState int mState;
private final String mTelecomCallId;
private final Uri mHandle;
private final int mHandlePresentation;
@@ -868,6 +890,13 @@
return builder.toString();
}
+ /**
+ * @return the state of the {@link Call} represented by this {@link Call.Details}.
+ */
+ public final @CallState int getState() {
+ return mState;
+ }
+
/** {@hide} */
@TestApi
public String getTelecomCallId() {
@@ -1069,6 +1098,7 @@
if (o instanceof Details) {
Details d = (Details) o;
return
+ Objects.equals(mState, d.mState) &&
Objects.equals(mHandle, d.mHandle) &&
Objects.equals(mHandlePresentation, d.mHandlePresentation) &&
Objects.equals(mCallerDisplayName, d.mCallerDisplayName) &&
@@ -1095,7 +1125,8 @@
@Override
public int hashCode() {
- return Objects.hash(mHandle,
+ return Objects.hash(mState,
+ mHandle,
mHandlePresentation,
mCallerDisplayName,
mCallerDisplayNamePresentation,
@@ -1117,6 +1148,7 @@
/** {@hide} */
public Details(
+ @CallState int state,
String telecomCallId,
Uri handle,
int handlePresentation,
@@ -1136,6 +1168,7 @@
String contactDisplayName,
int callDirection,
int callerNumberVerificationStatus) {
+ mState = state;
mTelecomCallId = telecomCallId;
mHandle = handle;
mHandlePresentation = handlePresentation;
@@ -1160,6 +1193,7 @@
/** {@hide} */
public static Details createFromParcelableCall(ParcelableCall parcelableCall) {
return new Details(
+ parcelableCall.getState(),
parcelableCall.getId(),
parcelableCall.getHandle(),
parcelableCall.getHandlePresentation(),
@@ -1186,6 +1220,8 @@
StringBuilder sb = new StringBuilder();
sb.append("[id: ");
sb.append(mTelecomCallId);
+ sb.append(", state: ");
+ sb.append(Call.stateToString(mState));
sb.append(", pa: ");
sb.append(mAccountHandle);
sb.append(", hdl: ");
@@ -1302,7 +1338,7 @@
* @param call The {@code Call} invoking this method.
* @param state The new state of the {@code Call}.
*/
- public void onStateChanged(Call call, int state) {}
+ public void onStateChanged(Call call, @CallState int state) {}
/**
* Invoked when the parent of this {@code Call} has changed. See {@link #getParent()}.
@@ -2171,9 +2207,11 @@
/**
* Obtains the state of this {@code Call}.
*
- * @return A state value, chosen from the {@code STATE_*} constants.
+ * @return The call state.
+ * @deprecated The call state is available via {@link Call.Details#getState()}.
*/
- public int getState() {
+ @Deprecated
+ public @CallState int getState() {
return mState;
}
@@ -2551,6 +2589,30 @@
final void internalSetDisconnected() {
if (mState != Call.STATE_DISCONNECTED) {
mState = Call.STATE_DISCONNECTED;
+ if (mDetails != null) {
+ mDetails = new Details(mState,
+ mDetails.getTelecomCallId(),
+ mDetails.getHandle(),
+ mDetails.getHandlePresentation(),
+ mDetails.getCallerDisplayName(),
+ mDetails.getCallerDisplayNamePresentation(),
+ mDetails.getAccountHandle(),
+ mDetails.getCallCapabilities(),
+ mDetails.getCallProperties(),
+ mDetails.getDisconnectCause(),
+ mDetails.getConnectTimeMillis(),
+ mDetails.getGatewayInfo(),
+ mDetails.getVideoState(),
+ mDetails.getStatusHints(),
+ mDetails.getExtras(),
+ mDetails.getIntentExtras(),
+ mDetails.getCreationTimeMillis(),
+ mDetails.getContactDisplayName(),
+ mDetails.getCallDirection(),
+ mDetails.getCallerNumberVerificationStatus()
+ );
+ fireDetailsChanged(mDetails);
+ }
fireStateChanged(mState);
fireCallDestroyed();
}
diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java
index 182dc8b..320308c 100644
--- a/telecomm/java/android/telecom/ParcelableCall.java
+++ b/telecomm/java/android/telecom/ParcelableCall.java
@@ -399,7 +399,7 @@
}
/** The current state of the call. */
- public int getState() {
+ public @Call.CallState int getState() {
return mState;
}
diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java
index 4ae11b8..23cf511 100644
--- a/telephony/java/android/telephony/Annotation.java
+++ b/telephony/java/android/telephony/Annotation.java
@@ -447,6 +447,9 @@
DataFailCause.VSNCP_RECONNECT_NOT_ALLOWED,
DataFailCause.IPV6_PREFIX_UNAVAILABLE,
DataFailCause.HANDOFF_PREFERENCE_CHANGED,
+ DataFailCause.SLICE_REJECTED,
+ DataFailCause.MATCH_ALL_RULE_NOT_ALLOWED,
+ DataFailCause.ALL_MATCHING_RULES_FAILED,
DataFailCause.OEM_DCFAILCAUSE_1,
DataFailCause.OEM_DCFAILCAUSE_2,
DataFailCause.OEM_DCFAILCAUSE_3,
diff --git a/telephony/java/android/telephony/DataSpecificRegistrationInfo.java b/telephony/java/android/telephony/DataSpecificRegistrationInfo.java
index 597fe8f..957f683 100644
--- a/telephony/java/android/telephony/DataSpecificRegistrationInfo.java
+++ b/telephony/java/android/telephony/DataSpecificRegistrationInfo.java
@@ -68,22 +68,22 @@
public final boolean isEnDcAvailable;
/**
- * Provides network support info for LTE VoPS and LTE Emergency bearer support
+ * Provides network support info for VoPS and Emergency bearer support
*/
@Nullable
- private final LteVopsSupportInfo mLteVopsSupportInfo;
+ private final VopsSupportInfo mVopsSupportInfo;
/**
* @hide
*/
DataSpecificRegistrationInfo(
int maxDataCalls, boolean isDcNrRestricted, boolean isNrAvailable,
- boolean isEnDcAvailable, @Nullable LteVopsSupportInfo lteVops) {
+ boolean isEnDcAvailable, @Nullable VopsSupportInfo vops) {
this.maxDataCalls = maxDataCalls;
this.isDcNrRestricted = isDcNrRestricted;
this.isNrAvailable = isNrAvailable;
this.isEnDcAvailable = isEnDcAvailable;
- this.mLteVopsSupportInfo = lteVops;
+ this.mVopsSupportInfo = vops;
}
/**
@@ -97,7 +97,7 @@
isDcNrRestricted = dsri.isDcNrRestricted;
isNrAvailable = dsri.isNrAvailable;
isEnDcAvailable = dsri.isEnDcAvailable;
- mLteVopsSupportInfo = dsri.mLteVopsSupportInfo;
+ mVopsSupportInfo = dsri.mVopsSupportInfo;
}
private DataSpecificRegistrationInfo(/* @NonNull */ Parcel source) {
@@ -105,7 +105,7 @@
isDcNrRestricted = source.readBoolean();
isNrAvailable = source.readBoolean();
isEnDcAvailable = source.readBoolean();
- mLteVopsSupportInfo = LteVopsSupportInfo.CREATOR.createFromParcel(source);
+ mVopsSupportInfo = source.readParcelable(VopsSupportInfo.class.getClassLoader());
}
@Override
@@ -114,7 +114,7 @@
dest.writeBoolean(isDcNrRestricted);
dest.writeBoolean(isNrAvailable);
dest.writeBoolean(isEnDcAvailable);
- mLteVopsSupportInfo.writeToParcel(dest, flags);
+ dest.writeParcelable(mVopsSupportInfo, flags);
}
@Override
@@ -131,15 +131,15 @@
.append(" isDcNrRestricted = " + isDcNrRestricted)
.append(" isNrAvailable = " + isNrAvailable)
.append(" isEnDcAvailable = " + isEnDcAvailable)
- .append(" " + mLteVopsSupportInfo)
+ .append(" " + mVopsSupportInfo)
.append(" }")
.toString();
}
@Override
public int hashCode() {
- return Objects.hash(maxDataCalls, isDcNrRestricted, isNrAvailable, isEnDcAvailable,
- mLteVopsSupportInfo);
+ return Objects.hash(maxDataCalls, isDcNrRestricted, isNrAvailable,
+ isEnDcAvailable, mVopsSupportInfo);
}
@Override
@@ -153,7 +153,7 @@
&& this.isDcNrRestricted == other.isDcNrRestricted
&& this.isNrAvailable == other.isNrAvailable
&& this.isEnDcAvailable == other.isEnDcAvailable
- && Objects.equals(mLteVopsSupportInfo, other.mLteVopsSupportInfo);
+ && Objects.equals(mVopsSupportInfo, other.mVopsSupportInfo);
}
public static final @NonNull Parcelable.Creator<DataSpecificRegistrationInfo> CREATOR =
@@ -171,10 +171,26 @@
/**
* @return The LTE VOPS (Voice over Packet Switched) support information
+ *
+ * @deprecated use {@link #getVopsSupportInfo()}
*/
+ @Deprecated
@NonNull
public LteVopsSupportInfo getLteVopsSupportInfo() {
- return mLteVopsSupportInfo;
+ return mVopsSupportInfo instanceof LteVopsSupportInfo
+ ? (LteVopsSupportInfo) mVopsSupportInfo
+ : new LteVopsSupportInfo(LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE,
+ LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE);
}
+ /**
+ * @return The VOPS (Voice over Packet Switched) support information.
+ *
+ * The instance of {@link LTEVopsSupportInfo}, or {@link NrVopsSupportInfo},
+ * null if there is there is no VOPS support information available.
+ */
+ @Nullable
+ public VopsSupportInfo getVopsSupportInfo() {
+ return mVopsSupportInfo;
+ }
}
diff --git a/telephony/java/android/telephony/DataThrottlingRequest.java b/telephony/java/android/telephony/DataThrottlingRequest.java
index f50bb58..2827e8d 100644
--- a/telephony/java/android/telephony/DataThrottlingRequest.java
+++ b/telephony/java/android/telephony/DataThrottlingRequest.java
@@ -17,6 +17,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.RequiresFeature;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -52,6 +53,9 @@
* @hide
*/
@SystemApi
+ @RequiresFeature(
+ enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+ value = TelephonyManager.CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING)
public static final int DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER = 1;
/**
@@ -63,6 +67,9 @@
* @hide
*/
@SystemApi
+ @RequiresFeature(
+ enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+ value = TelephonyManager.CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING)
public static final int DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER = 2;
/**
@@ -76,6 +83,9 @@
* @hide
*/
@SystemApi
+ @RequiresFeature(
+ enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+ value = TelephonyManager.CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING)
public static final int DATA_THROTTLING_ACTION_HOLD = 3;
/**
diff --git a/telephony/java/android/telephony/LteVopsSupportInfo.java b/telephony/java/android/telephony/LteVopsSupportInfo.java
index 83e41bf..87761e2 100644
--- a/telephony/java/android/telephony/LteVopsSupportInfo.java
+++ b/telephony/java/android/telephony/LteVopsSupportInfo.java
@@ -21,7 +21,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
-import android.os.Parcelable;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -32,7 +32,7 @@
* @hide
*/
@SystemApi
-public final class LteVopsSupportInfo implements Parcelable {
+public final class LteVopsSupportInfo extends VopsSupportInfo {
/**@hide*/
@Retention(RetentionPolicy.SOURCE)
@@ -42,7 +42,10 @@
public @interface LteVopsStatus {}
/**
* Indicates information not available from modem.
+ *
+ * @deprecated as no instance will be created in this case
*/
+ @Deprecated
public static final int LTE_STATUS_NOT_AVAILABLE = 1;
/**
@@ -82,13 +85,38 @@
return mEmcBearerSupport;
}
+ /**
+ * Returns whether VoPS is supported by the network
+ */
+ @Override
+ public boolean isVopsSupported() {
+ return mVopsSupport == LTE_STATUS_SUPPORTED;
+ }
+
+ /**
+ * Returns whether emergency service is supported by the network
+ */
+ @Override
+ public boolean isEmergencyServiceSupported() {
+ return mEmcBearerSupport == LTE_STATUS_SUPPORTED;
+ }
+
+ /**
+ * Returns whether emergency service fallback is supported by the network
+ */
+ @Override
+ public boolean isEmergencyServiceFallbackSupported() {
+ return false;
+ }
+
@Override
public int describeContents() {
return 0;
}
@Override
- public void writeToParcel(Parcel out, int flags) {
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ super.writeToParcel(out, flags, AccessNetworkType.EUTRAN);
out.writeInt(mVopsSupport);
out.writeInt(mEmcBearerSupport);
}
@@ -124,6 +152,8 @@
new Creator<LteVopsSupportInfo>() {
@Override
public LteVopsSupportInfo createFromParcel(Parcel in) {
+ // Skip the type info.
+ in.readInt();
return new LteVopsSupportInfo(in);
}
@@ -133,6 +163,11 @@
}
};
+ /** @hide */
+ protected static LteVopsSupportInfo createFromParcelBody(Parcel in) {
+ return new LteVopsSupportInfo(in);
+ }
+
private LteVopsSupportInfo(Parcel in) {
mVopsSupport = in.readInt();
mEmcBearerSupport = in.readInt();
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index a78f813..5fb60d7 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -293,11 +293,12 @@
@Nullable CellIdentity cellIdentity, @Nullable String rplmn,
int maxDataCalls, boolean isDcNrRestricted,
boolean isNrAvailable, boolean isEndcAvailable,
- LteVopsSupportInfo lteVopsSupportInfo) {
+ @Nullable VopsSupportInfo vopsSupportInfo) {
this(domain, transportType, registrationState, accessNetworkTechnology, rejectCause,
emergencyOnly, availableServices, cellIdentity, rplmn);
mDataSpecificInfo = new DataSpecificRegistrationInfo(
- maxDataCalls, isDcNrRestricted, isNrAvailable, isEndcAvailable, lteVopsSupportInfo);
+ maxDataCalls, isDcNrRestricted, isNrAvailable,
+ isEndcAvailable, vopsSupportInfo);
updateNrState();
}
diff --git a/telephony/java/android/telephony/NrVopsSupportInfo.aidl b/telephony/java/android/telephony/NrVopsSupportInfo.aidl
new file mode 100644
index 0000000..460a589
--- /dev/null
+++ b/telephony/java/android/telephony/NrVopsSupportInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 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.telephony;
+
+parcelable NrVopsSupportInfo;
diff --git a/telephony/java/android/telephony/NrVopsSupportInfo.java b/telephony/java/android/telephony/NrVopsSupportInfo.java
new file mode 100644
index 0000000..155ee38
--- /dev/null
+++ b/telephony/java/android/telephony/NrVopsSupportInfo.java
@@ -0,0 +1,260 @@
+/*
+ * 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.telephony;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Class stores information related to NR network VoPS support
+ * @hide
+ */
+@SystemApi
+public final class NrVopsSupportInfo extends VopsSupportInfo {
+
+ /**
+ * Indicates network does not support vops
+ */
+ public static final int NR_STATUS_VOPS_NOT_SUPPORTED = 0;
+
+ /**
+ * Indicates network supports vops over 3gpp access.
+ */
+ public static final int NR_STATUS_VOPS_3GPP_SUPPORTED = 1;
+
+ /**
+ * Indicates network supports vops over non 3gpp access
+ */
+ public static final int NR_STATUS_VOPS_NON_3GPP_SUPPORTED = 2;
+
+ /**@hide*/
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = {"NR_STATUS_VOPS_"},
+ value = {
+ NR_STATUS_VOPS_NOT_SUPPORTED,
+ NR_STATUS_VOPS_3GPP_SUPPORTED,
+ NR_STATUS_VOPS_NON_3GPP_SUPPORTED
+ })
+ public @interface NrVopsStatus {}
+
+ /**
+ * Indicates network does not support emergency service
+ */
+ public static final int NR_STATUS_EMC_NOT_SUPPORTED = 0;
+
+ /**
+ * Indicates network supports emergency service in NR connected to 5GCN only
+ */
+ public static final int NR_STATUS_EMC_5GCN_ONLY = 1;
+
+ /**
+ * Indicates network supports emergency service in E-UTRA connected to 5GCN only
+ */
+ public static final int NR_STATUS_EMC_EUTRA_5GCN_ONLY = 2;
+
+ /**
+ * Indicates network supports emergency service in NR connected to 5GCN and
+ * E-UTRA connected to 5GCN
+ */
+ public static final int NR_STATUS_EMC_NR_EUTRA_5GCN = 3;
+
+ /**@hide*/
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = {"NR_STATUS_EMC_"},
+ value = {
+ NR_STATUS_EMC_NOT_SUPPORTED,
+ NR_STATUS_EMC_5GCN_ONLY,
+ NR_STATUS_EMC_EUTRA_5GCN_ONLY,
+ NR_STATUS_EMC_NR_EUTRA_5GCN
+ })
+ public @interface NrEmcStatus {}
+
+ /**
+ * Indicates network does not support emergency service
+ */
+ public static final int NR_STATUS_EMF_NOT_SUPPORTED = 0;
+
+ /**
+ * Indicates network supports emergency service fallback in NR connected to 5GCN only
+ */
+ public static final int NR_STATUS_EMF_5GCN_ONLY = 1;
+
+ /**
+ * Indicates network supports emergency service fallback in E-UTRA connected to 5GCN only
+ */
+ public static final int NR_STATUS_EMF_EUTRA_5GCN_ONLY = 2;
+
+ /**
+ * Indicates network supports emergency service fallback in NR connected to 5GCN
+ * and E-UTRA connected to 5GCN
+ */
+ public static final int NR_STATUS_EMF_NR_EUTRA_5GCN = 3;
+
+ /**@hide*/
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = {"NR_STATUS_EMF_"},
+ value = {
+ NR_STATUS_EMF_NOT_SUPPORTED,
+ NR_STATUS_EMF_5GCN_ONLY,
+ NR_STATUS_EMF_EUTRA_5GCN_ONLY,
+ NR_STATUS_EMF_NR_EUTRA_5GCN
+ })
+ public @interface NrEmfStatus {}
+
+ @NrVopsStatus
+ private final int mVopsSupport;
+ @NrEmcStatus
+ private final int mEmcSupport;
+ @NrEmfStatus
+ private final int mEmfSupport;
+
+ public NrVopsSupportInfo(@NrVopsStatus int vops, @NrEmcStatus int emc, @NrEmcStatus int emf) {
+ mVopsSupport = vops;
+ mEmcSupport = emc;
+ mEmfSupport = emf;
+ }
+
+ /**
+ * Provides the NR VoPS support capability as described in:
+ * 3GPP 24.501 EPS network feature support -> IMS VoPS
+ */
+ public @NrVopsStatus int getVopsSupport() {
+ return mVopsSupport;
+ }
+
+ /**
+ * Provides the NR Emergency bearer support capability as described in:
+ * 3GPP 24.501 EPS network feature support -> EMC, and
+ * 38.331 SIB1 : ims-EmergencySupport
+ */
+ public @NrEmcStatus int getEmcSupport() {
+ return mEmcSupport;
+ }
+
+ /**
+ * Provides the NR emergency service fallback support capability as
+ * described in 3GPP 24.501 EPS network feature support -> EMF
+ */
+ public @NrEmfStatus int getEmfSupport() {
+ return mEmfSupport;
+ }
+
+ /**
+ * Returns whether VoPS is supported by the network
+ */
+ @Override
+ public boolean isVopsSupported() {
+ return mVopsSupport != NR_STATUS_VOPS_NOT_SUPPORTED;
+ }
+
+ /**
+ * Returns whether emergency service is supported by the network
+ */
+ @Override
+ public boolean isEmergencyServiceSupported() {
+ return mEmcSupport != NR_STATUS_EMC_NOT_SUPPORTED;
+ }
+
+ /**
+ * Returns whether emergency service fallback is supported by the network
+ */
+ public boolean isEmergencyServiceFallbackSupported() {
+ return mEmfSupport != NR_STATUS_EMF_NOT_SUPPORTED;
+ }
+
+ /**
+ * Implement the Parcelable interface
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ super.writeToParcel(out, flags, AccessNetworkType.NGRAN);
+ out.writeInt(mVopsSupport);
+ out.writeInt(mEmcSupport);
+ out.writeInt(mEmfSupport);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (o == null || !(o instanceof NrVopsSupportInfo)) {
+ return false;
+ }
+ if (this == o) return true;
+ NrVopsSupportInfo other = (NrVopsSupportInfo) o;
+ return mVopsSupport == other.mVopsSupport
+ && mEmcSupport == other.mEmcSupport
+ && mEmfSupport == other.mEmfSupport;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mVopsSupport, mEmcSupport, mEmfSupport);
+ }
+
+ /**
+ * @return string representation.
+ */
+ @NonNull
+ @Override
+ public String toString() {
+ return ("NrVopsSupportInfo : "
+ + " mVopsSupport = " + mVopsSupport
+ + " mEmcSupport = " + mEmcSupport
+ + " mEmfSupport = " + mEmfSupport);
+ }
+
+ public static final @android.annotation.NonNull Creator<NrVopsSupportInfo> CREATOR =
+ new Creator<NrVopsSupportInfo>() {
+ @Override
+ public NrVopsSupportInfo createFromParcel(Parcel in) {
+ // Skip the type info.
+ in.readInt();
+ return new NrVopsSupportInfo(in);
+ }
+
+ @Override
+ public NrVopsSupportInfo[] newArray(int size) {
+ return new NrVopsSupportInfo[size];
+ }
+ };
+
+ /** @hide */
+ protected static NrVopsSupportInfo createFromParcelBody(Parcel in) {
+ return new NrVopsSupportInfo(in);
+ }
+
+ private NrVopsSupportInfo(Parcel in) {
+ mVopsSupport = in.readInt();
+ mEmcSupport = in.readInt();
+ mEmfSupport = in.readInt();
+ }
+}
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 17af463..a527e8d 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -2219,7 +2219,7 @@
ret = iccISms.getSmsCapacityOnIccForSubscriber(getSubscriptionId());
}
} catch (RemoteException ex) {
- throw new RuntimeException(ex);
+ Log.e(TAG, "getSmsCapacityOnIcc() RemoteException", ex);
}
return ret;
}
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/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f06b538..d3246ca 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -11037,6 +11037,25 @@
}
/**
+ * Determines the {@link PhoneAccountHandle} associated with this TelephonyManager.
+ *
+ * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+ *
+ * <p>Requires Permission android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE or that the
+ * calling app has carrier privileges (see {@link #hasCarrierPrivileges})
+ *
+ * @return The {@link PhoneAccountHandle} associated with the TelphonyManager, or {@code null}
+ * if there is no associated {@link PhoneAccountHandle}; this can happen if the subscription is
+ * data-only or an opportunistic subscription.
+ */
+ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public @Nullable PhoneAccountHandle getPhoneAccountHandle() {
+ return getPhoneAccountHandleForSubscriptionId(getSubId());
+ }
+
+ /**
* Determines the {@link PhoneAccountHandle} associated with a subscription Id.
*
* @param subscriptionId The subscription Id to check.
@@ -12388,12 +12407,6 @@
* "Data capable" means that this device supports packet-switched
* data connections over the telephony network.
* <p>
- * Note: the meaning of this flag is subtly different from the
- * PackageManager.FEATURE_TELEPHONY system feature, which is available
- * on any device with a telephony radio, even if the device is
- * voice-only.
- *
- * @hide
*/
public boolean isDataCapable() {
if (mContext == null) return true;
@@ -14928,12 +14941,23 @@
public static final String CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE =
"CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE";
+ /**
+ * Indicates whether a data throttling request sent with {@link #sendThermalMitigationRequest}
+ * is supported. See comments on {@link #sendThermalMitigationRequest} for more information.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING =
+ "CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING";
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@StringDef(prefix = "CAPABILITY_", value = {
CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE,
CAPABILITY_ALLOWED_NETWORK_TYPES_USED,
- CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE
+ CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE,
+ CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING,
})
public @interface RadioInterfaceCapability {}
@@ -15043,11 +15067,24 @@
* and can be used at any time during data throttling to hold onto the current level of data
* throttling.
*
+ * <p> If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}({@link
+ * #CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING}) returns false, then sending a {@link
+ * DataThrottlingRequest#DATA_THROTTLING_ACTION_HOLD}, {@link
+ * DataThrottlingRequest#DATA_THROTTLING_ACTION_THROTTLE_SECONDARY_CARRIER}, or {@link
+ * DataThrottlingRequest#DATA_THROTTLING_ACTION_THROTTLE_PRIMARY_CARRIER} will result in {@link
+ * IllegalArgumentException} being thrown. However, on devices that do not
+ * support data throttling, {@link
+ * DataThrottlingRequest#DATA_THROTTLING_ACTION_NO_DATA_THROTTLING} can still be requested in
+ * order to undo the mitigations above it (i.e {@link
+ * ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_VOICE_ONLY} and/or {@link
+ * ThermalMitigationRequest#THERMAL_MITIGATION_ACTION_RADIO_OFF}).
+ *
* @param thermalMitigationRequest Thermal mitigation request. See {@link
* ThermalMitigationRequest} for details.
*
* @throws IllegalStateException if the Telephony process is not currently available.
- * @throws IllegalArgumentException if the thermalMitigationRequest had invalid parameters.
+ * @throws IllegalArgumentException if the thermalMitigationRequest had invalid parameters or
+ * if the device's modem does not support data throttling.
*
* @hide
*/
diff --git a/telephony/java/android/telephony/VopsSupportInfo.aidl b/telephony/java/android/telephony/VopsSupportInfo.aidl
new file mode 100644
index 0000000..31c608f
--- /dev/null
+++ b/telephony/java/android/telephony/VopsSupportInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 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.telephony;
+
+parcelable VopsSupportInfo;
diff --git a/telephony/java/android/telephony/VopsSupportInfo.java b/telephony/java/android/telephony/VopsSupportInfo.java
new file mode 100644
index 0000000..f89bfa9
--- /dev/null
+++ b/telephony/java/android/telephony/VopsSupportInfo.java
@@ -0,0 +1,103 @@
+/*
+ * 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.telephony;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
+
+/**
+ * Abstract base class for the information related to network VoPS support.
+ * This is the base class for XxVopsSupportInfo which represent VoPS support
+ * information for specific network access techonology.
+ * @hide
+ */
+@SuppressLint("ParcelNotFinal")
+@SystemApi
+public abstract class VopsSupportInfo implements Parcelable {
+
+ /**
+ * @hide
+ */
+ public VopsSupportInfo() {}
+
+ /**
+ * Returns whether VoPS is supported by the network
+ */
+ public abstract boolean isVopsSupported();
+
+ /**
+ * Returns whether emergency service is supported by the network
+ */
+ public abstract boolean isEmergencyServiceSupported();
+
+ /**
+ * Returns whether emergency service fallback is supported by the network
+ */
+ public abstract boolean isEmergencyServiceFallbackSupported();
+
+ /**
+ * Implement the Parcelable interface
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public abstract void writeToParcel(@NonNull Parcel dest, int flags);
+
+ /**
+ * Used by child classes for parceling.
+ *
+ * @hide
+ */
+ protected void writeToParcel(@NonNull Parcel dest, int flags, int type) {
+ dest.writeInt(type);
+ }
+
+ /** Implement the Parcelable interface */
+ public static final @android.annotation.NonNull Creator<VopsSupportInfo> CREATOR =
+ new Creator<VopsSupportInfo>() {
+ @Override
+ public VopsSupportInfo createFromParcel(Parcel in) {
+ int type = in.readInt();
+ switch (type) {
+ case AccessNetworkType.EUTRAN:
+ return LteVopsSupportInfo.createFromParcelBody(in);
+ case AccessNetworkType.NGRAN:
+ return NrVopsSupportInfo.createFromParcelBody(in);
+ default: throw new RuntimeException("Bad VopsSupportInfo Parcel");
+ }
+ }
+
+ @Override
+ public VopsSupportInfo[] newArray(int size) {
+ return new VopsSupportInfo[size];
+ }
+ };
+
+ @Override
+ public abstract int hashCode();
+
+ @Override
+ public abstract boolean equals(Object o);
+}
diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
index 52d0f03..a133ead 100644
--- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java
+++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
@@ -29,7 +29,9 @@
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* Contains the User Capability Exchange capabilities corresponding to a contact's URI.
@@ -110,7 +112,6 @@
/**
* Builder to help construct {@link RcsContactUceCapability} instances when capabilities were
* queried through SIP OPTIONS.
- * @hide
*/
public static final class OptionsBuilder {
@@ -151,7 +152,7 @@
* @param tags the list of the supported feature tags
* @return this OptionBuilder
*/
- public @NonNull OptionsBuilder addFeatureTags(@NonNull List<String> tags) {
+ public @NonNull OptionsBuilder addFeatureTags(@NonNull Set<String> tags) {
mCapabilities.mFeatureTags.addAll(tags);
return this;
}
@@ -220,7 +221,7 @@
private @CapabilityMechanism int mCapabilityMechanism;
private @RequestResult int mRequestResult;
- private final List<String> mFeatureTags = new ArrayList<>();
+ private final Set<String> mFeatureTags = new HashSet<>();
private final List<RcsContactPresenceTuple> mPresenceTuples = new ArrayList<>();
private RcsContactUceCapability(@NonNull Uri contactUri, @CapabilityMechanism int mechanism,
@@ -235,7 +236,9 @@
mCapabilityMechanism = in.readInt();
mSourceType = in.readInt();
mRequestResult = in.readInt();
- in.readStringList(mFeatureTags);
+ List<String> featureTagList = new ArrayList<>();
+ in.readStringList(featureTagList);
+ mFeatureTags.addAll(featureTagList);
in.readParcelableList(mPresenceTuples, RcsContactPresenceTuple.class.getClassLoader());
}
@@ -245,7 +248,7 @@
out.writeInt(mCapabilityMechanism);
out.writeInt(mSourceType);
out.writeInt(mRequestResult);
- out.writeStringList(mFeatureTags);
+ out.writeStringList(new ArrayList<>(mFeatureTags));
out.writeParcelableList(mPresenceTuples, flags);
}
@@ -285,7 +288,20 @@
if (mCapabilityMechanism != CAPABILITY_MECHANISM_OPTIONS) {
return Collections.emptyList();
}
- return Collections.unmodifiableList(mFeatureTags);
+ return Collections.unmodifiableList(new ArrayList<>(mFeatureTags));
+ }
+
+ /**
+ * @return The feature tags present in the OPTIONS response from the network.
+ * <p>
+ * Note: this is only populated if {@link #getCapabilityMechanism} is
+ * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_OPTIONS}
+ */
+ public @NonNull Set<String> getFeatureTags() {
+ if (mCapabilityMechanism != CAPABILITY_MECHANISM_OPTIONS) {
+ return Collections.emptySet();
+ }
+ return Collections.unmodifiableSet(mFeatureTags);
}
/**
diff --git a/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java
index a217d13..c3d7325 100644
--- a/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/CapabilityExchangeAidlWrapper.java
@@ -26,7 +26,8 @@
import android.telephony.ims.stub.CapabilityExchangeEventListener;
import android.util.Log;
-import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
/**
* The ICapabilityExchangeEventListener wrapper class to store the listener which is registered by
@@ -84,7 +85,7 @@
* request to the framework.
*/
public void onRemoteCapabilityRequest(@NonNull Uri contactUri,
- @NonNull List<String> remoteCapabilities, @NonNull OptionsRequestCallback callback)
+ @NonNull Set<String> remoteCapabilities, @NonNull OptionsRequestCallback callback)
throws ImsException {
ICapabilityExchangeEventListener listener = mListenerBinder;
if (listener == null) {
@@ -114,7 +115,8 @@
};
try {
- listener.onRemoteCapabilityRequest(contactUri, remoteCapabilities, internalCallback);
+ listener.onRemoteCapabilityRequest(contactUri, new ArrayList<>(remoteCapabilities),
+ internalCallback);
} catch (RemoteException e) {
Log.w(LOG_TAG, "Remote capability request exception: " + e);
throw new ImsException("Remote is not available",
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index 85703f8..6315b24 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -47,6 +47,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.HashSet;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
@@ -145,8 +146,8 @@
throws RemoteException {
OptionsResponseCallback callbackWrapper = new RcsOptionsResponseAidlWrapper(callback);
executeMethodAsync(() -> mReference.getCapabilityExchangeImplBaseInternal()
- .sendOptionsCapabilityRequest(contactUri, myCapabilities, callbackWrapper),
- "sendOptionsCapabilityRequest");
+ .sendOptionsCapabilityRequest(contactUri, new HashSet<>(myCapabilities),
+ callbackWrapper), "sendOptionsCapabilityRequest");
}
// Call the methods with a clean calling identity on the executor and wait indefinitely for
diff --git a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
index 6295548..a3be8da 100644
--- a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
+++ b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
@@ -26,7 +26,7 @@
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.RcsFeature;
-import java.util.List;
+import java.util.Set;
/**
* The interface that is used by the framework to listen to events from the vendor RCS stack
@@ -98,7 +98,8 @@
* {@link OptionsRequestCallback#onRespondToCapabilityRequestWithError}.
* @param contactUri The URI associated with the remote contact that is
* requesting capabilities.
- * @param remoteCapabilities The remote contact's capability information.
+ * @param remoteCapabilities The remote contact's capability information. The capability
+ * information is in the format defined in RCC.07 section 2.6.1.3.
* @param callback The callback of this request which is sent from the remote user.
* @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is not
* currently connected to the framework. This can happen if the {@link RcsFeature} is not
@@ -107,6 +108,6 @@
* cases when the Telephony stack has crashed.
*/
void onRemoteCapabilityRequest(@NonNull Uri contactUri,
- @NonNull List<String> remoteCapabilities,
+ @NonNull Set<String> remoteCapabilities,
@NonNull OptionsRequestCallback callback) throws ImsException;
}
diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
index 03e17fb..25b9446 100644
--- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
@@ -33,6 +33,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.Collection;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.Executor;
/**
@@ -433,6 +434,7 @@
* @param contactUri The URI of the remote user that we wish to get the capabilities of.
* @param myCapabilities The capabilities of this device to send to the remote user.
* @param callback The callback of this request which is sent from the remote user.
+ * @hide
*/
// executor used is defined in the constructor.
@SuppressLint("ExecutorRegistration")
@@ -446,4 +448,27 @@
// Do not do anything, this is a stub implementation.
}
}
+
+ /**
+ * Push one's own capabilities to a remote user via the SIP OPTIONS presence exchange mechanism
+ * in order to receive the capabilities of the remote user in response.
+ * <p>
+ * The implementer must use {@link OptionsResponseCallback} to send the response of
+ * this query from the network back to the framework.
+ * @param contactUri The URI of the remote user that we wish to get the capabilities of.
+ * @param myCapabilities The capabilities of this device to send to the remote user.
+ * @param callback The callback of this request which is sent from the remote user.
+ */
+ // executor used is defined in the constructor.
+ @SuppressLint("ExecutorRegistration")
+ public void sendOptionsCapabilityRequest(@NonNull Uri contactUri,
+ @NonNull Set<String> myCapabilities, @NonNull OptionsResponseCallback callback) {
+ // Stub - to be implemented by service
+ Log.w(LOG_TAG, "sendOptionsCapabilityRequest called with no implementation.");
+ try {
+ callback.onCommandError(COMMAND_CODE_NOT_SUPPORTED);
+ } catch (ImsException e) {
+ // Do not do anything, this is a stub implementation.
+ }
+ }
}
diff --git a/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt b/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt
index b9b347b..c1a86b3 100644
--- a/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt
+++ b/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt
@@ -87,7 +87,7 @@
assertEquals(down.id, eventId)
// Now send CALLBACK_INPUT to the assigner. It should provide the latest motion event
- assigner.onChoreographerCallback()
+ assigner.notifyFrameProcessed()
eventId = assigner.processEvent(move3)
assertEquals(move3.id, eventId)
eventId = assigner.processEvent(move4)
@@ -122,7 +122,7 @@
eventId = assigner.processEvent(up)
// DOWN is only sticky for Motions, not for keys
assertEquals(up.id, eventId)
- assigner.onChoreographerCallback()
+ assigner.notifyFrameProcessed()
val down2 = createKeyEvent(KeyEvent.ACTION_DOWN, 22)
eventId = assigner.processEvent(down2)
assertEquals(down2.id, eventId)
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 a7ad695..f161e52 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -35,6 +35,9 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P;
+import static android.net.NetworkCapabilities.REDACT_FOR_ACCESS_FINE_LOCATION;
+import static android.net.NetworkCapabilities.REDACT_FOR_LOCAL_MAC_ADDRESS;
+import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
import static android.net.NetworkCapabilities.RESTRICTED_CAPABILITIES;
import static android.net.NetworkCapabilities.SIGNAL_STRENGTH_UNSPECIFIED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
@@ -51,7 +54,6 @@
import static com.android.testutils.MiscAsserts.assertThrows;
import static com.android.testutils.ParcelUtils.assertParcelSane;
import static com.android.testutils.ParcelUtils.assertParcelingIsLossless;
-import static com.android.testutils.ParcelUtils.parcelingRoundTrip;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -62,7 +64,6 @@
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
-import android.net.wifi.WifiInfo;
import android.net.wifi.aware.DiscoverySession;
import android.net.wifi.aware.PeerHandle;
import android.net.wifi.aware.WifiAwareNetworkSpecifier;
@@ -352,55 +353,6 @@
testParcelSane(netCap);
}
- private NetworkCapabilities createNetworkCapabilitiesWithWifiInfo() {
- // uses a real WifiInfo to test parceling of sensitive data.
- final WifiInfo wifiInfo = new WifiInfo.Builder()
- .setSsid("sssid1234".getBytes())
- .setBssid("00:11:22:33:44:55")
- .build();
- return new NetworkCapabilities()
- .addCapability(NET_CAPABILITY_INTERNET)
- .addCapability(NET_CAPABILITY_EIMS)
- .addCapability(NET_CAPABILITY_NOT_METERED)
- .setSSID(TEST_SSID)
- .setTransportInfo(wifiInfo)
- .setRequestorPackageName("com.android.test")
- .setRequestorUid(9304);
- }
-
- @Test
- public void testParcelNetworkCapabilitiesWithLocationSensitiveFields() {
- assumeTrue(isAtLeastS());
-
- final NetworkCapabilities netCap = createNetworkCapabilitiesWithWifiInfo();
- final NetworkCapabilities netCapWithLocationSensitiveFields =
- new NetworkCapabilities(netCap, true);
-
- assertParcelingIsLossless(netCapWithLocationSensitiveFields);
- testParcelSane(netCapWithLocationSensitiveFields);
-
- assertEquals(netCapWithLocationSensitiveFields,
- parcelingRoundTrip(netCapWithLocationSensitiveFields));
- }
-
- @Test
- public void testParcelNetworkCapabilitiesWithoutLocationSensitiveFields() {
- assumeTrue(isAtLeastS());
-
- final NetworkCapabilities netCap = createNetworkCapabilitiesWithWifiInfo();
- final NetworkCapabilities netCapWithoutLocationSensitiveFields =
- new NetworkCapabilities(netCap, false);
-
- final NetworkCapabilities sanitizedNetCap =
- new NetworkCapabilities(netCapWithoutLocationSensitiveFields);
- final WifiInfo sanitizedWifiInfo = new WifiInfo.Builder()
- .setSsid(new byte[0])
- .setBssid(WifiInfo.DEFAULT_MAC_ADDRESS)
- .build();
- sanitizedNetCap.setTransportInfo(sanitizedWifiInfo);
- assertEquals(sanitizedNetCap, parcelingRoundTrip(netCapWithoutLocationSensitiveFields));
- }
-
private void testParcelSane(NetworkCapabilities cap) {
if (isAtLeastS()) {
assertParcelSane(cap, 17);
@@ -411,6 +363,45 @@
}
}
+ private static NetworkCapabilities createNetworkCapabilitiesWithTransportInfo() {
+ return new NetworkCapabilities()
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .addCapability(NET_CAPABILITY_EIMS)
+ .addCapability(NET_CAPABILITY_NOT_METERED)
+ .setSSID(TEST_SSID)
+ .setTransportInfo(new TestTransportInfo())
+ .setRequestorPackageName("com.android.test")
+ .setRequestorUid(9304);
+ }
+
+ @Test
+ public void testNetworkCapabilitiesCopyWithNoRedactions() {
+ assumeTrue(isAtLeastS());
+
+ final NetworkCapabilities netCap = createNetworkCapabilitiesWithTransportInfo();
+ final NetworkCapabilities netCapWithNoRedactions =
+ new NetworkCapabilities(netCap, NetworkCapabilities.REDACT_NONE);
+ TestTransportInfo testTransportInfo =
+ (TestTransportInfo) netCapWithNoRedactions.getTransportInfo();
+ assertFalse(testTransportInfo.locationRedacted);
+ assertFalse(testTransportInfo.localMacAddressRedacted);
+ assertFalse(testTransportInfo.settingsRedacted);
+ }
+
+ @Test
+ public void testNetworkCapabilitiesCopyWithoutLocationSensitiveFields() {
+ assumeTrue(isAtLeastS());
+
+ final NetworkCapabilities netCap = createNetworkCapabilitiesWithTransportInfo();
+ final NetworkCapabilities netCapWithNoRedactions =
+ new NetworkCapabilities(netCap, REDACT_FOR_ACCESS_FINE_LOCATION);
+ TestTransportInfo testTransportInfo =
+ (TestTransportInfo) netCapWithNoRedactions.getTransportInfo();
+ assertTrue(testTransportInfo.locationRedacted);
+ assertFalse(testTransportInfo.localMacAddressRedacted);
+ assertFalse(testTransportInfo.settingsRedacted);
+ }
+
@Test
public void testOemPaid() {
NetworkCapabilities nc = new NetworkCapabilities();
@@ -540,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
@@ -605,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);
@@ -1062,18 +1073,42 @@
} catch (IllegalArgumentException e) { }
}
- private class TestTransportInfo implements TransportInfo {
+ /**
+ * Test TransportInfo to verify redaction mechanism.
+ */
+ private static class TestTransportInfo implements TransportInfo {
+ public final boolean locationRedacted;
+ public final boolean localMacAddressRedacted;
+ public final boolean settingsRedacted;
+
TestTransportInfo() {
+ locationRedacted = false;
+ localMacAddressRedacted = false;
+ settingsRedacted = false;
+ }
+
+ TestTransportInfo(boolean locationRedacted,
+ boolean localMacAddressRedacted,
+ boolean settingsRedacted) {
+ this.locationRedacted = locationRedacted;
+ this.localMacAddressRedacted =
+ localMacAddressRedacted;
+ this.settingsRedacted = settingsRedacted;
}
@Override
- public TransportInfo makeCopy(boolean parcelLocationSensitiveFields) {
- return this;
+ public TransportInfo makeCopy(@NetworkCapabilities.RedactionType long redactions) {
+ return new TestTransportInfo(
+ (redactions & NetworkCapabilities.REDACT_FOR_ACCESS_FINE_LOCATION) != 0,
+ (redactions & REDACT_FOR_LOCAL_MAC_ADDRESS) != 0,
+ (redactions & REDACT_FOR_NETWORK_SETTINGS) != 0
+ );
}
@Override
- public boolean hasLocationSensitiveFields() {
- return false;
+ public @NetworkCapabilities.RedactionType long getApplicableRedactions() {
+ return REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_LOCAL_MAC_ADDRESS
+ | REDACT_FOR_NETWORK_SETTINGS;
}
}
@@ -1084,7 +1119,7 @@
final int requestUid = 10100;
final int[] administratorUids = {ownerUid, 10001};
final TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier(1);
- final TestTransportInfo transportInfo = new TestTransportInfo();
+ final TransportInfo transportInfo = new TransportInfo() {};
final String ssid = "TEST_SSID";
final String packageName = "com.google.test.networkcapabilities";
final NetworkCapabilities nc = new NetworkCapabilities.Builder()
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 0b31999..0c2fb4e 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -82,6 +82,10 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P;
import static android.net.NetworkCapabilities.NET_CAPABILITY_XCAP;
+import static android.net.NetworkCapabilities.REDACT_FOR_ACCESS_FINE_LOCATION;
+import static android.net.NetworkCapabilities.REDACT_FOR_LOCAL_MAC_ADDRESS;
+import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
+import static android.net.NetworkCapabilities.REDACT_NONE;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
@@ -237,7 +241,6 @@
import android.net.shared.NetworkMonitorUtils;
import android.net.shared.PrivateDnsConfig;
import android.net.util.MultinetworkPolicyTracker;
-import android.net.wifi.WifiInfo;
import android.os.BadParcelableException;
import android.os.Binder;
import android.os.Build;
@@ -1175,11 +1178,6 @@
}
@Override
- public int getNetId() {
- return (mMockNetworkAgent == null) ? NETID_UNSET : mMockNetworkAgent.getNetwork().netId;
- }
-
- @Override
public int getActiveVpnType() {
return mVpnType;
}
@@ -1203,10 +1201,12 @@
mNetworkCapabilities);
mMockNetworkAgent.waitForIdle(TIMEOUT_MS);
- verify(mMockNetd, times(1)).networkAddUidRanges(eq(mMockVpn.getNetId()),
+ final int expectedNetId = mMockVpn.getNetwork() == null ? NETID_UNSET
+ : mMockVpn.getNetwork().getNetId();
+ verify(mMockNetd, times(1)).networkAddUidRanges(eq(expectedNetId),
eq(toUidRangeStableParcels(uids)));
verify(mMockNetd, never())
- .networkRemoveUidRanges(eq(mMockVpn.getNetId()), any());
+ .networkRemoveUidRanges(eq(expectedNetId), any());
mAgentRegistered = true;
updateState(NetworkInfo.DetailedState.CONNECTED, "registerAgent");
mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities());
@@ -1564,25 +1564,26 @@
}).when(deps).makeMultinetworkPolicyTracker(any(), any(), any());
doReturn(true).when(deps).getCellular464XlatEnabled();
- doReturn(60000).when(mResources).getInteger(
- com.android.connectivity.resources.R.integer.config_networkTransitionTimeout);
- doReturn("").when(mResources).getString(
- com.android.connectivity.resources.R.string.config_networkCaptivePortalServerUrl);
+ doReturn(60000).when(mResources).getInteger(R.integer.config_networkTransitionTimeout);
+ doReturn("").when(mResources).getString(R.string.config_networkCaptivePortalServerUrl);
doReturn(new String[]{ WIFI_WOL_IFNAME }).when(mResources).getStringArray(
- com.android.connectivity.resources.R.array.config_wakeonlan_supported_interfaces);
+ R.array.config_wakeonlan_supported_interfaces);
doReturn(new String[] { "0,1", "1,3" }).when(mResources).getStringArray(
- com.android.connectivity.resources.R.array.config_networkSupportedKeepaliveCount);
- doReturn(com.android.connectivity.resources.R.array.config_networkSupportedKeepaliveCount)
- .when(mResources).getIdentifier(eq("config_networkSupportedKeepaliveCount"),
- eq("array"), any());
- doReturn(com.android.connectivity.resources.R.array.network_switch_type_name)
- .when(mResources).getIdentifier(eq("network_switch_type_name"),
- eq("array"), any());
-
+ R.array.config_networkSupportedKeepaliveCount);
+ doReturn(new String[0]).when(mResources).getStringArray(
+ R.array.config_networkNotifySwitches);
+ doReturn(new int[]{10, 11, 12, 14, 15}).when(mResources).getIntArray(
+ R.array.config_protectedNetworks);
// We don't test the actual notification value strings, so just return an empty array.
// It doesn't matter what the values are as long as it's not null.
doReturn(new String[0]).when(mResources).getStringArray(R.array.network_switch_type_name);
+ doReturn(R.array.config_networkSupportedKeepaliveCount).when(mResources)
+ .getIdentifier(eq("config_networkSupportedKeepaliveCount"), eq("array"), any());
+ doReturn(R.array.network_switch_type_name).when(mResources)
+ .getIdentifier(eq("network_switch_type_name"), eq("array"), any());
+
+
final ConnectivityResources connRes = mock(ConnectivityResources.class);
doReturn(mResources).when(connRes).get();
doReturn(connRes).when(deps).getResources(any());
@@ -7484,6 +7485,9 @@
final NetworkRequest vpnUidRequest = new NetworkRequest.Builder().build();
registerNetworkCallbackAsUid(vpnUidRequest, vpnUidCallback, VPN_UID);
+ final TestNetworkCallback vpnUidDefaultCallback = new TestNetworkCallback();
+ registerDefaultNetworkCallbackAsUid(vpnUidDefaultCallback, VPN_UID);
+
final int uid = Process.myUid();
final int userId = UserHandle.getUserId(uid);
final ArrayList<String> allowList = new ArrayList<>();
@@ -7502,6 +7506,7 @@
callback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent);
vpnUidCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ vpnUidDefaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
assertNull(mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
@@ -7514,6 +7519,7 @@
callback.expectBlockedStatusCallback(false, mWiFiNetworkAgent);
defaultCallback.expectBlockedStatusCallback(false, mWiFiNetworkAgent);
vpnUidCallback.assertNoCallback();
+ vpnUidDefaultCallback.assertNoCallback();
expectNetworkRejectNonSecureVpn(inOrder, false, firstHalf, secondHalf);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
@@ -7528,6 +7534,7 @@
callback.assertNoCallback();
defaultCallback.assertNoCallback();
vpnUidCallback.assertNoCallback();
+ vpnUidDefaultCallback.assertNoCallback();
// The following requires that the UID of this test package is greater than VPN_UID. This
// is always true in practice because a plain AOSP build with no apps installed has almost
@@ -7548,6 +7555,7 @@
callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
defaultCallback.assertNoCallback();
vpnUidCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
+ vpnUidDefaultCallback.assertNoCallback();
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
@@ -7568,6 +7576,7 @@
defaultCallback.expectBlockedStatusCallback(true, mWiFiNetworkAgent);
assertBlockedCallbackInAnyOrder(callback, true, mWiFiNetworkAgent, mCellNetworkAgent);
vpnUidCallback.assertNoCallback();
+ vpnUidDefaultCallback.assertNoCallback();
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
assertNull(mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
@@ -7579,6 +7588,7 @@
defaultCallback.expectBlockedStatusCallback(false, mWiFiNetworkAgent);
assertBlockedCallbackInAnyOrder(callback, false, mWiFiNetworkAgent, mCellNetworkAgent);
vpnUidCallback.assertNoCallback();
+ vpnUidDefaultCallback.assertNoCallback();
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
@@ -7593,6 +7603,7 @@
callback.assertNoCallback();
defaultCallback.assertNoCallback();
vpnUidCallback.assertNoCallback();
+ vpnUidDefaultCallback.assertNoCallback();
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
@@ -7604,6 +7615,7 @@
callback.assertNoCallback();
defaultCallback.assertNoCallback();
vpnUidCallback.assertNoCallback();
+ vpnUidDefaultCallback.assertNoCallback();
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
@@ -7616,6 +7628,7 @@
defaultCallback.expectBlockedStatusCallback(true, mWiFiNetworkAgent);
assertBlockedCallbackInAnyOrder(callback, true, mWiFiNetworkAgent, mCellNetworkAgent);
vpnUidCallback.assertNoCallback();
+ vpnUidDefaultCallback.assertNoCallback();
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
assertNull(mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
@@ -7626,6 +7639,7 @@
assertUidRangesUpdatedForMyUid(true);
defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
vpnUidCallback.assertNoCallback(); // vpnUidCallback has NOT_VPN capability.
+ vpnUidDefaultCallback.assertNoCallback(); // VPN does not apply to VPN_UID
assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
@@ -7636,11 +7650,14 @@
mMockVpn.disconnect();
defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent);
+ vpnUidCallback.assertNoCallback();
+ vpnUidDefaultCallback.assertNoCallback();
assertNull(mCm.getActiveNetwork());
mCm.unregisterNetworkCallback(callback);
mCm.unregisterNetworkCallback(defaultCallback);
mCm.unregisterNetworkCallback(vpnUidCallback);
+ mCm.unregisterNetworkCallback(vpnUidDefaultCallback);
}
private void setupLegacyLockdownVpn() {
@@ -8840,29 +8857,34 @@
final NetworkCapabilities netCap = new NetworkCapabilities().setOwnerUid(ownerUid);
return mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- netCap, includeLocationSensitiveInfo, callerUid,
+ netCap, includeLocationSensitiveInfo, Process.myUid(), callerUid,
mContext.getPackageName(), getAttributionTag())
.getOwnerUid();
}
- private void verifyWifiInfoCopyNetCapsPermission(
+ private void verifyTransportInfoCopyNetCapsPermission(
int callerUid, boolean includeLocationSensitiveInfo,
boolean shouldMakeCopyWithLocationSensitiveFieldsParcelable) {
- final WifiInfo wifiInfo = mock(WifiInfo.class);
- when(wifiInfo.hasLocationSensitiveFields()).thenReturn(true);
- final NetworkCapabilities netCap = new NetworkCapabilities().setTransportInfo(wifiInfo);
+ final TransportInfo transportInfo = mock(TransportInfo.class);
+ when(transportInfo.getApplicableRedactions()).thenReturn(REDACT_FOR_ACCESS_FINE_LOCATION);
+ final NetworkCapabilities netCap =
+ new NetworkCapabilities().setTransportInfo(transportInfo);
mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- netCap, includeLocationSensitiveInfo, callerUid,
+ netCap, includeLocationSensitiveInfo, Process.myPid(), callerUid,
mContext.getPackageName(), getAttributionTag());
- verify(wifiInfo).makeCopy(eq(shouldMakeCopyWithLocationSensitiveFieldsParcelable));
+ if (shouldMakeCopyWithLocationSensitiveFieldsParcelable) {
+ verify(transportInfo).makeCopy(REDACT_NONE);
+ } else {
+ verify(transportInfo).makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION);
+ }
}
- private void verifyOwnerUidAndWifiInfoNetCapsPermission(
+ private void verifyOwnerUidAndTransportInfoNetCapsPermission(
boolean shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag,
boolean shouldInclLocationSensitiveOwnerUidWithIncludeFlag,
- boolean shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag,
- boolean shouldInclLocationSensitiveWifiInfoWithIncludeFlag) {
+ boolean shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag,
+ boolean shouldInclLocationSensitiveTransportInfoWithIncludeFlag) {
final int myUid = Process.myUid();
final int expectedOwnerUidWithoutIncludeFlag =
@@ -8876,13 +8898,13 @@
assertEquals(expectedOwnerUidWithIncludeFlag, getOwnerUidNetCapsPermission(
myUid, myUid, true /* includeLocationSensitiveInfo */));
- verifyWifiInfoCopyNetCapsPermission(myUid,
+ verifyTransportInfoCopyNetCapsPermission(myUid,
false, /* includeLocationSensitiveInfo */
- shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag);
+ shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag);
- verifyWifiInfoCopyNetCapsPermission(myUid,
+ verifyTransportInfoCopyNetCapsPermission(myUid,
true, /* includeLocationSensitiveInfo */
- shouldInclLocationSensitiveWifiInfoWithIncludeFlag);
+ shouldInclLocationSensitiveTransportInfoWithIncludeFlag);
}
@@ -8892,15 +8914,15 @@
setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION);
- verifyOwnerUidAndWifiInfoNetCapsPermission(
+ verifyOwnerUidAndTransportInfoNetCapsPermission(
// Ensure that we include owner uid even if the request asks to remove it since the
// app has necessary permissions and targetSdk < S.
true, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
- false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
+ false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */
// Ensure that we remove location info if the request asks to remove it even if the
// app has necessary permissions.
- true /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+ true /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */
);
}
@@ -8910,15 +8932,15 @@
setupLocationPermissions(Build.VERSION_CODES.R, true, AppOpsManager.OPSTR_FINE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION);
- verifyOwnerUidAndWifiInfoNetCapsPermission(
+ verifyOwnerUidAndTransportInfoNetCapsPermission(
// Ensure that we include owner uid even if the request asks to remove it since the
// app has necessary permissions and targetSdk < S.
true, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
- false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
+ false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */
// Ensure that we remove location info if the request asks to remove it even if the
// app has necessary permissions.
- true /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+ true /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */
);
}
@@ -8929,15 +8951,15 @@
setupLocationPermissions(Build.VERSION_CODES.S, true, AppOpsManager.OPSTR_FINE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION);
- verifyOwnerUidAndWifiInfoNetCapsPermission(
+ verifyOwnerUidAndTransportInfoNetCapsPermission(
// Ensure that we owner UID if the request asks us to remove it even if the app
// has necessary permissions since targetSdk >= S.
false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
- false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
+ false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */
// Ensure that we remove location info if the request asks to remove it even if the
// app has necessary permissions.
- true /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+ true /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */
);
}
@@ -8947,15 +8969,15 @@
setupLocationPermissions(Build.VERSION_CODES.P, true, AppOpsManager.OPSTR_COARSE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION);
- verifyOwnerUidAndWifiInfoNetCapsPermission(
+ verifyOwnerUidAndTransportInfoNetCapsPermission(
// Ensure that we owner UID if the request asks us to remove it even if the app
// has necessary permissions since targetSdk >= S.
true, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
- false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
+ false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */
// Ensure that we remove location info if the request asks to remove it even if the
// app has necessary permissions.
- true /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+ true /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */
);
}
@@ -8965,11 +8987,11 @@
setupLocationPermissions(Build.VERSION_CODES.Q, false, AppOpsManager.OPSTR_FINE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION);
- verifyOwnerUidAndWifiInfoNetCapsPermission(
+ verifyOwnerUidAndTransportInfoNetCapsPermission(
false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
false, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
- false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
- false /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+ false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */
+ false /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */
);
}
@@ -8992,11 +9014,11 @@
setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_COARSE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION);
- verifyOwnerUidAndWifiInfoNetCapsPermission(
+ verifyOwnerUidAndTransportInfoNetCapsPermission(
false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
false, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
- false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
- false /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+ false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */
+ false /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */
);
}
@@ -9006,14 +9028,193 @@
// Test that not having fine location permission leads to sanitization.
setupLocationPermissions(Build.VERSION_CODES.Q, true, null /* op */, null /* perm */);
- verifyOwnerUidAndWifiInfoNetCapsPermission(
+ verifyOwnerUidAndTransportInfoNetCapsPermission(
false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
false, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
- false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
- false /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+ false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */
+ false /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */
);
}
+ @Test
+ public void testCreateForCallerWithLocalMacAddressSanitizedWithLocalMacAddressPermission()
+ throws Exception {
+ mServiceContext.setPermission(Manifest.permission.LOCAL_MAC_ADDRESS, PERMISSION_GRANTED);
+
+ final TransportInfo transportInfo = mock(TransportInfo.class);
+ when(transportInfo.getApplicableRedactions())
+ .thenReturn(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_LOCAL_MAC_ADDRESS);
+ final NetworkCapabilities netCap =
+ new NetworkCapabilities().setTransportInfo(transportInfo);
+
+ mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled(
+ netCap, false /* includeLocationSensitiveInfoInTransportInfo */,
+ Process.myPid(), Process.myUid(),
+ mContext.getPackageName(), getAttributionTag());
+ // don't redact MAC_ADDRESS fields, only location sensitive fields.
+ verify(transportInfo).makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION);
+ }
+
+ @Test
+ public void testCreateForCallerWithLocalMacAddressSanitizedWithoutLocalMacAddressPermission()
+ throws Exception {
+ mServiceContext.setPermission(Manifest.permission.LOCAL_MAC_ADDRESS, PERMISSION_DENIED);
+
+ final TransportInfo transportInfo = mock(TransportInfo.class);
+ when(transportInfo.getApplicableRedactions())
+ .thenReturn(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_LOCAL_MAC_ADDRESS);
+ final NetworkCapabilities netCap =
+ new NetworkCapabilities().setTransportInfo(transportInfo);
+
+ mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled(
+ netCap, false /* includeLocationSensitiveInfoInTransportInfo */,
+ Process.myPid(), Process.myUid(),
+ mContext.getPackageName(), getAttributionTag());
+ // redact both MAC_ADDRESS & location sensitive fields.
+ verify(transportInfo).makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION
+ | REDACT_FOR_LOCAL_MAC_ADDRESS);
+ }
+
+ @Test
+ public void testCreateForCallerWithLocalMacAddressSanitizedWithSettingsPermission()
+ throws Exception {
+ mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED);
+
+ final TransportInfo transportInfo = mock(TransportInfo.class);
+ when(transportInfo.getApplicableRedactions())
+ .thenReturn(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_NETWORK_SETTINGS);
+ final NetworkCapabilities netCap =
+ new NetworkCapabilities().setTransportInfo(transportInfo);
+
+ mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled(
+ netCap, false /* includeLocationSensitiveInfoInTransportInfo */,
+ Process.myPid(), Process.myUid(),
+ mContext.getPackageName(), getAttributionTag());
+ // don't redact NETWORK_SETTINGS fields, only location sensitive fields.
+ verify(transportInfo).makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION);
+ }
+
+ @Test
+ public void testCreateForCallerWithLocalMacAddressSanitizedWithoutSettingsPermission()
+ throws Exception {
+ mServiceContext.setPermission(Manifest.permission.LOCAL_MAC_ADDRESS, PERMISSION_DENIED);
+
+ final TransportInfo transportInfo = mock(TransportInfo.class);
+ when(transportInfo.getApplicableRedactions())
+ .thenReturn(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_NETWORK_SETTINGS);
+ final NetworkCapabilities netCap =
+ new NetworkCapabilities().setTransportInfo(transportInfo);
+
+ mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled(
+ netCap, false /* includeLocationSensitiveInfoInTransportInfo */,
+ Process.myPid(), Process.myUid(),
+ mContext.getPackageName(), getAttributionTag());
+ // redact both NETWORK_SETTINGS & location sensitive fields.
+ verify(transportInfo).makeCopy(
+ REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_NETWORK_SETTINGS);
+ }
+
+ /**
+ * Test TransportInfo to verify redaction mechanism.
+ */
+ private static class TestTransportInfo implements TransportInfo {
+ public final boolean locationRedacted;
+ public final boolean localMacAddressRedacted;
+ public final boolean settingsRedacted;
+
+ TestTransportInfo() {
+ locationRedacted = false;
+ localMacAddressRedacted = false;
+ settingsRedacted = false;
+ }
+
+ TestTransportInfo(boolean locationRedacted, boolean localMacAddressRedacted,
+ boolean settingsRedacted) {
+ this.locationRedacted = locationRedacted;
+ this.localMacAddressRedacted =
+ localMacAddressRedacted;
+ this.settingsRedacted = settingsRedacted;
+ }
+
+ @Override
+ public TransportInfo makeCopy(@NetworkCapabilities.RedactionType long redactions) {
+ return new TestTransportInfo(
+ (redactions & REDACT_FOR_ACCESS_FINE_LOCATION) != 0,
+ (redactions & REDACT_FOR_LOCAL_MAC_ADDRESS) != 0,
+ (redactions & REDACT_FOR_NETWORK_SETTINGS) != 0
+ );
+ }
+
+ @Override
+ public @NetworkCapabilities.RedactionType long getApplicableRedactions() {
+ return REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_LOCAL_MAC_ADDRESS
+ | REDACT_FOR_NETWORK_SETTINGS;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof TestTransportInfo)) return false;
+ TestTransportInfo that = (TestTransportInfo) other;
+ return that.locationRedacted == this.locationRedacted
+ && that.localMacAddressRedacted == this.localMacAddressRedacted
+ && that.settingsRedacted == this.settingsRedacted;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(locationRedacted, localMacAddressRedacted, settingsRedacted);
+ }
+ }
+
+ private void verifyNetworkCallbackLocationDataInclusionUsingTransportInfoAndOwnerUidInNetCaps(
+ @NonNull TestNetworkCallback wifiNetworkCallback, int actualOwnerUid,
+ @NonNull TransportInfo actualTransportInfo, int expectedOwnerUid,
+ @NonNull TransportInfo expectedTransportInfo) throws Exception {
+ when(mPackageManager.getTargetSdkVersion(anyString())).thenReturn(Build.VERSION_CODES.S);
+ final NetworkCapabilities ncTemplate =
+ new NetworkCapabilities()
+ .addTransportType(TRANSPORT_WIFI)
+ .setOwnerUid(actualOwnerUid);
+
+ final NetworkRequest wifiRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_WIFI).build();
+ mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback);
+
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, new LinkProperties(),
+ ncTemplate);
+ mWiFiNetworkAgent.connect(false);
+
+ wifiNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+
+ // Send network capabilities update with TransportInfo to trigger capabilities changed
+ // callback.
+ mWiFiNetworkAgent.setNetworkCapabilities(
+ ncTemplate.setTransportInfo(actualTransportInfo), true);
+
+ wifiNetworkCallback.expectCapabilitiesThat(mWiFiNetworkAgent,
+ nc -> Objects.equals(expectedOwnerUid, nc.getOwnerUid())
+ && Objects.equals(expectedTransportInfo, nc.getTransportInfo()));
+
+ }
+
+ @Test
+ public void testVerifyLocationDataIsNotIncludedWhenInclFlagNotSet() throws Exception {
+ final TestNetworkCallback wifiNetworkCallack = new TestNetworkCallback();
+ final int ownerUid = Process.myUid();
+ final TransportInfo transportInfo = new TestTransportInfo();
+ // Even though the test uid holds privileged permissions, mask location fields since
+ // the callback did not explicitly opt-in to get location data.
+ final TransportInfo sanitizedTransportInfo = new TestTransportInfo(
+ true, /* locationRedacted */
+ true, /* localMacAddressRedacted */
+ true /* settingsRedacted */
+ );
+ // Should not expect location data since the callback does not set the flag for including
+ // location data.
+ verifyNetworkCallbackLocationDataInclusionUsingTransportInfoAndOwnerUidInNetCaps(
+ wifiNetworkCallack, ownerUid, transportInfo, INVALID_UID, sanitizedTransportInfo);
+ }
+
private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
throws Exception {
final Set<UidRange> vpnRange = Collections.singleton(PRIMARY_UIDRANGE);
@@ -9557,11 +9758,14 @@
exemptUidCaptor.capture());
assertContainsExactly(exemptUidCaptor.getValue(), Process.VPN_UID, exemptUid);
+ final int expectedNetId = mMockVpn.getNetwork() == null ? NETID_UNSET
+ : mMockVpn.getNetwork().getNetId();
+
if (add) {
- inOrder.verify(mMockNetd, times(1)).networkAddUidRanges(eq(mMockVpn.getNetId()),
+ inOrder.verify(mMockNetd, times(1)).networkAddUidRanges(eq(expectedNetId),
eq(toUidRangeStableParcels(vpnRanges)));
} else {
- inOrder.verify(mMockNetd, times(1)).networkRemoveUidRanges(eq(mMockVpn.getNetId()),
+ inOrder.verify(mMockNetd, times(1)).networkRemoveUidRanges(eq(expectedNetId),
eq(toUidRangeStableParcels(vpnRanges)));
}
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
index 9ab60a4..116d755e 100644
--- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -32,6 +32,7 @@
import android.content.Context;
import android.content.res.Resources;
import android.net.ConnectivityManager;
+import android.net.ConnectivityResources;
import android.net.IDnsResolver;
import android.net.INetd;
import android.net.LinkProperties;
@@ -47,10 +48,11 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.R;
+import com.android.connectivity.resources.R;
import com.android.server.ConnectivityService;
import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -84,10 +86,16 @@
MockitoAnnotations.initMocks(this);
when(mCtx.getResources()).thenReturn(mResources);
when(mCtx.getPackageName()).thenReturn("com.android.server.connectivity");
+ ConnectivityResources.setResourcesContextForTest(mCtx);
mMonitor = new TestableLingerMonitor(mCtx, mNotifier, HIGH_DAILY_LIMIT, HIGH_RATE_LIMIT);
}
+ @After
+ public void tearDown() {
+ ConnectivityResources.setResourcesContextForTest(null);
+ }
+
@Test
public void testTransitions() {
setNotificationSwitch(transition(WIFI, CELLULAR));
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 814cad4..a9d5822 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -32,9 +32,8 @@
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.argThat;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
@@ -656,7 +655,7 @@
.setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID));
} else if (transport == TRANSPORT_WIFI) {
WifiInfo wifiInfo = mock(WifiInfo.class);
- when(wifiInfo.makeCopy(anyBoolean())).thenReturn(wifiInfo);
+ when(wifiInfo.makeCopy(anyLong())).thenReturn(wifiInfo);
when(mMockDeps.getSubIdForWifiInfo(eq(wifiInfo))).thenReturn(TEST_SUBSCRIPTION_ID);
ncBuilder