diff options
250 files changed, 6124 insertions, 2874 deletions
diff --git a/Android.bp b/Android.bp index ee5e992935ae..f47ee20ed7b4 100644 --- a/Android.bp +++ b/Android.bp @@ -401,6 +401,13 @@ filegroup { ":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 @@ java_library { "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 999860fdf4da..e65abcfba3e4 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 @@ public class BlobStoreIdleJobService extends JobService { @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 0d3e0016fca0..60f64757f65a 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.Parcelable; 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 @@ public class JobParameters implements Parcelable { */ // 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 @@ public class JobParameters implements Parcelable { } /** @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 @@ public class JobParameters implements Parcelable { 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 @@ public class JobParameters implements Parcelable { } /** - * 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 @@ public class JobParameters implements Parcelable { } 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 @@ public class JobParameters implements Parcelable { } 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 0f3d299291c5..fa7a2d362ffa 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 @@ public abstract class JobService extends Service { * 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 88f21a53f930..63b3959eeafd 100644 --- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java +++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java @@ -40,9 +40,9 @@ import java.util.List; /** * 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 @@ public class PowerExemptionManager { * * @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 @@ public class PowerExemptionManager { * @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 @@ public class PowerExemptionManager { */ @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 @@ public class PowerExemptionManager { @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 eba39c7573be..07231b099815 100644 --- a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java +++ b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java @@ -439,12 +439,12 @@ public class PowerWhitelistManager { * 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 @@ public class PowerWhitelistManager { * @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 @@ public class PowerWhitelistManager { * @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 @@ public class PowerWhitelistManager { * 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 @@ public class PowerWhitelistManager { * 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 7833a037463c..6ae91a0917a4 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 @@ package com.android.server.job; 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 @@ public interface JobSchedulerInternal { /** * 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 b958c3f694bf..325be1b5c3a5 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 @@ class JobConcurrencyManager { 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 @@ class JobConcurrencyManager { int[] preferredUidForContext = mRecycledPreferredUidForContext; int[] workTypeForContext = mRecycledWorkTypeForContext; String[] preemptReasonForContext = mRecycledPreemptReasonForContext; + int[] preemptReasonCodeForContext = mRecycledPreemptReasonCodeForContext; String[] shouldStopJobReason = mRecycledShouldStopJobReason; updateCounterConfigLocked(); @@ -528,6 +531,7 @@ class JobConcurrencyManager { 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 @@ class JobConcurrencyManager { 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 @@ class JobConcurrencyManager { // 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 @@ class JobConcurrencyManager { 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 @@ class JobConcurrencyManager { if (selectedContextId != -1) { contextIdToJobMap[selectedContextId] = nextPending; slotChanged[selectedContextId] = true; + preemptReasonCodeForContext[selectedContextId] = preemptReasonCode; preemptReasonForContext[selectedContextId] = preemptReason; } if (startingJob) { @@ -631,8 +639,13 @@ class JobConcurrencyManager { } // 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 @@ class JobConcurrencyManager { 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 6ffac91d7098..02f912919878 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 @@ public final class JobPackageTracker { 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 @@ public final class JobPackageTracker { 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 2b08ba554404..a041f8c0b512 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService } } - 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService * 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService * 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 @@ public class JobSchedulerService extends com.android.server.SystemService * {@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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService @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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService } @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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 9ef46df7dac5..e8bcbfbb2c58 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 @@ public final class JobServiceContext implements ServiceConnection { /** 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 @@ public final class JobServiceContext implements ServiceConnection { && (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 @@ public final class JobServiceContext implements ServiceConnection { } @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 @@ public final class JobServiceContext implements ServiceConnection { } 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 @@ public final class JobServiceContext implements ServiceConnection { // 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 @@ public final class JobServiceContext implements ServiceConnection { 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 @@ public final class JobServiceContext implements ServiceConnection { } 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 @@ public final class JobServiceContext implements ServiceConnection { completedJob.hasContentTriggerConstraint()); try { mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(), mRunningJob.getSourceUid(), - stopReason); + legacyStopReason); } catch (RemoteException e) { // Whatever. } @@ -879,7 +887,7 @@ public final class JobServiceContext implements ServiceConnection { 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 a230b23f03a4..548a1ac14391 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 @@ public final class BackgroundJobsController extends StateController { 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 bad8dc1ad1cb..659cfa715352 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 static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; 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 @@ public final class JobStatus { */ 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 @@ public final class JobStatus { /** 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 @@ public final class JobStatus { 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 @@ public final class JobStatus { } /** @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 @@ public final class JobStatus { "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 @@ public final class JobStatus { 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 @@ public final class JobStatus { * 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 ac59f9542e99..2962b1017315 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 @@ import com.android.server.job.controllers.JobStatus; /** * 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 @@ public abstract class JobRestriction { 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 class JobRestriction { 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 954a5b8bdaa8..8b699e9b04c3 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 @@ public class ThermalStatusRestriction extends JobRestriction { 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 7e2edd6af0b6..c18e122c2a1a 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -1729,42 +1729,66 @@ package android { 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 @@ package android.app { 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 @@ package android.app { 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 @@ package android.app.job { 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 @@ package android.app.job { 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 @@ package android.bluetooth { 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 @@ -39159,7 +39203,7 @@ package android.telecom { 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(); @@ -39254,6 +39298,7 @@ package android.telecom { 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); @@ -42099,7 +42144,7 @@ package android.telephony { 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); @@ -42112,7 +42157,7 @@ package android.telephony { 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); @@ -42335,6 +42380,7 @@ package android.telephony { 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(); @@ -42371,6 +42417,7 @@ package android.telephony { 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 3eac6a461cfb..1b3edcf28d65 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -1192,12 +1192,14 @@ package android.app.backup { 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; } @@ -2140,6 +2142,17 @@ package android.bluetooth.le { 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 @@ -8257,9 +8270,10 @@ package android.os { 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 @@ -11058,7 +11072,8 @@ package android.telephony { 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; } @@ -11103,14 +11118,16 @@ package android.telephony { 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 } @@ -11200,6 +11217,29 @@ package android.telephony { 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); @@ -11865,6 +11905,7 @@ package android.telephony { 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 @@ -12026,6 +12067,16 @@ package android.telephony { 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 { @@ -13243,6 +13294,7 @@ package android.telephony.ims { 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); @@ -13257,6 +13309,14 @@ package android.telephony.ims { 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); @@ -13550,7 +13610,7 @@ package android.telephony.ims.feature { 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; } @@ -13747,7 +13807,7 @@ package android.telephony.ims.stub { 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 a34982699d8c..ae1cbf77dd8a 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -29,7 +29,6 @@ package android { 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"; diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index a1dce77c9191..006b2b5161d3 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -1606,7 +1606,8 @@ public class ActivityOptions { /** * 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 146d648fe65a..618eda8c84e8 100644 --- a/core/java/android/app/Application.java +++ b/core/java/android/app/Application.java @@ -22,6 +22,7 @@ import android.annotation.Nullable; 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 @@ import java.util.ArrayList; 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 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 { @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 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 { } } - 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 3bfddf7db015..e5a969ac98ad 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -244,6 +244,8 @@ interface IActivityTaskManager { 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 f4e214c540ac..5dd10f10930b 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1207,9 +1207,16 @@ public class Notification implements Parcelable 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 +2675,14 @@ public class Notification implements Parcelable } } + 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 +2698,19 @@ public class Notification implements Parcelable 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 +2729,12 @@ public class Notification implements Parcelable 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 +2747,7 @@ public class Notification implements Parcelable visitor.accept(message.getDataUri()); Person senderPerson = message.getSenderPerson(); - if (senderPerson != null && senderPerson.getIconUri() != null) { + if (senderPerson != null) { visitor.accept(senderPerson.getIconUri()); } } @@ -2735,19 +2760,15 @@ public class Notification implements Parcelable 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()); } } @@ -7231,7 +7252,7 @@ public class Notification implements Parcelable * @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 +7292,7 @@ public class Notification implements Parcelable * Set the content description of the big picture. */ @NonNull - public BigPictureStyle bigPictureContentDescription( + public BigPictureStyle setContentDescription( @Nullable CharSequence contentDescription) { mPictureContentDescription = contentDescription; return this; @@ -7280,8 +7301,12 @@ public class Notification implements Parcelable /** * @hide */ - public Bitmap getBigPicture() { - return mPicture; + @Nullable + public Icon getBigPicture() { + if (mPictureIcon != null) { + return mPictureIcon; + } + return null; } /** @@ -7289,7 +7314,16 @@ public class Notification implements Parcelable */ @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 +7365,8 @@ public class Notification implements Parcelable @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 +7381,14 @@ public class Notification implements Parcelable 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 +7403,12 @@ public class Notification implements Parcelable */ @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 +7433,12 @@ public class Notification implements Parcelable */ @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 +7495,7 @@ public class Notification implements Parcelable 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 +7518,17 @@ public class Notification implements Parcelable 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 +7549,16 @@ public class Notification implements Parcelable } 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 +7580,32 @@ public class Notification implements Parcelable 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; } } diff --git a/core/java/android/app/WindowContext.java b/core/java/android/app/WindowContext.java index cbe2995f2467..d44918cf0379 100644 --- a/core/java/android/app/WindowContext.java +++ b/core/java/android/app/WindowContext.java @@ -20,8 +20,11 @@ import static android.view.WindowManagerImpl.createWindowContextWindowManager; 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 @@ public class WindowContext extends ContextWrapper { 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 @@ public class WindowContext extends ContextWrapper { } 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 2298e84d755e..82cef072ad0f 100644 --- a/core/java/android/app/WindowTokenClient.java +++ b/core/java/android/app/WindowTokenClient.java @@ -61,7 +61,7 @@ public class WindowTokenClient extends IWindowToken.Stub { @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 @@ public class WindowTokenClient extends IWindowToken.Stub { 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 675934629f2d..51430c059768 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 @@ public class RestoreSet implements Parcelable { * 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 class RestoreSet implements Parcelable { */ 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; + } + + /** + * 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); } - 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. + * @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 @@ public class RestoreSet implements Parcelable { 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 @@ public class RestoreSet implements Parcelable { name = in.readString(); device = in.readString(); token = in.readLong(); + backupTransportFlags = in.readInt(); } } diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index a6b4b47f0db2..82da4fbf5f7e 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -841,8 +841,8 @@ public class AppWidgetHostView extends FrameLayout { * 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 8d4157259ff7..a3d19ca6425c 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -52,6 +52,8 @@ import android.os.SystemProperties; 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 @@ public final class BluetoothAdapter { 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 07dbdce3984f..89086497a446 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -1022,6 +1022,24 @@ public final class BluetoothDevice implements Parcelable { 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 @@ public final class BluetoothDevice implements Parcelable { private static volatile IBluetooth sService; private final String mAddress; + @AddressType private final int mAddressType; /*package*/ @UnsupportedAppUsage @@ -1084,6 +1103,7 @@ public final class BluetoothDevice implements Parcelable { } 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 51f63f7fb9fa..7459f871dba7 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 @@ public final class ScanFilter implements Parcelable { @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 @@ public final class ScanFilter implements Parcelable { /** @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 @@ public final class ScanFilter implements Parcelable { mManufacturerId = manufacturerId; mManufacturerData = manufacturerData; mManufacturerDataMask = manufacturerDataMask; + mAddressType = addressType; + mIrk = irk; } @Override @@ -280,6 +291,23 @@ public final class ScanFilter implements Parcelable { 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 final class ScanFilter implements Parcelable { */ 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 @@ public final class ScanFilter implements Parcelable { * * @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 @@ public final class ScanFilter implements Parcelable { mServiceUuid, mUuidMask, mServiceSolicitationUuid, mServiceSolicitationUuidMask, mServiceDataUuid, mServiceData, mServiceDataMask, - mManufacturerId, mManufacturerData, mManufacturerDataMask); + mManufacturerId, mManufacturerData, mManufacturerDataMask, + mAddressType, mIrk); } } } diff --git a/core/java/android/content/ComponentCallbacksController.java b/core/java/android/content/ComponentCallbacksController.java new file mode 100644 index 000000000000..a81aaf78bdb9 --- /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 64ca92fa8132..92ff640e33b0 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -655,12 +655,21 @@ public abstract class Context { /** * 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 @@ public abstract class Context { * 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 a2880dfdfd17..614143e7c04d 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 55e19f2727bf..d187f60ecd44 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 @@ public final class DomainVerificationManager { } /** - * 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 3da1227f1bac..96e7d3bc75c0 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -326,6 +326,20 @@ public class NetworkPolicyManager { * @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 fa6472ee4a79..4674aa2d120e 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -6030,7 +6030,7 @@ public abstract class BatteryStats implements Parcelable { 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 e68f330a2129..7e404972d11a 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -358,6 +358,38 @@ public final class DeviceConfig { 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 @@ public final class DeviceConfig { */ @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 @@ public final class DeviceConfig { "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 diff --git a/core/java/android/service/notification/ScheduleCalendar.java b/core/java/android/service/notification/ScheduleCalendar.java index 6ed966e0bbbd..314c97db4e7b 100644 --- a/core/java/android/service/notification/ScheduleCalendar.java +++ b/core/java/android/service/notification/ScheduleCalendar.java @@ -57,25 +57,18 @@ public class ScheduleCalendar { } /** - * 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 d3eaeae993e6..e5106e2b0ce7 100644 --- a/core/java/android/util/Slog.java +++ b/core/java/android/util/Slog.java @@ -26,6 +26,10 @@ import java.util.Formatter; 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 000000000000..dc93a473fe44 --- /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/InputEventAssigner.java b/core/java/android/view/InputEventAssigner.java index c159a127f4eb..7fac6c5e4af6 100644 --- a/core/java/android/view/InputEventAssigner.java +++ b/core/java/android/view/InputEventAssigner.java @@ -45,13 +45,13 @@ import static android.view.InputDevice.SOURCE_TOUCHSCREEN; 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 @@ public class InputEventAssigner { * @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/SurfaceView.java b/core/java/android/view/SurfaceView.java index 3ffe0c660f9a..f1eef9fad8a1 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -1296,7 +1296,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall 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 e4fb61107c4a..66ee23a199fe 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -330,7 +330,6 @@ public final class ViewRootImpl implements ViewParent, private boolean mUseBLASTAdapter; private boolean mForceDisableBLAST; - private boolean mEnableTripleBuffering; private boolean mFastScrollSoundEffectsEnabled; @@ -474,6 +473,7 @@ public final class ViewRootImpl implements ViewParent, FrameInfo frameInfo = mChoreographer.mFrameInfo; mViewFrameInfo.populateFrameInfo(frameInfo); mViewFrameInfo.reset(); + mInputEventAssigner.notifyFrameProcessed(); return frameInfo; } @@ -1179,9 +1179,6 @@ public final class ViewRootImpl implements ViewParent, 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 = @@ -1907,7 +1904,7 @@ public final class ViewRootImpl implements ViewParent, 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 +8499,6 @@ public final class ViewRootImpl implements ViewParent, 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/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index 47ac1ee5b339..18013e815d13 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -118,7 +118,6 @@ public final class WindowManagerGlobal { 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 37220fe6870b..c5bce28fcee1 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -608,7 +608,12 @@ public class BaseInputConnection implements InputConnection { 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 34a60bbe7642..38019c9a34f8 100644 --- a/core/java/android/view/inputmethod/InputConnection.java +++ b/core/java/android/view/inputmethod/InputConnection.java @@ -325,16 +325,16 @@ public interface InputConnection { 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/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java index 58dfc0df3573..34ad659d148c 100644 --- a/core/java/android/widget/EdgeEffect.java +++ b/core/java/android/widget/EdgeEffect.java @@ -640,6 +640,12 @@ public class EdgeEffect { 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 @@ public class EdgeEffect { * 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 319e78807684..cb2bba1846c5 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -5476,14 +5476,14 @@ public class RemoteViews implements Parcelable, Filter { /** * 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/com/android/internal/app/AppPredictionServiceResolverComparator.java b/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java index 26af61520593..b76ef0fd4ed8 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.Intent; 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 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator // 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 @@ class AppPredictionServiceResolverComparator extends AbstractResolverComparator 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 d4d853624700..862c9001e380 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -256,15 +256,6 @@ public class ChooserActivity extends ResolverActivity implements 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 @@ public class ChooserActivity extends ResolverActivity implements 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 @@ public class ChooserActivity extends ResolverActivity implements 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 @@ public class ChooserActivity extends ResolverActivity implements 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 @@ public class ChooserActivity extends ResolverActivity implements 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 @@ public class ChooserActivity extends ResolverActivity implements 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 @@ public class ChooserActivity extends ResolverActivity implements 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 @@ public class ChooserActivity extends ResolverActivity implements } private void sendImpressionToAppPredictor(TargetInfo targetInfo, ChooserListAdapter adapter) { - if (!mChooserTargetRankingEnabled) { - return; - } AppPredictor directShareAppPredictor = getAppPredictorForDirectShareIfEnabled( mChooserMultiProfilePagerAdapter.getCurrentUserHandle()); if (directShareAppPredictor == null) { @@ -2399,11 +2350,6 @@ public class ChooserActivity extends ResolverActivity implements 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 @@ public class ChooserActivity extends ResolverActivity implements 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 @@ public class ChooserActivity extends ResolverActivity implements // 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 570066807f16..cc2b12a99d79 100644 --- a/core/java/com/android/internal/app/ChooserListAdapter.java +++ b/core/java/com/android/internal/app/ChooserListAdapter.java @@ -21,7 +21,6 @@ import static com.android.internal.app.ChooserActivity.TARGET_TYPE_SHORTCUTS_FRO 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.os.UserManager; 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 com.android.internal.config.sysui.SystemUiDeviceConfigFlags; 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 @@ public class ChooserListAdapter extends ResolverListAdapter { 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 @@ public class ChooserListAdapter extends ResolverListAdapter { 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 @@ public class ChooserListAdapter extends ResolverListAdapter { 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 @@ public class ChooserListAdapter extends ResolverListAdapter { void refreshListView() { if (mListViewDataChanged) { - if (mAppendDirectShareEnabled) { - appendServiceTargetsWithQuota(); - } super.notifyDataSetChanged(); } mListViewDataChanged = false; @@ -221,10 +204,6 @@ public class ChooserListAdapter extends ResolverListAdapter { 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 @@ public class ChooserListAdapter extends ResolverListAdapter { + 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 @@ public class ChooserListAdapter extends ResolverListAdapter { Log.d(TAG, " => " + target.toString() + " score=" + targetScore + " base=" + target.getScore() + " lastScore=" + lastScore - + " baseScore=" + baseScore); + + " baseScore=" + baseScore + + " applyAppLimit=" + mApplySharingAppLimits); } lastScore = targetScore; @@ -573,217 +550,13 @@ public class ChooserListAdapter extends ResolverListAdapter { } /** - * 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 @@ public class ChooserListAdapter extends ResolverListAdapter { 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 class ChooserListAdapter extends ResolverListAdapter { */ 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 52801faf9c36..02cbccc77c3f 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -415,6 +415,12 @@ public final class SystemUiDeviceConfigFlags { 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 +479,14 @@ public final class SystemUiDeviceConfigFlags { */ 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 7f8788529714..a043756ca262 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.PrintWriterPrinter; 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 @@ public class BatteryStatsImpl extends BatteryStats { } /** - * 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/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp index b790056fb58e..b46b5a23e3fb 100644 --- a/core/jni/android_graphics_BLASTBufferQueue.cpp +++ b/core/jni/android_graphics_BLASTBufferQueue.cpp @@ -68,7 +68,7 @@ private: }; 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 @@ static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring jName, jlong surfac 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 void nativeSetTransactionCompleteCallback(JNIEnv* env, jclass clazz, jlon 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/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 29d5e7bbfcd1..8d7f5425ea62 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. diff --git a/core/res/remote_color_resources_res/values/colors.xml b/core/res/remote_color_resources_res/values/colors.xml index 295f16e959e6..e4bcae43a0e5 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 e628f09b8327..9616628ac693 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 90d6b07b24bd..5926fde42c20 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 bdc4fa92b2f2..1379523258de 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 799636addd4b..f79fd6242d54 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 4793bb8e0360..c58f565b568b 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 c82863109e7d..63fdc81f73f6 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 82c420ad97fc..1ad6f6a791d4 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/values/colors.xml b/core/res/res/values/colors.xml index 22467e4c1402..91896febc571 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 9b5632174e44..3fbd7caf4875 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/public.xml b/core/res/res/values/public.xml index 3e5fad8a34b4..7694fafaa6fb 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/tests/coretests/src/android/content/ComponentCallbacksControllerTest.java b/core/tests/coretests/src/android/content/ComponentCallbacksControllerTest.java new file mode 100644 index 000000000000..09985a8bee4b --- /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 000000000000..2dd3f69852c1 --- /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/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java index d9012f649dd3..4c58ad3d7f8d 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 @@ public class ChooserActivityTest { 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 @@ public class ChooserActivityTest { 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 @@ public class ChooserActivityTest { .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 ac16af348471..abde232a8a5a 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 3aaf11cef17c..4534d36342db 100644 --- a/graphics/java/android/graphics/BLASTBufferQueue.java +++ b/graphics/java/android/graphics/BLASTBufferQueue.java @@ -28,7 +28,7 @@ public final class BLASTBufferQueue { 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 @@ public final class BLASTBufferQueue { /** 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 d2b3cf6a4fe2..6bd0e0a4ff86 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 4768cf58152b..fa94ec557883 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 final class OneHandedSettingsUtil { */ 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/hwui/Readback.cpp b/libs/hwui/Readback.cpp index b71bb07dbc86..145526996678 100644 --- a/libs/hwui/Readback.cpp +++ b/libs/hwui/Readback.cpp @@ -120,12 +120,6 @@ CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, Matrix4& texTran 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 @@ CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, Matrix4& texTran 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 85a083ee84bc..ade0ea40e157 100644 --- a/location/java/android/location/util/identity/CallerIdentity.java +++ b/location/java/android/location/util/identity/CallerIdentity.java @@ -55,6 +55,20 @@ public final class CallerIdentity { } /** + * 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 @@ public final class CallerIdentity { } } - /** - * 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 3cd615b658b2..9774e8032c95 100644 --- a/media/java/android/media/AudioProfile.java +++ b/media/java/android/media/AudioProfile.java @@ -18,6 +18,9 @@ package android.media; 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 class AudioProfile { 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 3de78bb9ef9f..644afb79814f 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -99,9 +99,7 @@ import java.util.concurrent.Executor; /** - * MediaPlayer class can be used to control playback - * of audio/video files and streams. An example on how to use the methods in - * this class can be found in {@link android.widget.VideoView}. + * MediaPlayer class can be used to control playback of audio/video files and streams. * * <p>MediaPlayer is not thread-safe. Creation of and all access to player instances * should be on the same thread. If registering <a href="#Callbacks">callbacks</a>, diff --git a/packages/Connectivity/TEST_MAPPING b/packages/Connectivity/TEST_MAPPING new file mode 100644 index 000000000000..94f9232bc482 --- /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 e415e01fea3a..ad44b27f6d0b 100644 --- a/packages/Connectivity/framework/api/current.txt +++ b/packages/Connectivity/framework/api/current.txt @@ -396,6 +396,7 @@ package android.net { 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 9ca6d8fedc68..b179c6da294d 100644 --- a/packages/Connectivity/framework/api/module-lib-current.txt +++ b/packages/Connectivity/framework/api/module-lib-current.txt @@ -45,6 +45,7 @@ package android.net { public final class NetworkCapabilities implements android.os.Parcelable { ctor public NetworkCapabilities(@Nullable android.net.NetworkCapabilities, long); method @Nullable public java.util.Set<android.util.Range<java.lang.Integer>> getUids(); + method public boolean hasUnwantedCapability(int); field public static final long REDACT_ALL = -1L; // 0xffffffffffffffffL field public static final long REDACT_FOR_ACCESS_FINE_LOCATION = 1L; // 0x1L field public static final long REDACT_FOR_LOCAL_MAC_ADDRESS = 2L; // 0x2L @@ -57,7 +58,13 @@ package android.net { method @NonNull public android.net.NetworkCapabilities.Builder setUids(@Nullable java.util.Set<android.util.Range<java.lang.Integer>>); } + public class NetworkRequest implements android.os.Parcelable { + method public boolean hasUnwantedCapability(int); + } + public static class NetworkRequest.Builder { + method @NonNull public android.net.NetworkRequest.Builder addUnwantedCapability(int); + method @NonNull public android.net.NetworkRequest.Builder removeUnwantedCapability(int); method @NonNull public android.net.NetworkRequest.Builder setUids(@Nullable java.util.Set<android.util.Range<java.lang.Integer>>); } diff --git a/packages/Connectivity/framework/api/system-current.txt b/packages/Connectivity/framework/api/system-current.txt index 535ba384fad0..c19fcdd2a728 100644 --- a/packages/Connectivity/framework/api/system-current.txt +++ b/packages/Connectivity/framework/api/system-current.txt @@ -52,7 +52,7 @@ package android.net { 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); diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java index ebedfe9ccb78..f2078307a59a 100644 --- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java +++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java @@ -4944,20 +4944,20 @@ public class ConnectivityManager { * {@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/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java index c9c0940dfdf5..881fa8c2702c 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java +++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java @@ -639,19 +639,31 @@ public final class NetworkCapabilities implements Parcelable { } /** - * Removes (if found) the given capability from this {@code NetworkCapability} instance. + * Removes (if found) the given capability from this {@code NetworkCapability} + * instance that were added via addCapability(int) or setCapabilities(int[], int[]). * * @param capability the capability to be removed. * @return This NetworkCapabilities instance, to facilitate chaining. * @hide */ public @NonNull NetworkCapabilities removeCapability(@NetCapability int capability) { - // Note that this method removes capabilities that were added via addCapability(int), - // addUnwantedCapability(int) or setCapabilities(int[], int[]). checkValidCapability(capability); final long mask = ~(1 << capability); mNetworkCapabilities &= mask; - mUnwantedNetworkCapabilities &= mask; + return this; + } + + /** + * Removes (if found) the given unwanted capability from this {@code NetworkCapability} + * instance that were added via addUnwantedCapability(int) or setCapabilities(int[], int[]). + * + * @param capability the capability to be removed. + * @return This NetworkCapabilities instance, to facilitate chaining. + * @hide + */ + public @NonNull NetworkCapabilities removeUnwantedCapability(@NetCapability int capability) { + checkValidCapability(capability); + mUnwantedNetworkCapabilities &= ~(1 << capability); return this; } @@ -723,6 +735,7 @@ public final class NetworkCapabilities implements Parcelable { } /** @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public boolean hasUnwantedCapability(@NetCapability int capability) { return isValidCapability(capability) && ((mUnwantedNetworkCapabilities & (1 << capability)) != 0); @@ -736,10 +749,16 @@ public final class NetworkCapabilities implements Parcelable { return ((mNetworkCapabilities & CONNECTIVITY_MANAGED_CAPABILITIES) != 0); } - /** Note this method may result in having the same capability in wanted and unwanted lists. */ private void combineNetCapabilities(@NonNull NetworkCapabilities nc) { - this.mNetworkCapabilities |= nc.mNetworkCapabilities; - this.mUnwantedNetworkCapabilities |= nc.mUnwantedNetworkCapabilities; + final long wantedCaps = this.mNetworkCapabilities | nc.mNetworkCapabilities; + final long unwantedCaps = + this.mUnwantedNetworkCapabilities | nc.mUnwantedNetworkCapabilities; + if ((wantedCaps & unwantedCaps) != 0) { + throw new IllegalArgumentException( + "Cannot have the same capability in wanted and unwanted lists."); + } + this.mNetworkCapabilities = wantedCaps; + this.mUnwantedNetworkCapabilities = unwantedCaps; } /** diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java index cf131f0df607..bcbc04f72efc 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java +++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java @@ -216,6 +216,14 @@ public class NetworkRequest implements Parcelable { } /** + * 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 @@ public class NetworkRequest implements Parcelable { * * @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 @@ public class NetworkRequest implements Parcelable { * * @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 c0f262815b0c..c4bebc0a982e 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkUtils.java +++ b/packages/Connectivity/framework/src/android/net/NetworkUtils.java @@ -323,22 +323,7 @@ public class NetworkUtils { */ @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/service/Android.bp b/packages/Connectivity/service/Android.bp index f630ceac3662..1330e719e774 100644 --- a/packages/Connectivity/service/Android.bp +++ b/packages/Connectivity/service/Android.bp @@ -68,6 +68,7 @@ java_library { "net-utils-framework-common", "netd-client", "PlatformProperties", + "service-connectivity-protos", ], apex_available: [ "//apex_available:platform", @@ -76,6 +77,21 @@ java_library { } 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/jarjar-rules.txt b/packages/Connectivity/service/jarjar-rules.txt index a7b419b020b5..5caa11b11ae4 100644 --- a/packages/Connectivity/service/jarjar-rules.txt +++ b/packages/Connectivity/service/jarjar-rules.txt @@ -12,3 +12,6 @@ rule android.util.LocalLog* com.android.connectivity.util.LocalLog@1 # 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/packages/Connectivity/service/proto/connectivityproto.proto b/packages/Connectivity/service/proto/connectivityproto.proto new file mode 100644 index 000000000000..a992d7c26368 --- /dev/null +++ b/packages/Connectivity/service/proto/connectivityproto.proto @@ -0,0 +1,20 @@ +/* + * 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. + */ + +syntax = "proto2"; + +// 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 d3e4d1cabee3..1c112c65fe3e 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 a0bf4b16f482..765f31cada4a 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 a18c0f15cbc3..b5da6bee457e 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 fc788b669b31..a351ca00d000 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 90df3b13be9f..23baeb925aff 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 e2b62a43224a..6c6a5d946e42 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 2b351bcbe9a0..7ec137aa4adf 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 b5d064e81b84..86b34f544bd5 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 a3a490deff50..e5e03e10a420 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 f1d4a8a087e8..9ccfb80c20d7 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 95ba023e1ca0..f667c6a5a7e7 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 0b03d99c90f6..4c35d2fe75c0 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/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml index 5d4078d3419b..fbb84fdfcedc 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 b86ae6d199d7..2b4fef0c9ba7 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 19bcf95922c2..6d44138c5354 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_tile_large_empty.xml b/packages/SystemUI/res/layout/people_tile_large_empty.xml new file mode 100644 index 000000000000..1e00307bfc23 --- /dev/null +++ b/packages/SystemUI/res/layout/people_tile_large_empty.xml @@ -0,0 +1,67 @@ +<?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. + --> +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <LinearLayout + android:id="@+id/item" + android:layout_width="match_parent" + 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="16dp" + android:paddingVertical="16dp"> + + <ImageView + android:id="@+id/person_icon" + android:layout_gravity="center" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <TextView + android:id="@+id/name" + android:layout_gravity="center" + android:paddingTop="14dp" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:ellipsize="end" + android:singleLine="true" + android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem" + android:textColor="?android:attr/textColorPrimary" + 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 000000000000..f2341b55488f --- /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 index b1a37bf71beb..c35787efec5d 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 index 6a606e10e3fb..e4e4cd8956a7 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 index 7b1abaed28ca..914ee3c089d4 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 0076c5132673..a773b2242eaa 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 b050945068f2..ff4e8e002d88 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 b1e14346c3fa..d52a25139ce7 100644 --- a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java +++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java @@ -22,8 +22,8 @@ import static android.telephony.PhoneStateListener.LISTEN_NONE; 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 @@ public class CarrierTextController { 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 bb4038e92ff4..b4858f42c25b 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 @@ package com.android.systemui.accessibility.floatingmenu; 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 @@ public class AccessibilityTargetAdapter extends Adapter<ViewHolder> { 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 d3168c8d2a05..a7c1451a1d1b 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.ActivityNotFoundException; 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.os.UserHandle; 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.app.IVoiceInteractionSessionShowCallback; 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 @@ public class AssistManager { // 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 @@ public class AssistManager { 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 @@ public class AssistManager { 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 @@ public class AssistManager { } protected boolean shouldShowOrb() { - return false; + return !ActivityManager.isLowRamDeviceStatic(); } public void startAssist(Bundle args) { @@ -293,10 +232,8 @@ public class AssistManager { 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 @@ public class AssistManager { 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 @@ public class AssistManager { 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 f78436a8c51a..fe48c26c413c 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 @@ public class AssistOrbContainer extends FrameLayout { 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 class AssistOrbContainer extends FrameLayout { 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 @@ public class AssistOrbContainer extends FrameLayout { mNavbarScrim.setAlpha(1f); } - private void startEnterAnimation() { + private void startEnterAnimation(Runnable onDone) { if (mAnimatingOut) { return; } @@ -106,7 +115,8 @@ public class AssistOrbContainer extends FrameLayout { .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 000000000000..81a13a236685 --- /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 08e5d562113d..1f652dbfdaf2 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 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud @NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager; private boolean mForceShow; + private boolean mQsExpanded; protected UdfpsKeyguardViewController( @NonNull UdfpsKeyguardView view, @@ -64,7 +65,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud protected void onViewDetached() { super.onViewDetached(); mStatusBarStateController.removeCallback(mStateListener); - mAlternateAuthInterceptor.reset(); + mAlternateAuthInterceptor.resetForceShow(); mKeyguardViewManager.setAlternateAuthInterceptor(null); } @@ -100,6 +101,11 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud if (mForceShow) { return false; } + + if (mQsExpanded) { + return true; + } + return super.shouldPauseAuth(); } @@ -130,7 +136,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud } @Override - public boolean reset() { + public boolean resetForceShow() { if (!mForceShow) { return false; } @@ -140,7 +146,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud } @Override - public boolean isShowingAlternativeAuth() { + public boolean isShowingAlternateAuth() { return mForceShow; } @@ -150,6 +156,12 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud } @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 461a7303c184..553e5a7c6d7d 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.DialogInterface; 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 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, 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 f9c2a2aed751..93ce5a83f684 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 @@ package com.android.systemui.people; 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.provider.ContactsContract; 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.internal.util.ArrayUtils; 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.List; 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 class PeopleSpaceUtils { 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 @@ public class PeopleSpaceUtils { .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 class PeopleSpaceUtils { 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 @@ public class PeopleSpaceUtils { 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 000000000000..ae81ab04ec10 --- /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 eedcdab68b9f..b1689f665ebb 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.privacy.logging.PrivacyLogger; 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 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader 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 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader 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 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader mUiEventLogger = uiEventLogger; mQSTileHost = qsTileHost; mStatusBarIconController = statusBarIconController; - mCommandQueue = commandQueue; mDemoModeController = demoModeController; mUserTracker = userTracker; mLifecycle = new LifecycleRegistry(mLifecycleOwner); @@ -238,7 +235,7 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader 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 1ec175c01be7..78ee89607570 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.Canvas; 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.AttributeSet; 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 @@ import com.android.systemui.R; */ 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 @@ public class CropView extends View { 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 @@ public class CropView extends View { Parcelable superState = super.onSaveInstanceState(); SavedState ss = new SavedState(superState); - ss.mTopBoundary = getTopBoundary(); - ss.mBottomBoundary = getBottomBoundary(); + ss.mCrop = mCrop; return ss; } @@ -110,45 +111,67 @@ public class CropView extends View { 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 @@ public class CropView extends View { 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 @@ public class CropView extends View { /** * 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 @@ public class CropView extends View { 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 @@ public class CropView extends View { } /** - * @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 @@ public class CropView extends View { /** * 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 @@ public class CropView extends View { @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 @@ public class CropView extends View { @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 @@ public class CropView extends View { @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 @@ public class CropView extends View { } 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 @@ public class CropView extends View { * 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 @@ public class CropView extends View { */ 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 6a004c2c01dc..01afacfad385 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 @@ public class LongScreenshotActivity extends Activity { 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 @@ public class LongScreenshotActivity extends Activity { 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 @@ public class LongScreenshotActivity extends Activity { 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 @@ public class LongScreenshotActivity extends Activity { } } - 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 @@ public class LongScreenshotActivity extends Activity { 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 90f304262ea1..08cd91ccada5 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 @@ public class MagnifierView extends View implements CropView.CropInteractionListe private float mCheckerboardBoxSize = 40; private float mLastCropPosition; + private float mLastCenter = 0.5f; private CropView.CropBoundary mCropBoundary; private ViewPropertyAnimator mTranslationAnimator; @@ -131,7 +132,7 @@ public class MagnifierView extends View implements CropView.CropInteractionListe 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 @@ public class MagnifierView extends View implements CropView.CropInteractionListe @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 5c650ad3982e..d15f1ff97e7e 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 @@ public class ScreenshotView extends FrameLayout implements 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 @@ public class ScreenshotView extends FrameLayout implements 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 @@ public class ScreenshotView extends FrameLayout implements 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 @@ public class ScreenshotView extends FrameLayout implements createScreenshotActionsShadeAnimation().start(); - SwipeDismissHandler swipeDismissHandler = new SwipeDismissHandler(); - mScreenshotPreview.setOnTouchListener(swipeDismissHandler); - mActionsContainer.setOnTouchListener(swipeDismissHandler); - mActionsContainerBackground.setOnTouchListener(swipeDismissHandler); - mBackgroundProtection.setOnTouchListener(swipeDismissHandler); + setOnTouchListener(mSwipeDismissHandler); } }); @@ -776,30 +800,16 @@ public class ScreenshotView extends FrameLayout implements } 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 @@ public class ScreenshotView extends FrameLayout implements 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 @@ public class ScreenshotView extends FrameLayout implements } 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 @@ public class ScreenshotView extends FrameLayout implements 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/OperatorNameView.java b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java index 2aba1038a97d..cc7a4f836c63 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 @@ package com.android.systemui.statusbar; 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 @@ public class OperatorNameView extends TextView implements DemoModeCommandReceive .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/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java index 39f5847ce2a6..562d0ec06a63 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.KeyguardStateController; 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 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue 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 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue 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 33798d680d05..2d760e6fc176 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.Dependency; 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 com.android.systemui.statusbar.policy.UserInfoControllerImpl; 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 @@ public class KeyguardStatusBarView extends RelativeLayout 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 @@ public class KeyguardStatusBarView extends RelativeLayout mStatusIconContainer = findViewById(R.id.statusIcons); loadDimens(); + loadBlockList(); mBatteryController = Dependency.get(BatteryController.class); } @@ -181,6 +184,14 @@ public class KeyguardStatusBarView extends RelativeLayout 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 @@ public class KeyguardStatusBarView extends RelativeLayout 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 4ef6668acfb9..415cfff3713b 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 @@ public class NotificationPanelViewController extends PanelViewController { 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 @@ public class NotificationPanelViewController extends PanelViewController { mNotificationContainerParent.setQsExpanded(expanded); mPulseExpansionHandler.setQsExpanded(expanded); mKeyguardBypassController.setQSExpanded(expanded); + mStatusBarKeyguardViewManager.setQsExpanded(expanded); } } @@ -3389,7 +3393,7 @@ public class NotificationPanelViewController extends PanelViewController { 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 @@ public class NotificationPanelViewController extends PanelViewController { @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 ed4f32492c9c..77abe792ee8e 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 @@ public abstract class PanelViewController { 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 0e1fe22b2454..c83b60d07aa6 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 @@ public class StatusBar extends SystemUI implements DemoMode, 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 8fe9a481ccf6..93b83d3cbcbd 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_ICON 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.R; 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.CallIndicatorI 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 @@ public interface StatusBarIconController { * 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; - - 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); + int TAG_PRIMARY = 0; + + /** */ + 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 @@ public interface StatusBarIconController { * 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 @@ public interface StatusBarIconController { /** * 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 interface StatusBarIconController { } } - 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 @@ public interface StatusBarIconController { 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 @@ public interface StatusBarIconController { 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 @@ public interface StatusBarIconController { 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 6404aea05a4d..75900a2bffa1 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 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu private Context mContext; + /** */ @Inject public StatusBarIconControllerImpl( Context context, @@ -84,6 +85,7 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu demoModeController.addCallback(this); } + /** */ @Override public void addIconGroup(IconManager group) { mIconGroups.add(group); @@ -101,12 +103,14 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu } } + /** */ @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 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu 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 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu * 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 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu setIcon(index, holder); } + /** */ @Override public void setIcon(int index, @NonNull StatusBarIconHolder holder) { boolean isNew = getIcon(index, holder.getTag()) == null; @@ -328,6 +335,7 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu handleSet(index, holder); } + /** */ @Override public void setIconAccessibilityLiveRegion(String slotName, int accessibilityLiveRegion) { Slot slot = getSlot(slotName); @@ -344,15 +352,18 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu } } + /** */ 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 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu } } + /** */ @Override public void removeIcon(int index, int tag) { if (getIcon(index, tag) == null) { @@ -384,6 +396,7 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu 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 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu super.dump(pw); } + /** */ @Override public void onDemoModeStarted() { for (IconManager manager : mIconGroups) { @@ -411,6 +425,7 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu } } + /** */ @Override public void onDemoModeFinished() { for (IconManager manager : mIconGroups) { @@ -420,6 +435,7 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu } } + /** */ @Override public void dispatchDemoCommand(String command, Bundle args) { for (IconManager manager : mIconGroups) { @@ -429,6 +445,7 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu } } + /** */ @Override public List<String> demoCommands() { List<String> s = new ArrayList<>(); @@ -436,6 +453,7 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu 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 19db02a71777..af342dd31a76 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 @@ public class StatusBarIconHolder { 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 @@ public class StatusBarIconHolder { 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 @@ public class StatusBarIconHolder { return holder; } + /** */ public static StatusBarIconHolder fromWifiIconState(WifiIconState state) { StatusBarIconHolder holder = new StatusBarIconHolder(); holder.mWifiState = state; @@ -63,6 +70,7 @@ public class StatusBarIconHolder { return holder; } + /** */ public static StatusBarIconHolder fromMobileIconState(MobileIconState state) { StatusBarIconHolder holder = new StatusBarIconHolder(); holder.mMobileState = state; @@ -75,7 +83,8 @@ public class StatusBarIconHolder { * 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 c1f300b49d24..7ee7aa4100d4 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 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb 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 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb * 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 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb 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 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb * 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 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb * @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 5dc91047770d..f2ee85886dca 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.BroadcastReceiver; 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 class EmergencyCryptkeeperText extends TextView { } 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 db039b44d91a..8a8602142363 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.FileDescriptor; 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 @@ public class NetworkControllerImpl extends BroadcastReceiver mSubscriptionManager = subManager; mSubDefaults = defaultsHandler; mConnectivityManager = connectivityManager; - mHasMobileDataFeature = - mConnectivityManager.isNetworkSupported(ConnectivityManager.TYPE_MOBILE); + mHasMobileDataFeature = telephonyManager.isDataCapable(); mDemoModeController = demoModeController; // telephony @@ -337,10 +337,19 @@ public class NetworkControllerImpl extends BroadcastReceiver // 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 @@ public class NetworkControllerImpl extends BroadcastReceiver 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 @@ public class NetworkControllerImpl extends BroadcastReceiver 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 bbb2f1a5259a..278663b270bc 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 @@ public class ThemeOverlayApplier implements Dumpable { "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 @@ public class ThemeOverlayApplier implements Dumpable { */ 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 @@ public class ThemeOverlayApplier implements Dumpable { @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 @@ public class ThemeOverlayApplier implements Dumpable { 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 f19228783b88..d317712ce87c 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 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { 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 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { 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 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { mainColor = Color.TRANSPARENT; accentCandidate = Color.TRANSPARENT; } else { - mainColor = getDominantColor(currentColors); + mainColor = getNeutralColor(currentColors); accentCandidate = getAccentColor(currentColors); } @@ -218,13 +214,12 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { 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 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { /** * 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 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { 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 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { 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 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { // 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 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { 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 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { 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 aa4122fd190a..6f2c0af5384d 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.reset; 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 @@ public class CarrierTextControllerTest extends SysuiTestCase { @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor; @Mock - private ConnectivityManager mConnectivityManager; + private PackageManager mPackageManager; @Mock private TelephonyManager mTelephonyManager; @Mock @@ -123,8 +123,8 @@ public class CarrierTextControllerTest extends SysuiTestCase { 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 65f0f7b87cf4..44109924f9f5 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 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { 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 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { 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 ee98a591c8a0..1c7a84a36404 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.NotificationChannel; 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.provider.ContactsContract; 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 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { 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 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { .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 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { .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 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { 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 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { 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 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { } @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 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { 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 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { 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 000000000000..39bf06050741 --- /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 4948c2b18746..3595095ba615 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.privacy.logging.PrivacyLogger 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 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() { @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 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() { uiEventLogger, qsTileHost, statusBarIconController, - commandQueue, demoModeController, userTracker, quickQSPanelController, @@ -233,4 +229,4 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() { `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/phone/StatusBarIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java index 7b7e2d3e34df..f147f1cec9ed 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 androidx.test.filters.SmallTest; 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 @@ public class StatusBarIconControllerTest extends LeakCheckedTest { @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 @@ public class StatusBarIconControllerTest extends LeakCheckedTest { 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 @@ public class StatusBarIconControllerTest extends LeakCheckedTest { } 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 999d2822c928..8f36415d60af 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.verify; 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.ServiceState; 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 @@ public class NetworkControllerBaseTest extends SysuiTestCase { 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 @@ public class NetworkControllerBaseTest extends SysuiTestCase { } 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 @@ public class NetworkControllerBaseTest extends SysuiTestCase { 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 37b6a5dc1fde..b108dd817bde 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 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { 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 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { 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 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { 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 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { 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 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { 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 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { 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 93cf3e8317ce..6aab9c762a95 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 @@ public class NetworkControllerEthernetTest extends NetworkControllerBaseTest { } 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 c0d9c3dc00a6..91e9f0622cbf 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.mock; 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 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest { @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 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest { @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 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest { 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 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest { @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 092c1168b5ae..ab7cbf77f5ee 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 @@ -59,9 +59,11 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest { 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 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest { 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 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest { 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 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest { 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 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest { 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 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest { 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 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest { 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 6067b42e0ef8..eb6fc2e950c9 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_IC 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 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { 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 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { 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 8a0ac1111b59..4e7e0a349aa6 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 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { // 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 000000000000..d07a525fdffa --- /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 e9a099ae43a9..2c5038940e98 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.Context; 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 @@ public class AppPredictionManagerService extends 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 735f420ca03e..cd332a68fa88 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.ArrayMap; 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 @@ public class AppPredictionPerUserService extends 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 @@ public class AppPredictionPerUserService extends @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 9ac93d92713b..b160d78a0b64 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 class CompanionDeviceManagerService extends SystemService implements Bind } 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 @@ public class CompanionDeviceManagerService extends SystemService implements Bind } 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 @@ public class CompanionDeviceManagerService extends SystemService implements Bind 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 @@ public class CompanionDeviceManagerService extends SystemService implements Bind 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/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 78c93581e506..19858488a917 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -87,7 +87,6 @@ import static android.system.OsConstants.IPPROTO_UDP; 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; @@ -1389,7 +1388,7 @@ public class ConnectivityService extends IConnectivityManager.Stub 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); } @@ -6121,10 +6120,15 @@ public class ConnectivityService extends IConnectivityManager.Stub 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; @@ -8410,7 +8414,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - 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/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 1b352c728554..d2fd8ff3890e 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 @@ public final class ActiveServices { } 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 c4548a3070a7..492759f7c73d 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.PropertyInvalidatedCache; 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 d8d6528621d2..70a54cfdafa5 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 @@ public class AssistDataRequester extends IAssistDataReceiver.Stub { * 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 @@ public class AssistDataRequester extends IAssistDataReceiver.Stub { * 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 @@ public class AssistDataRequester extends IAssistDataReceiver.Stub { 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 4c9ab63a100b..d789bbb04d7a 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 @@ public class MeasuredEnergySnapshot { /** * 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 @@ public class MeasuredEnergySnapshot { 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/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java index c6f39aa07f26..3eb475921304 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 @@ public class Sensor { }); } + @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 5631647816ec..d843bc94455c 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 @@ class Sensor { 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 40d20734eb50..00b39f11a4c6 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.RemoteException; 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 71565301e3ed..781bad7aa183 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 @@ class HostClipboardMonitor implements Runnable { 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 @@ class HostClipboardMonitor implements Runnable { // 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/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java index 0c0d45995a2b..b57ad5d84e82 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 @@ public class NetworkNotificationManager { // 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 @@ public class NetworkNotificationManager { (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 @@ public class NetworkNotificationManager { } 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 @@ public class NetworkNotificationManager { 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 class NetworkNotificationManager { 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/content/SyncJobService.java b/services/core/java/com/android/server/content/SyncJobService.java index aaf9cbc168af..1f4606104ab8 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 class SyncJobService extends JobService { 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 @@ public class SyncJobService extends JobService { 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 @@ public class SyncJobService extends JobService { return "job:null"; } else { return "job:#" + params.getJobId() + ":" - + "sr=[" + params.getStopReason() + "/" + params.getDebugStopReason() + "]:" + + "sr=[" + params.getLegacyStopReason() + + "/" + params.getDebugStopReason() + "]:" + SyncOperation.maybeCreateFromJobExtras(params.getExtras()); } } diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index 2920ddb2d76d..6d1606defa04 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 android.location.provider.LocationProviderBase.ACTION_NETWORK_PROV 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 class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { private final Context mContext; private final Injector mInjector; - private final LocationEventLog mEventLog; private final LocalService mLocalService; private final GeofenceManager mGeofenceManager; @@ -261,18 +260,20 @@ public class LocationManagerService extends ILocationManager.Stub { @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 @@ public class LocationManagerService extends ILocationManager.Stub { } LocationProviderManager manager = new LocationProviderManager(mContext, mInjector, - mEventLog, providerName, mPassiveManager); + providerName, mPassiveManager); addLocationProviderManager(manager, null); return manager; } @@ -335,7 +336,7 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { } 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { mGnssManagerService.onSystemReady(); LocationProviderManager gnssManager = new LocationProviderManager(mContext, mInjector, - mEventLog, GPS_PROVIDER, mPassiveManager); + GPS_PROVIDER, mPassiveManager); addLocationProviderManager(gnssManager, mGnssManagerService.getGnssLocationProvider()); } @@ -476,7 +474,7 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { ipw.println("Event Log:"); ipw.increaseIndent(); - mEventLog.iterate(ipw::println); + EVENT_LOG.iterate(ipw::println); ipw.decreaseIndent(); } @@ -1456,7 +1454,7 @@ public class LocationManagerService extends ILocationManager.Stub { @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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 29ce3783c4a1..045e06d001e6 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 @@ import com.android.internal.util.Preconditions; /** 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 @@ public class LocationEventLog extends LocalEventLog { } } - 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 @@ public class LocationEventLog extends LocalEventLog { 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 @@ public class LocationEventLog extends LocalEventLog { @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 @@ public class LocationEventLog extends LocalEventLog { } } + 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 @@ public class LocationEventLog extends LocalEventLog { @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 bcdfed45bd9c..21946ca88401 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 @@ public class GnssManagerService { 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 c7d8144342ab..dbc903d35cc8 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.GnssLog; 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 @@ public class GnssMetrics { /** 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 @@ public class GnssMetrics { 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 @@ public class GnssMetrics { } /** - * Logs sv status data - */ + * Logs sv status data + */ public void logSvStatus(GnssStatus status) { boolean isL5; // Calculate SvStatus Information @@ -216,8 +222,8 @@ public class GnssMetrics { } /** - * 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 @@ public class GnssMetrics { private double mSumSquare; private long mLongSum; - Statistics() {} + Statistics() { + } /** Resets statistics */ public synchronized void reset() { @@ -498,7 +505,7 @@ public class GnssMetrics { 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 @@ public class GnssMetrics { 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 @@ public class GnssMetrics { */ 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 cc00d5684991..53407d928012 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 android.os.PowerManager.locationPowerSaveModeToString; 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 @@ public abstract class LocationPowerSaveModeHelper { 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 @@ public abstract class LocationPowerSaveModeHelper { 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 c47a64d6d9a7..a675f5467b3b 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 android.os.PowerSaveState; 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 @@ public class SystemLocationPowerSaveModeHelper extends LocationPowerSaveModeHelp @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 3f7345e7c0c3..632ed6ef192a 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.database.ContentObserver; 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 @@ public class SystemSettingsHelper extends SettingsHelper { */ @Override public void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) { - int userId = ActivityManager.getCurrentUser(); - - 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); + int[] userIds; + try { + userIds = ActivityManager.getService().getRunningUserIds(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + + 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(); + + 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; + } - List<String> locationPackageWhitelist = mLocationPackageWhitelist.getValueForUser( - userId); - if (!locationPackageWhitelist.isEmpty()) { - ipw.println("Location Allow Packages:"); + ipw.print("user "); + ipw.print(userId); + ipw.println(":"); ipw.increaseIndent(); + + 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 d4a8fbd0ceb0..ed1e65457b24 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 @@ public class SystemUserInfoHelper extends UserInfoHelper { */ @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 0fcc1ecc4c1a..c835370a6f86 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 @@ package com.android.server.location.injector; 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 @@ public abstract class UserInfoHelper { 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 dc8b1d001c74..102263b5f3c2 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.LocationManagerService.TAG; 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.FgThread; 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 @@ public class LocationProviderManager extends + 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 @@ public class LocationProviderManager extends onProviderListenerRegister(); if (mForeground) { - mEventLog.logProviderClientForeground(mName, getIdentity()); + EVENT_LOG.logProviderClientForeground(mName, getIdentity()); } } @@ -358,7 +358,7 @@ public class LocationProviderManager extends 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 @@ public class LocationProviderManager extends 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 @@ public class LocationProviderManager extends onProviderListenerInactive(); - mEventLog.logProviderClientInactive(mName, getIdentity()); + EVENT_LOG.logProviderClientInactive(mName, getIdentity()); } /** @@ -543,9 +543,9 @@ public class LocationProviderManager extends 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 @@ public class LocationProviderManager extends 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 @@ public class LocationProviderManager extends listener.deliverOnLocationChanged(deliverLocationResult, mUseWakeLock ? mWakeLock::release : null); - mEventLog.logProviderDeliveredLocations(mName, locationResult.size(), + EVENT_LOG.logProviderDeliveredLocations(mName, locationResult.size(), getIdentity()); } @@ -1178,7 +1178,7 @@ public class LocationProviderManager extends // 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 @@ public class LocationProviderManager extends 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 @@ public class LocationProviderManager extends @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 @@ public class LocationProviderManager extends mEnabledListeners = new ArrayList<>(); mProviderRequestListeners = new CopyOnWriteArrayList<>(); - mEventLog = eventLog; mLocationManagerInternal = Objects.requireNonNull( LocalServices.getService(LocationManagerInternal.class)); mSettingsHelper = injector.getSettingsHelper(); @@ -1477,7 +1475,7 @@ public class LocationProviderManager extends 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 @@ public class LocationProviderManager extends } @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 @@ public class LocationProviderManager extends } // 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 @@ public class LocationProviderManager extends 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 @@ public class LocationProviderManager extends 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 @@ public class LocationProviderManager extends } } - private @Nullable LocationResult getPermittedLocationResult( + @Nullable LocationResult getPermittedLocationResult( @Nullable LocationResult fineLocationResult, @PermissionLevel int permissionLevel) { switch (permissionLevel) { case PERMISSION_FINE: @@ -2538,6 +2536,8 @@ public class LocationProviderManager extends 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 027f4e94f55b..b35af4f6475c 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.location.provider.ProviderRequest; 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 @@ import java.util.Collection; */ 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 6f4aa642500f..ab7e526a8e68 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 android.location.provider.ProviderRequest.INTERVAL_DISABLED; 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.annotations.GuardedBy; 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 @@ public final class StationaryThrottlingLocationProvider extends DelegateLocation 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 @@ public final class StationaryThrottlingLocationProvider extends DelegateLocation @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 @@ public final class StationaryThrottlingLocationProvider extends DelegateLocation 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 @@ public final class StationaryThrottlingLocationProvider extends DelegateLocation } } 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 @@ public final class StationaryThrottlingLocationProvider extends DelegateLocation } 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 c86e49bc7948..317e61b44e46 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 android.util.ArraySet; 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 @@ public class ProxyLocationProvider extends AbstractLocationProvider { 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 @@ public class ProxyLocationProvider extends AbstractLocationProvider { 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 @@ public class ProxyLocationProvider extends AbstractLocationProvider { 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 @@ public class ProxyLocationProvider extends AbstractLocationProvider { 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 @@ public class ProxyLocationProvider extends AbstractLocationProvider { 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 46ece74180fe..db2e9085bdb3 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 @@ public class MediaSessionService extends SystemService implements Monitor { 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/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index d649925e91de..16eac91ee6bb 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.NetworkIdentity.OEM_NONE; 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; @@ -4624,8 +4626,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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) { @@ -4699,18 +4701,18 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // 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(); } } @@ -5858,12 +5860,17 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 68fc14c13143..08dbd77708a4 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -7490,9 +7490,11 @@ public class NotificationManagerService extends SystemService { 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) { @@ -7526,6 +7528,18 @@ public class NotificationManagerService extends SystemService { 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(); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 43eeb2a5eb57..b27c0bd7034b 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 @@ public class PackageManagerService extends IPackageManager.Stub @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 b421cfc95295..cda48063e914 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 @@ public final class Permission { } } } + 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 616058fc2562..3bb5c1694734 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 @@ public class PermissionManagerService extends IPermissionManager.Stub { >= 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 cb3b5c9db7e7..44ff3eb4b942 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.Intent; 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 @@ public final class DomainVerificationUtils { 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/rotationresolver/RotationResolverManagerService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java index a7f3cdb8bcd2..c029bf53809a 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.Manifest; 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 @@ public class RotationResolverManagerService extends 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 @@ public class RotationResolverManagerService extends 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 @@ public class RotationResolverManagerService extends 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 a7767a4fbd66..23e3eba68c76 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.ComponentName; 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 @@ public final class TimeZoneUpdateIdler extends JobService { @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 2452c8d3ddc9..222e852728f5 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 @@ package com.android.server.timezonedetector; 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 @@ public final class ServiceConfigAccessor { * 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 4fa920e5b7d2..f054c5756e20 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 @@ class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider { @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 cc815dc61886..e116a8742208 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_RESU 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 @@ abstract class LocationTimeZoneProvider implements Dumpable { } /** - * 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 @@ abstract class LocationTimeZoneProvider implements Dumpable { // 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 @@ abstract class LocationTimeZoneProvider implements Dumpable { 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 @@ abstract class LocationTimeZoneProvider implements Dumpable { } } - 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 index cab5ad25c54e..951e9d05a150 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 @@ package com.android.server.timezonedetector.location; 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 000000000000..0f4367dddc6e --- /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 e6d37b60882e..b947c8883d07 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 @@ final class AccessibilityController { 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 3a0eb397d210..dc6917502e27 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 @@ class ActivityMetricsLogger { 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 7c236617d14f..eadfbe2c4ac0 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 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // 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 060323c34b40..12c67bb9b7be 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 @@ public abstract class ActivityTaskManagerInternal { */ 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 09f5c937928c..29c5cec59789 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.Watchdog; 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 64ff1084a6b8..d0bab06c5336 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 @@ public class RecentsAnimationController implements DeathRecipient { ? 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 */); @@ -1004,9 +1005,7 @@ public class RecentsAnimationController implements DeathRecipient { 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 @@ public class RecentsAnimationController implements DeathRecipient { 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 57394d6d858e..6b1071c9e84d 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 class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub 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 5ef9420a10d8..49e704f9cc55 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 @@ public abstract class WindowOrientationListener { FrameworkStatsLog.write( FrameworkStatsLog.DEVICE_ROTATED, event.timestamp, - rotationToLogEnum(reportedRotation)); + rotationToLogEnum(reportedRotation), + FrameworkStatsLog.DEVICE_ROTATED__ROTATION_EVENT_TYPE__ACTUAL_EVENT); } } diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java index e7d01219f6c1..eab3b770a94a 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 @@ public class PeopleService extends SystemService { 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 28612f1dd49b..d256d9c24540 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.annotation.UserIdInt; 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 @@ class SessionInfo { 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 c89dadc3fbd6..e19108198bb7 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.AppPredictionContext; 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 @@ public class AppTargetPredictor { /** 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 @@ public class AppTargetPredictor { 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 236ac8407faa..368b737d2133 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.Nullable; 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 @@ class ShareTargetPredictor extends AppTargetPredictor { 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 @@ class ShareTargetPredictor extends AppTargetPredictor { if (mIntentFilter != null) { getDataManager().reportShareTargetEvent(event, mIntentFilter); } + if (mRemoteAppPredictor != null) { + mRemoteAppPredictor.notifyAppTargetEvent(event); + } } /** Provides prediction on direct share targets */ @@ -129,6 +153,15 @@ class ShareTargetPredictor extends AppTargetPredictor { 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 d5eda203e42f..dce853afc763 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 @@ class DomainVerificationCollectorTest { } 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 7e25901301aa..1b0a305b5bdd 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 @@ class DomainVerificationEnforcerTest { 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 0e74b65d25d5..ef79b08aa1c5 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 @@ class DomainVerificationManagerApiTest { }, 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 fe3672d06bc0..0ce16e6b6af2 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.junit.Test 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 @@ class DomainVerificationPackageTest { }, 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 377bae15e2d5..b7c69226ded2 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 @@ class DomainVerificationSettingsMutationTest { 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 44c1b8f3fbb9..54648ab67422 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 @@ class DomainVerificationUserStateOverrideTest { }, 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 7925b69852ba..0fcda819d83c 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 @@ public class JobStatusTest { 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 @@ public class JobStatusTest { 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 f73af535f452..ee1a4f4b3578 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 @@ public class QuotaControllerTest { // 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 059744388197..ef4864620d22 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 @@ package com.android.server.location.injector; 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 @@ public class FakeLocationPowerSaveModeHelper extends LocationPowerSaveModeHelper @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 6156ba9359cd..28da027669b8 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.filters.SmallTest; 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 @@ public class SystemLocationPowerSaveModeHelperTest { 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 1f102ac32a8e..ae70dadba041 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 @@ public class TestInjector implements Injector { 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 1b58e924dd6a..24b85f056731 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 androidx.test.runner.AndroidJUnit4; 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 @@ public class LocationProviderManagerTest { 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 c3cca64154d6..04e0151e619a 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 android.util.Log; 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 @@ public class StationaryThrottlingLocationProviderTest { mDelegateProvider = new FakeProvider(mDelegate); mProvider = new StationaryThrottlingLocationProvider("test_provider", mInjector, - mDelegateProvider, new LocationEventLog()); + mDelegateProvider); mProvider.getController().setListener(mListener); mProvider.getController().start(); } diff --git a/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java b/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java index a7b32ac5c387..68a6e6017bc6 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 @@ public class BackgroundRestrictionsTest { 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 3ea86f2e9ac0..87881bf839cc 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 @@ public class MockPriorityJobService extends JobService { 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 ecff409ccaf1..506624064265 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.Binder; 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 @@ public final class PeopleServiceTest { .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 b09a3c374e86..fac5c1f94e56 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.junit.Assert.assertEquals; 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.ShortcutInfo; 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 @@ public final class ShareTargetPredictorTest { 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 @@ public final class ShareTargetPredictorTest { 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 @@ public final class ShareTargetPredictorTest { .setExtras(new Bundle()) .build(); mPredictor = new ShareTargetPredictor( - predictionContext, mUpdatePredictionsMethod, mDataManager, USER_ID); + predictionContext, mUpdatePredictionsMethod, mDataManager, USER_ID, mContext); mPredictor.predictTargets(); @@ -349,7 +379,7 @@ public final class ShareTargetPredictorTest { .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 5a100a297cfc..a0e9d977954f 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 @@ public class ControllerImplTest { private TestCallback mTestCallback; private TestLocationTimeZoneProvider mTestPrimaryLocationTimeZoneProvider; private TestLocationTimeZoneProvider mTestSecondaryLocationTimeZoneProvider; - private FakeTimeZoneIdValidator mTimeZoneAvailabilityChecker; @Before public void setUp() { @@ -84,13 +83,10 @@ public class ControllerImplTest { }; 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 @@ public class ControllerImplTest { * 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 @@ public class ControllerImplTest { 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 000000000000..e75d05c9a686 --- /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 d13a04e13406..0edb559b04b3 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 org.junit.Test; 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 @@ public class LocationTimeZoneProviderTest { 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 @@ public class LocationTimeZoneProviderTest { 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 @@ public class LocationTimeZoneProviderTest { 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 @@ public class LocationTimeZoneProviderTest { 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 @@ public class LocationTimeZoneProviderTest { } @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 @@ public class LocationTimeZoneProviderTest { 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 @@ public class LocationTimeZoneProviderTest { } } - 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/ZoneInfoDbTimeZoneProviderEventPreProcessorTest.java index 5561b2c6a7aa..173705be4bf1 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneIdValidatorTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessorTest.java @@ -19,6 +19,7 @@ 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; @@ -26,29 +27,44 @@ import java.util.Arrays; import java.util.List; import java.util.TimeZone; +/** Tests for {@link ZoneInfoDbTimeZoneProviderEventPreProcessor}. */ @Presubmit -public class ZoneInfoDbTimeZoneIdValidatorTest { - private final LocationTimeZoneProvider.TimeZoneIdValidator mTzChecker = - new ZoneInfoDbTimeZoneIdValidator(); +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(mTzChecker.isValid(timeZone)).isTrue(); + .that(mPreProcessor.preProcess(event)).isEqualTo(event); } } @Test - public void nonExistingZones_areNotSupported() { + public void eventWithNonExistingZones_areMappedToUncertainEvent() { List<String> nonExistingTimeZones = Arrays.asList( - "SystemV/HST10", "Atlantic/Atlantis", "EUROPE/LONDON", "Etc/GMT-5:30" - ); + "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(mTzChecker.isValid(timeZone)) - .isFalse(); + .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/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index e37b82fe7ac0..aa9feeaaf959 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -4910,6 +4910,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { .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 3b1537639973..11162043bb27 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 @@ public class ScheduleCalendarTest extends UiServiceTestCase { 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 5a527a219055..7446e9e0bb9f 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 @@ public class ScheduleConditionProviderTest extends UiServiceTestCase { 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 002859e3366b..7c9ff79df55e 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; 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 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { } @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/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index 2a5ddfd891b1..4e64838d99ce 100755 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -138,6 +138,27 @@ public final class Call { 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 @@ public final class Call { // 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 @@ public final class Call { 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 @@ public final class Call { 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 @@ public final class Call { @Override public int hashCode() { - return Objects.hash(mHandle, + return Objects.hash(mState, + mHandle, mHandlePresentation, mCallerDisplayName, mCallerDisplayNamePresentation, @@ -1117,6 +1148,7 @@ public final class Call { /** {@hide} */ public Details( + @CallState int state, String telecomCallId, Uri handle, int handlePresentation, @@ -1136,6 +1168,7 @@ public final class Call { String contactDisplayName, int callDirection, int callerNumberVerificationStatus) { + mState = state; mTelecomCallId = telecomCallId; mHandle = handle; mHandlePresentation = handlePresentation; @@ -1160,6 +1193,7 @@ public final class Call { /** {@hide} */ public static Details createFromParcelableCall(ParcelableCall parcelableCall) { return new Details( + parcelableCall.getState(), parcelableCall.getId(), parcelableCall.getHandle(), parcelableCall.getHandlePresentation(), @@ -1186,6 +1220,8 @@ public final class Call { 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 @@ public final class Call { * @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 @@ public final class Call { /** * 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 @@ public final class Call { 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 182dc8bb8325..320308c9e926 100644 --- a/telecomm/java/android/telecom/ParcelableCall.java +++ b/telecomm/java/android/telecom/ParcelableCall.java @@ -399,7 +399,7 @@ public final class ParcelableCall implements Parcelable { } /** 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 4ae11b8458cb..23cf5116b2da 100644 --- a/telephony/java/android/telephony/Annotation.java +++ b/telephony/java/android/telephony/Annotation.java @@ -447,6 +447,9 @@ public class Annotation { 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 597fe8f85cfa..957f683292f7 100644 --- a/telephony/java/android/telephony/DataSpecificRegistrationInfo.java +++ b/telephony/java/android/telephony/DataSpecificRegistrationInfo.java @@ -68,22 +68,22 @@ public final class DataSpecificRegistrationInfo implements Parcelable { 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 @@ public final class DataSpecificRegistrationInfo implements Parcelable { isDcNrRestricted = dsri.isDcNrRestricted; isNrAvailable = dsri.isNrAvailable; isEnDcAvailable = dsri.isEnDcAvailable; - mLteVopsSupportInfo = dsri.mLteVopsSupportInfo; + mVopsSupportInfo = dsri.mVopsSupportInfo; } private DataSpecificRegistrationInfo(/* @NonNull */ Parcel source) { @@ -105,7 +105,7 @@ public final class DataSpecificRegistrationInfo implements Parcelable { 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 @@ public final class DataSpecificRegistrationInfo implements Parcelable { dest.writeBoolean(isDcNrRestricted); dest.writeBoolean(isNrAvailable); dest.writeBoolean(isEnDcAvailable); - mLteVopsSupportInfo.writeToParcel(dest, flags); + dest.writeParcelable(mVopsSupportInfo, flags); } @Override @@ -131,15 +131,15 @@ public final class DataSpecificRegistrationInfo implements Parcelable { .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 @@ public final class DataSpecificRegistrationInfo implements Parcelable { && 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 @@ public final class DataSpecificRegistrationInfo implements Parcelable { /** * @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 f50bb58c4b2e..2827e8dc8539 100644 --- a/telephony/java/android/telephony/DataThrottlingRequest.java +++ b/telephony/java/android/telephony/DataThrottlingRequest.java @@ -17,6 +17,7 @@ package android.telephony; 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 @@ public final class DataThrottlingRequest implements Parcelable { * @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 @@ public final class DataThrottlingRequest implements Parcelable { * @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 @@ public final class DataThrottlingRequest implements Parcelable { * @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 83e41bf3df3b..87761e21350b 100644 --- a/telephony/java/android/telephony/LteVopsSupportInfo.java +++ b/telephony/java/android/telephony/LteVopsSupportInfo.java @@ -21,7 +21,7 @@ import android.annotation.NonNull; 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 @@ import java.util.Objects; * @hide */ @SystemApi -public final class LteVopsSupportInfo implements Parcelable { +public final class LteVopsSupportInfo extends VopsSupportInfo { /**@hide*/ @Retention(RetentionPolicy.SOURCE) @@ -42,7 +42,10 @@ public final class LteVopsSupportInfo implements Parcelable { 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 @@ public final class LteVopsSupportInfo implements Parcelable { 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 @@ public final class LteVopsSupportInfo implements Parcelable { new Creator<LteVopsSupportInfo>() { @Override public LteVopsSupportInfo createFromParcel(Parcel in) { + // Skip the type info. + in.readInt(); return new LteVopsSupportInfo(in); } @@ -133,6 +163,11 @@ public final class LteVopsSupportInfo implements Parcelable { } }; + /** @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 a78f81331c8c..5fb60d7599ea 100644 --- a/telephony/java/android/telephony/NetworkRegistrationInfo.java +++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java @@ -293,11 +293,12 @@ public final class NetworkRegistrationInfo implements Parcelable { @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 000000000000..460a58971837 --- /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 000000000000..155ee384b5b0 --- /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/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index f7580d77186d..a46621a83c1e 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -630,7 +630,7 @@ public class SubscriptionManager { 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 @@ public class SubscriptionManager { * 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 f06b538bf3b1..d3246ca8ba6c 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -11037,6 +11037,25 @@ public class TelephonyManager { } /** + * 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 @@ public class TelephonyManager { * "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 class TelephonyManager { 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 @@ public class TelephonyManager { * 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 000000000000..31c608fe9546 --- /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 000000000000..f89bfa9b60b4 --- /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 52d0f036788c..a133eadb3517 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.Retention; 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 @@ public final class RcsContactUceCapability implements Parcelable { /** * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were * queried through SIP OPTIONS. - * @hide */ public static final class OptionsBuilder { @@ -151,7 +152,7 @@ public final class RcsContactUceCapability implements Parcelable { * @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 @@ public final class RcsContactUceCapability implements Parcelable { 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 @@ public final class RcsContactUceCapability implements Parcelable { 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 @@ public final class RcsContactUceCapability implements Parcelable { 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 @@ public final class RcsContactUceCapability implements Parcelable { 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 a217d1321342..c3d7325f2e0a 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.RcsContactUceCapability; 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 @@ public class CapabilityExchangeAidlWrapper implements CapabilityExchangeEventLis * 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 @@ public class CapabilityExchangeAidlWrapper implements CapabilityExchangeEventLis }; 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 85703f8de5e5..6315b242184a 100644 --- a/telephony/java/android/telephony/ims/feature/RcsFeature.java +++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java @@ -47,6 +47,7 @@ import com.android.internal.telephony.util.TelephonyUtils; 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 @@ public class RcsFeature extends ImsFeature { 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 62955487897f..a3be8dab2891 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.RcsUceAdapter; 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 @@ public interface CapabilityExchangeEventListener { * {@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 @@ public interface CapabilityExchangeEventListener { * 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 03e17fbc2c0d..25b9446152ac 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.Retention; 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 @@ public class RcsCapabilityExchangeImplBase { * @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 @@ public class RcsCapabilityExchangeImplBase { // 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 b9b347b02958..c1a86b3a2dac 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 @@ class InputEventAssignerTest { 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 @@ class InputEventAssignerTest { 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/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java index d40b88ca599f..f161e52c2fd5 100644 --- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java +++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java @@ -531,11 +531,22 @@ public class NetworkCapabilitiesTest { assertFalse(nc1.equalsNetCapabilities(nc2)); nc2.addUnwantedCapability(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)); + 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.removeUnwantedCapability(NET_CAPABILITY_INTERNET); + assertFalse(nc1.equalsNetCapabilities(nc2)); + nc2.removeUnwantedCapability(NET_CAPABILITY_INTERNET); + assertTrue(nc1.equalsNetCapabilities(nc2)); + } else { + nc1.removeCapability(NET_CAPABILITY_INTERNET); + assertFalse(nc1.equalsNetCapabilities(nc2)); + nc2.removeCapability(NET_CAPABILITY_INTERNET); + assertTrue(nc1.equalsNetCapabilities(nc2)); + } } @Test @@ -596,11 +607,20 @@ public class NetworkCapabilitiesTest { // 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); diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 88f42c145c6e..0c2fb4ec6f27 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -7485,6 +7485,9 @@ public class ConnectivityServiceTest { 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<>(); @@ -7503,6 +7506,7 @@ public class ConnectivityServiceTest { 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); @@ -7515,6 +7519,7 @@ public class ConnectivityServiceTest { 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()); @@ -7529,6 +7534,7 @@ public class ConnectivityServiceTest { 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 @@ -7549,6 +7555,7 @@ public class ConnectivityServiceTest { 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); @@ -7569,6 +7576,7 @@ public class ConnectivityServiceTest { 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); @@ -7580,6 +7588,7 @@ public class ConnectivityServiceTest { 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); @@ -7594,6 +7603,7 @@ public class ConnectivityServiceTest { 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); @@ -7605,6 +7615,7 @@ public class ConnectivityServiceTest { 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); @@ -7617,6 +7628,7 @@ public class ConnectivityServiceTest { 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); @@ -7627,6 +7639,7 @@ public class ConnectivityServiceTest { 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); @@ -7637,11 +7650,14 @@ public class ConnectivityServiceTest { 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() { |