diff options
375 files changed, 9547 insertions, 4185 deletions
diff --git a/Android.bp b/Android.bp index 753cefc38ed6..149b22355c58 100644 --- a/Android.bp +++ b/Android.bp @@ -97,6 +97,7 @@ filegroup { ":platform-compat-native-aidl", // AIDL sources from external directories + ":android.hardware.gnss-V2-java-source", ":android.hardware.graphics.common-V3-java-source", ":android.hardware.security.keymint-V2-java-source", ":android.hardware.security.secureclock-V1-java-source", @@ -328,6 +329,7 @@ java_defaults { "modules-utils-preconditions", "modules-utils-synchronous-result-receiver", "modules-utils-os", + "modules-utils-uieventlogger-interface", "framework-permission-aidl-java", "spatializer-aidl-java", "audiopolicy-types-aidl-java", diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java index e2426c2f1758..f822a188c99c 100644 --- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java +++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java @@ -2,6 +2,7 @@ package com.android.server.usage; import android.annotation.CurrentTimeMillisLong; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager.ProcessState; import android.app.usage.AppStandbyInfo; @@ -237,4 +238,11 @@ public interface AppStandbyInternal { */ @ProcessState int getBroadcastResponseFgThresholdState(); + + /** + * Return the last known value corresponding to the {@code key} from + * {@link android.provider.DeviceConfig#NAMESPACE_APP_STANDBY} in AppStandbyController. + */ + @Nullable + String getAppStandbyConstant(@NonNull String key); } 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 18f63b7ad902..23056b5670b9 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java @@ -17,6 +17,7 @@ package com.android.server.job; import static com.android.server.job.JobSchedulerService.MAX_JOB_CONTEXTS_COUNT; +import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import android.annotation.IntDef; @@ -32,9 +33,11 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.UserInfo; +import android.os.BatteryStats; import android.os.Handler; import android.os.PowerManager; import android.os.RemoteException; +import android.os.ServiceManager; import android.os.UserHandle; import android.provider.DeviceConfig; import android.util.ArraySet; @@ -51,19 +54,24 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.app.IBatteryStats; import com.android.internal.app.procstats.ProcessStats; import com.android.internal.util.StatLogger; import com.android.server.JobSchedulerBackgroundThread; import com.android.server.LocalServices; import com.android.server.job.controllers.JobStatus; import com.android.server.job.controllers.StateController; +import com.android.server.job.restrictions.JobRestriction; import com.android.server.pm.UserManagerInternal; +import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.function.Consumer; +import java.util.function.Predicate; /** * This class decides, given the various configuration and the system status, which jobs can start @@ -278,6 +286,11 @@ class JobConcurrencyManager { String[] mRecycledShouldStopJobReason = new String[MAX_JOB_CONTEXTS_COUNT]; + /** + * Set of JobServiceContexts that we use to run jobs. + */ + final List<JobServiceContext> mActiveServices = new ArrayList<>(); + private final ArraySet<JobStatus> mRunningJobs = new ArraySet<>(); private final WorkCountTracker mWorkCountTracker = new WorkCountTracker(); @@ -358,6 +371,20 @@ class JobConcurrencyManager { onInteractiveStateChanged(mPowerManager.isInteractive()); } + /** + * Called when the boot phase reaches + * {@link com.android.server.SystemService#PHASE_THIRD_PARTY_APPS_CAN_START}. + */ + void onThirdPartyAppsCanStart() { + final IBatteryStats batteryStats = IBatteryStats.Stub.asInterface( + ServiceManager.getService(BatteryStats.SERVICE_NAME)); + for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) { + mActiveServices.add( + new JobServiceContext(mService, this, batteryStats, + mService.mJobPackageTracker, mContext.getMainLooper())); + } + } + @GuardedBy("mLock") void onAppRemovedLocked(String pkgName, int uid) { final PackageStats packageStats = mActivePkgStats.get(UserHandle.getUserId(uid), pkgName); @@ -390,6 +417,7 @@ class JobConcurrencyManager { case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED: if (mPowerManager != null && mPowerManager.isDeviceIdleMode()) { synchronized (mLock) { + stopUnexemptedJobsForDoze(); stopLongRunningJobsLocked("deep doze"); } } @@ -471,6 +499,11 @@ class JobConcurrencyManager { } @GuardedBy("mLock") + ArraySet<JobStatus> getRunningJobsLocked() { + return mRunningJobs; + } + + @GuardedBy("mLock") boolean isJobRunningLocked(JobStatus job) { return mRunningJobs.contains(job); } @@ -546,7 +579,7 @@ class JobConcurrencyManager { } final List<JobStatus> pendingJobs = mService.mPendingJobs; - final List<JobServiceContext> activeServices = mService.mActiveServices; + final List<JobServiceContext> activeServices = mActiveServices; // To avoid GC churn, we recycle the arrays. JobStatus[] contextIdToJobMap = mRecycledAssignContextIdToJobMap; @@ -719,9 +752,44 @@ class JobConcurrencyManager { } @GuardedBy("mLock") + boolean stopJobOnServiceContextLocked(JobStatus job, + @JobParameters.StopReason int reason, int internalReasonCode, String debugReason) { + if (!mRunningJobs.contains(job)) { + return false; + } + + 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, internalReasonCode, debugReason); + return true; + } + } + Slog.wtf(TAG, "Couldn't find running job on a context"); + mRunningJobs.remove(job); + return false; + } + + @GuardedBy("mLock") + private void stopUnexemptedJobsForDoze() { + // When becoming idle, make sure no jobs are actively running, + // except those using the idle exemption flag. + for (int i = 0; i < mActiveServices.size(); i++) { + JobServiceContext jsc = mActiveServices.get(i); + final JobStatus executing = jsc.getRunningJobLocked(); + if (executing != null && !executing.canRunInDoze()) { + jsc.cancelExecutingJobLocked(JobParameters.STOP_REASON_DEVICE_STATE, + JobParameters.INTERNAL_STOP_REASON_DEVICE_IDLE, + "cancelled due to doze"); + } + } + } + + @GuardedBy("mLock") private void stopLongRunningJobsLocked(@NonNull String debugReason) { for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; ++i) { - final JobServiceContext jsc = mService.mActiveServices.get(i); + final JobServiceContext jsc = mActiveServices.get(i); final JobStatus jobStatus = jsc.getRunningJobLocked(); if (jobStatus != null && !jsc.isWithinExecutionGuaranteeTime()) { @@ -731,6 +799,41 @@ class JobConcurrencyManager { } } + @GuardedBy("mLock") + void stopNonReadyActiveJobsLocked() { + for (int i = 0; i < mActiveServices.size(); i++) { + JobServiceContext serviceContext = mActiveServices.get(i); + final JobStatus running = serviceContext.getRunningJobLocked(); + if (running == null) { + continue; + } + if (!running.isReady()) { + if (running.getEffectiveStandbyBucket() == RESTRICTED_INDEX + && running.getStopReason() == JobParameters.STOP_REASON_APP_STANDBY) { + serviceContext.cancelExecutingJobLocked( + running.getStopReason(), + JobParameters.INTERNAL_STOP_REASON_RESTRICTED_BUCKET, + "cancelled due to restricted bucket"); + } else { + serviceContext.cancelExecutingJobLocked( + running.getStopReason(), + JobParameters.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED, + "cancelled due to unsatisfied constraints"); + } + } else { + final JobRestriction restriction = mService.checkIfRestricted(running); + if (restriction != null) { + final int internalReasonCode = restriction.getInternalReason(); + serviceContext.cancelExecutingJobLocked(restriction.getReason(), + internalReasonCode, + "restricted due to " + + JobParameters.getInternalReasonCodeDescription( + internalReasonCode)); + } + } + } + } + private void noteConcurrency() { mService.mJobPackageTracker.noteConcurrency(mRunningJobs.size(), // TODO: log per type instead of only TOP @@ -1078,6 +1181,24 @@ class JobConcurrencyManager { } @GuardedBy("mLock") + boolean executeTimeoutCommandLocked(PrintWriter pw, String pkgName, int userId, + boolean hasJobId, int jobId) { + boolean foundSome = false; + for (int i = 0; i < mActiveServices.size(); i++) { + final JobServiceContext jc = mActiveServices.get(i); + final JobStatus js = jc.getRunningJobLocked(); + if (jc.timeoutIfExecutingLocked(pkgName, userId, hasJobId, jobId, "shell")) { + foundSome = true; + pw.print("Timing out: "); + js.printUniqueId(pw); + pw.print(" "); + pw.println(js.getServiceComponent().flattenToShortString()); + } + } + return foundSome; + } + + @GuardedBy("mLock") private String printPendingQueueLocked() { StringBuilder s = new StringBuilder("Pending queue: "); Iterator<JobStatus> it = mService.mPendingJobs.iterator(); @@ -1200,6 +1321,43 @@ class JobConcurrencyManager { } } + @GuardedBy("mLock") + void dumpActiveJobsLocked(IndentingPrintWriter pw, Predicate<JobStatus> predicate, + long nowElapsed, long nowUptime) { + pw.println("Active jobs:"); + pw.increaseIndent(); + for (int i = 0; i < mActiveServices.size(); i++) { + JobServiceContext jsc = mActiveServices.get(i); + final JobStatus job = jsc.getRunningJobLocked(); + + if (job != null && !predicate.test(job)) { + continue; + } + + pw.print("Slot #"); pw.print(i); pw.print(": "); + jsc.dumpLocked(pw, nowElapsed); + + if (job != null) { + pw.increaseIndent(); + + pw.increaseIndent(); + job.dump(pw, false, nowElapsed); + pw.decreaseIndent(); + + pw.print("Evaluated bias: "); + pw.println(JobInfo.getBiasString(job.lastEvaluatedBias)); + + pw.print("Active at "); + TimeUtils.formatDuration(job.madeActive - nowUptime, pw); + pw.print(", pending for "); + TimeUtils.formatDuration(job.madeActive - job.madePending, pw); + pw.decreaseIndent(); + pw.println(); + } + } + pw.decreaseIndent(); + } + public void dumpProtoLocked(ProtoOutputStream proto, long tag, long now, long nowRealtime) { final long token = proto.start(tag); 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 b9362789c6c6..3d74bc98ad32 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -57,7 +57,6 @@ import android.content.pm.ServiceInfo; import android.net.Uri; import android.os.BatteryManager; import android.os.BatteryManagerInternal; -import android.os.BatteryStats; import android.os.BatteryStatsInternal; import android.os.Binder; import android.os.Handler; @@ -67,7 +66,6 @@ import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; -import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.os.WorkSource; @@ -90,7 +88,6 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.app.IBatteryStats; import com.android.internal.os.SomeArgs; import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; @@ -100,7 +97,6 @@ import com.android.server.AppStateTrackerImpl; import com.android.server.DeviceIdleInternal; import com.android.server.JobSchedulerBackgroundThread; import com.android.server.LocalServices; -import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob; import com.android.server.job.JobSchedulerServiceDumpProto.PendingJob; import com.android.server.job.controllers.BackgroundJobsController; import com.android.server.job.controllers.BatteryController; @@ -243,12 +239,6 @@ public class JobSchedulerService extends com.android.server.SystemService static final int MSG_CHECK_CHANGED_JOB_LIST = 8; static final int MSG_CHECK_MEDIA_EXEMPTION = 9; - /** - * Track Services that have currently active or pending jobs. The index is provided by - * {@link JobStatus#getServiceToken()} - */ - final List<JobServiceContext> mActiveServices = new ArrayList<>(); - /** List of controllers that will notify this service of updates to jobs. */ final List<StateController> mControllers; /** @@ -307,7 +297,6 @@ public class JobSchedulerService extends com.android.server.SystemService PackageManagerInternal mLocalPM; ActivityManagerInternal mActivityManagerInternal; - IBatteryStats mBatteryStats; DeviceIdleInternal mLocalDeviceIdleController; @VisibleForTesting AppStateTrackerImpl mAppStateTracker; @@ -1578,7 +1567,8 @@ public class JobSchedulerService extends com.android.server.SystemService mJobPackageTracker.noteNonpending(cancelled); } // Cancel if running. - stopJobOnServiceContextLocked(cancelled, reason, internalReasonCode, debugReason); + mConcurrencyManager.stopJobOnServiceContextLocked( + cancelled, reason, internalReasonCode, 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()); @@ -1627,19 +1617,7 @@ public class JobSchedulerService extends com.android.server.SystemService if (DEBUG) { Slog.d(TAG, "Doze state changed: " + deviceIdle); } - if (deviceIdle) { - // When becoming idle, make sure no jobs are actively running, - // except those using the idle exemption flag. - for (int i=0; i<mActiveServices.size(); i++) { - JobServiceContext jsc = mActiveServices.get(i); - final JobStatus executing = jsc.getRunningJobLocked(); - if (executing != null && !executing.canRunInDoze()) { - jsc.cancelExecutingJobLocked(JobParameters.STOP_REASON_DEVICE_STATE, - JobParameters.INTERNAL_STOP_REASON_DEVICE_IDLE, - "cancelled due to doze"); - } - } - } else { + if (!deviceIdle) { // When coming out of idle, allow thing to start back up. if (mReadyToRock) { if (mLocalDeviceIdleController != null) { @@ -1682,10 +1660,10 @@ public class JobSchedulerService extends com.android.server.SystemService // active is true if pending queue contains jobs OR some job is running. boolean active = mPendingJobs.size() > 0; if (mPendingJobs.size() <= 0) { - for (int i=0; i<mActiveServices.size(); i++) { - final JobServiceContext jsc = mActiveServices.get(i); - final JobStatus job = jsc.getRunningJobLocked(); - if (job != null && !job.canRunInDoze()) { + final ArraySet<JobStatus> runningJobs = mConcurrencyManager.getRunningJobsLocked(); + for (int i = runningJobs.size() - 1; i >= 0; --i) { + final JobStatus job = runningJobs.valueAt(i); + if (!job.canRunInDoze()) { // We will report active if we have a job running and it does not have an // exception that allows it to run in Doze. active = true; @@ -1895,16 +1873,9 @@ public class JobSchedulerService extends com.android.server.SystemService synchronized (mLock) { // Let's go! mReadyToRock = true; - mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService( - BatteryStats.SERVICE_NAME)); mLocalDeviceIdleController = LocalServices.getService(DeviceIdleInternal.class); - // Create the "runners". - for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) { - mActiveServices.add( - new JobServiceContext(this, mConcurrencyManager, mBatteryStats, - mJobPackageTracker, getContext().getMainLooper())); - } + mConcurrencyManager.onThirdPartyAppsCanStart(); // Attach jobs to their controllers. mJobs.forEachJob((job) -> { for (int controller = 0; controller < mControllers.size(); controller++) { @@ -1961,19 +1932,6 @@ public class JobSchedulerService extends com.android.server.SystemService return removed; } - private boolean stopJobOnServiceContextLocked(JobStatus job, - @JobParameters.StopReason int reason, int internalReasonCode, 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, internalReasonCode, debugReason); - return true; - } - } - return false; - } - /** Return {@code true} if the specified job is currently executing. */ @GuardedBy("mLock") public boolean isCurrentlyRunningLocked(JobStatus job) { @@ -2383,7 +2341,8 @@ public class JobSchedulerService extends com.android.server.SystemService * - if passes all the restrictions or has {@link JobInfo#BIAS_FOREGROUND_SERVICE} bias * or higher. */ - private JobRestriction checkIfRestricted(JobStatus job) { + @GuardedBy("mLock") + JobRestriction checkIfRestricted(JobStatus job) { if (evaluateJobBiasLocked(job) >= JobInfo.BIAS_FOREGROUND_SERVICE) { // Jobs with BIAS_FOREGROUND_SERVICE or higher should not be restricted return null; @@ -2397,38 +2356,9 @@ public class JobSchedulerService extends com.android.server.SystemService return null; } + @GuardedBy("mLock") private void stopNonReadyActiveJobsLocked() { - for (int i=0; i<mActiveServices.size(); i++) { - JobServiceContext serviceContext = mActiveServices.get(i); - final JobStatus running = serviceContext.getRunningJobLocked(); - if (running == null) { - continue; - } - if (!running.isReady()) { - if (running.getEffectiveStandbyBucket() == RESTRICTED_INDEX - && running.getStopReason() == JobParameters.STOP_REASON_APP_STANDBY) { - serviceContext.cancelExecutingJobLocked( - running.getStopReason(), - JobParameters.INTERNAL_STOP_REASON_RESTRICTED_BUCKET, - "cancelled due to restricted bucket"); - } else { - serviceContext.cancelExecutingJobLocked( - running.getStopReason(), - JobParameters.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED, - "cancelled due to unsatisfied constraints"); - } - } else { - final JobRestriction restriction = checkIfRestricted(running); - if (restriction != null) { - final int internalReasonCode = restriction.getInternalReason(); - serviceContext.cancelExecutingJobLocked(restriction.getReason(), - internalReasonCode, - "restricted due to " - + JobParameters.getInternalReasonCodeDescription( - internalReasonCode)); - } - } - } + mConcurrencyManager.stopNonReadyActiveJobsLocked(); } /** @@ -2598,7 +2528,7 @@ public class JobSchedulerService extends com.android.server.SystemService debugReason = "couldn't figure out why the job should stop running"; } } - stopJobOnServiceContextLocked(job, job.getStopReason(), + mConcurrencyManager.stopJobOnServiceContextLocked(job, job.getStopReason(), internalStopReason, debugReason); } else if (mPendingJobs.remove(job)) { noteJobNonPending(job); @@ -3516,9 +3446,11 @@ public class JobSchedulerService extends com.android.server.SystemService final ArrayList<JobInfo> runningJobs; synchronized (mLock) { - runningJobs = new ArrayList<>(mActiveServices.size()); - for (JobServiceContext jsc : mActiveServices) { - final JobStatus job = jsc.getRunningJobLocked(); + final ArraySet<JobStatus> runningJobStatuses = + mConcurrencyManager.getRunningJobsLocked(); + runningJobs = new ArrayList<>(runningJobStatuses.size()); + for (int i = runningJobStatuses.size() - 1; i >= 0; --i) { + final JobStatus job = runningJobStatuses.valueAt(i); if (job != null) { runningJobs.add(job.getJob()); } @@ -3599,18 +3531,8 @@ public class JobSchedulerService extends com.android.server.SystemService } synchronized (mLock) { - boolean foundSome = false; - for (int i = 0; i < mActiveServices.size(); i++) { - final JobServiceContext jc = mActiveServices.get(i); - final JobStatus js = jc.getRunningJobLocked(); - if (jc.timeoutIfExecutingLocked(pkgName, userId, hasJobId, jobId, "shell")) { - foundSome = true; - pw.print("Timing out: "); - js.printUniqueId(pw); - pw.print(" "); - pw.println(js.getServiceComponent().flattenToShortString()); - } - } + final boolean foundSome = mConcurrencyManager.executeTimeoutCommandLocked(pw, + pkgName, userId, hasJobId, jobId); if (!foundSome) { pw.println("No matching executing jobs found."); } @@ -4037,38 +3959,7 @@ public class JobSchedulerService extends com.android.server.SystemService pw.decreaseIndent(); pw.println(); - pw.println("Active jobs:"); - pw.increaseIndent(); - for (int i=0; i<mActiveServices.size(); i++) { - JobServiceContext jsc = mActiveServices.get(i); - final JobStatus job = jsc.getRunningJobLocked(); - - if (job != null && !predicate.test(job)) { - continue; - } - - pw.print("Slot #"); pw.print(i); pw.print(": "); - jsc.dumpLocked(pw, nowElapsed); - - if (job != null) { - pw.increaseIndent(); - - pw.increaseIndent(); - job.dump(pw, false, nowElapsed); - pw.decreaseIndent(); - - pw.print("Evaluated bias: "); - pw.println(JobInfo.getBiasString(job.lastEvaluatedBias)); - - pw.print("Active at "); - TimeUtils.formatDuration(job.madeActive - nowUptime, pw); - pw.print(", pending for "); - TimeUtils.formatDuration(job.madeActive - job.madePending, pw); - pw.decreaseIndent(); - pw.println(); - } - } - pw.decreaseIndent(); + mConcurrencyManager.dumpActiveJobsLocked(pw, predicate, nowElapsed, nowUptime); pw.println(); boolean recentPrinted = false; @@ -4228,45 +4119,6 @@ public class JobSchedulerService extends com.android.server.SystemService proto.end(pjToken); } - for (JobServiceContext jsc : mActiveServices) { - final long ajToken = proto.start(JobSchedulerServiceDumpProto.ACTIVE_JOBS); - final JobStatus job = jsc.getRunningJobLocked(); - - if (job == null) { - final long ijToken = proto.start(ActiveJob.INACTIVE); - - proto.write(ActiveJob.InactiveJob.TIME_SINCE_STOPPED_MS, - nowElapsed - jsc.mStoppedTime); - if (jsc.mStoppedReason != null) { - proto.write(ActiveJob.InactiveJob.STOPPED_REASON, - jsc.mStoppedReason); - } - - proto.end(ijToken); - } else { - final long rjToken = proto.start(ActiveJob.RUNNING); - - job.writeToShortProto(proto, ActiveJob.RunningJob.INFO); - - proto.write(ActiveJob.RunningJob.RUNNING_DURATION_MS, - nowElapsed - jsc.getExecutionStartTimeElapsed()); - proto.write(ActiveJob.RunningJob.TIME_UNTIL_TIMEOUT_MS, - jsc.getTimeoutElapsed() - nowElapsed); - - job.dump(proto, ActiveJob.RunningJob.DUMP, false, nowElapsed); - - proto.write(ActiveJob.RunningJob.EVALUATED_PRIORITY, - evaluateJobBiasLocked(job)); - - proto.write(ActiveJob.RunningJob.TIME_SINCE_MADE_ACTIVE_MS, - nowUptime - job.madeActive); - proto.write(ActiveJob.RunningJob.PENDING_DURATION_MS, - job.madeActive - job.madePending); - - proto.end(rjToken); - } - proto.end(ajToken); - } if (filterUid == -1) { proto.write(JobSchedulerServiceDumpProto.IS_READY_TO_ROCK, mReadyToRock); proto.write(JobSchedulerServiceDumpProto.REPORTED_ACTIVE, mReportedActive); 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 efcf14f07ee9..30fdb1e9ad4e 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 @@ -441,11 +441,6 @@ public final class JobStatus { /** 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; - } - /** * Core constructor for JobStatus instances. All other ctors funnel down to this one. * diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java index e986b1a9bf24..2e3b377d08a5 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java @@ -384,14 +384,16 @@ public class AppIdleHistory { return userHistory; } + // TODO (206518483): Remove unused parameter 'elapsedRealtime'. private AppUsageHistory getPackageHistory(ArrayMap<String, AppUsageHistory> userHistory, String packageName, long elapsedRealtime, boolean create) { AppUsageHistory appUsageHistory = userHistory.get(packageName); if (appUsageHistory == null && create) { appUsageHistory = new AppUsageHistory(); - appUsageHistory.lastUsedElapsedTime = getElapsedTime(elapsedRealtime); - appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime); - appUsageHistory.lastPredictedTime = getElapsedTime(0); + appUsageHistory.lastUsedByUserElapsedTime = Integer.MIN_VALUE; + appUsageHistory.lastUsedElapsedTime = Integer.MIN_VALUE; + appUsageHistory.lastUsedScreenTime = Integer.MIN_VALUE; + appUsageHistory.lastPredictedTime = Integer.MIN_VALUE; appUsageHistory.currentBucket = STANDBY_BUCKET_NEVER; appUsageHistory.bucketingReason = REASON_MAIN_DEFAULT; appUsageHistory.lastInformedBucket = -1; @@ -544,7 +546,7 @@ public class AppIdleHistory { AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, elapsedRealtime, false); if (appUsageHistory == null || appUsageHistory.lastUsedByUserElapsedTime == Long.MIN_VALUE - || appUsageHistory.lastUsedByUserElapsedTime == 0) { + || appUsageHistory.lastUsedByUserElapsedTime <= 0) { return Long.MAX_VALUE; } return getElapsedTime(elapsedRealtime) - appUsageHistory.lastUsedByUserElapsedTime; @@ -631,8 +633,12 @@ public class AppIdleHistory { // If we don't have any state for the app, assume never used if (appUsageHistory == null) return screenTimeThresholds.length - 1; - long screenOnDelta = getScreenOnTime(elapsedRealtime) - appUsageHistory.lastUsedScreenTime; - long elapsedDelta = getElapsedTime(elapsedRealtime) - appUsageHistory.lastUsedElapsedTime; + long screenOnDelta = appUsageHistory.lastUsedScreenTime >= 0 + ? getScreenOnTime(elapsedRealtime) - appUsageHistory.lastUsedScreenTime + : Long.MAX_VALUE; + long elapsedDelta = appUsageHistory.lastUsedElapsedTime >= 0 + ? getElapsedTime(elapsedRealtime) - appUsageHistory.lastUsedElapsedTime + : Long.MAX_VALUE; if (DEBUG) Slog.d(TAG, packageName + " lastUsedScreen=" + appUsageHistory.lastUsedScreenTime @@ -951,14 +957,14 @@ public class AppIdleHistory { + " reason=" + UsageStatsManager.reasonToString(appUsageHistory.bucketingReason)); idpw.print(" used="); - TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastUsedElapsedTime, idpw); + printLastActionElapsedTime(idpw, totalElapsedTime, appUsageHistory.lastUsedElapsedTime); idpw.print(" usedByUser="); - TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastUsedByUserElapsedTime, - idpw); + printLastActionElapsedTime(idpw, totalElapsedTime, + appUsageHistory.lastUsedByUserElapsedTime); idpw.print(" usedScr="); - TimeUtils.formatDuration(screenOnTime - appUsageHistory.lastUsedScreenTime, idpw); + printLastActionElapsedTime(idpw, totalElapsedTime, appUsageHistory.lastUsedScreenTime); idpw.print(" lastPred="); - TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastPredictedTime, idpw); + printLastActionElapsedTime(idpw, totalElapsedTime, appUsageHistory.lastPredictedTime); dumpBucketExpiryTimes(idpw, appUsageHistory, totalElapsedTime); idpw.print(" lastJob="); TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastJobRunTime, idpw); @@ -986,6 +992,15 @@ public class AppIdleHistory { idpw.decreaseIndent(); } + private void printLastActionElapsedTime(IndentingPrintWriter idpw, long totalElapsedTimeMS, + long lastActionTimeMs) { + if (lastActionTimeMs < 0) { + idpw.print("<uninitialized>"); + } else { + TimeUtils.formatDuration(totalElapsedTimeMS - lastActionTimeMs, idpw); + } + } + private void dumpBucketExpiryTimes(IndentingPrintWriter idpw, AppUsageHistory appUsageHistory, long totalElapsedTimeMs) { idpw.print(" expiryTimes="); diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index 4952894c068b..502913063a00 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -100,6 +100,7 @@ import android.os.UserHandle; import android.provider.DeviceConfig; import android.provider.Settings.Global; import android.telephony.TelephonyManager; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.IndentingPrintWriter; import android.util.Slog; @@ -130,6 +131,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; @@ -375,6 +377,15 @@ public class AppStandbyController ConstantsObserver.DEFAULT_BROADCAST_RESPONSE_FG_THRESHOLD_STATE; /** + * Map of last known values of keys in {@link DeviceConfig#NAMESPACE_APP_STANDBY}. + * + * Note: We are intentionally not guarding this by any lock since this is only updated on + * a handler thread and when querying, if we do end up seeing slightly older values, it is fine + * since the values are only used in tests and doesn't need to be queried in any other cases. + */ + private final Map<String, String> mAppStandbyProperties = new ArrayMap<>(); + + /** * Whether we should allow apps into the * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket or not. * If false, any attempts to put an app into the bucket will put the app into the @@ -887,8 +898,11 @@ public class AppStandbyController } } + final long elapsedLastUsedByUserTimeDelta = app.lastUsedByUserElapsedTime >= 0 + ? elapsedTimeAdjusted - app.lastUsedByUserElapsedTime + : Long.MAX_VALUE; if (app.lastRestrictAttemptElapsedTime > app.lastUsedByUserElapsedTime - && elapsedTimeAdjusted - app.lastUsedByUserElapsedTime + && elapsedLastUsedByUserTimeDelta >= mInjector.getAutoRestrictedBucketDelayMs()) { newBucket = STANDBY_BUCKET_RESTRICTED; reason = app.lastRestrictReason; @@ -1837,6 +1851,12 @@ public class AppStandbyController } @Override + @Nullable + public String getAppStandbyConstant(@NonNull String key) { + return mAppStandbyProperties.get(key); + } + + @Override public void flushToDisk() { synchronized (mAppIdleLock) { mAppIdleHistory.writeAppIdleTimes(mInjector.elapsedRealtime()); @@ -2774,6 +2794,7 @@ public class AppStandbyController } break; } + mAppStandbyProperties.put(name, properties.getString(name, null)); } } } diff --git a/core/api/current.txt b/core/api/current.txt index 24d48ab5996a..598881db0c50 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -4532,22 +4532,36 @@ package android.app { } public static class ActivityManager.TaskDescription implements android.os.Parcelable { - ctor public ActivityManager.TaskDescription(String, @DrawableRes int, int); - ctor public ActivityManager.TaskDescription(String, @DrawableRes int); - ctor public ActivityManager.TaskDescription(String); - ctor public ActivityManager.TaskDescription(); + ctor @Deprecated public ActivityManager.TaskDescription(String, @DrawableRes int, int); + ctor @Deprecated public ActivityManager.TaskDescription(String, @DrawableRes int); + ctor @Deprecated public ActivityManager.TaskDescription(String); + ctor @Deprecated public ActivityManager.TaskDescription(); ctor @Deprecated public ActivityManager.TaskDescription(String, android.graphics.Bitmap, int); ctor @Deprecated public ActivityManager.TaskDescription(String, android.graphics.Bitmap); ctor public ActivityManager.TaskDescription(android.app.ActivityManager.TaskDescription); method public int describeContents(); + method @ColorInt public int getBackgroundColor(); method @Deprecated public android.graphics.Bitmap getIcon(); method public String getLabel(); - method public int getPrimaryColor(); + method @ColorInt public int getNavigationBarColor(); + method @ColorInt public int getPrimaryColor(); + method @ColorInt public int getStatusBarColor(); method public void readFromParcel(android.os.Parcel); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.ActivityManager.TaskDescription> CREATOR; } + public static final class ActivityManager.TaskDescription.Builder { + ctor public ActivityManager.TaskDescription.Builder(); + method @NonNull public android.app.ActivityManager.TaskDescription build(); + method @NonNull public android.app.ActivityManager.TaskDescription.Builder setBackgroundColor(@ColorInt int); + method @NonNull public android.app.ActivityManager.TaskDescription.Builder setIcon(@DrawableRes int); + method @NonNull public android.app.ActivityManager.TaskDescription.Builder setLabel(@Nullable String); + method @NonNull public android.app.ActivityManager.TaskDescription.Builder setNavigationBarColor(@ColorInt int); + method @NonNull public android.app.ActivityManager.TaskDescription.Builder setPrimaryColor(@ColorInt int); + method @NonNull public android.app.ActivityManager.TaskDescription.Builder setStatusBarColor(@ColorInt int); + } + public class ActivityOptions { method @Nullable public android.graphics.Rect getLaunchBounds(); method public int getLaunchDisplayId(); @@ -5815,6 +5829,7 @@ package android.app { public class LocaleManager { method @NonNull public android.os.LocaleList getApplicationLocales(); method @NonNull @RequiresPermission(value="android.permission.READ_APP_SPECIFIC_LOCALES", conditional=true) public android.os.LocaleList getApplicationLocales(@NonNull String); + method @NonNull public android.os.LocaleList getSystemLocales(); method public void setApplicationLocales(@NonNull android.os.LocaleList); } @@ -7465,6 +7480,8 @@ package android.app.admin { method public CharSequence getStartUserSessionMessage(@NonNull android.content.ComponentName); method @Deprecated public boolean getStorageEncryption(@Nullable android.content.ComponentName); method public int getStorageEncryptionStatus(); + method @Nullable public String getString(@NonNull String, @NonNull java.util.function.Supplier<java.lang.String>); + method @Nullable public String getString(@NonNull String, @NonNull java.util.function.Supplier<java.lang.String>, @NonNull java.lang.Object...); method @Nullable public android.app.admin.SystemUpdatePolicy getSystemUpdatePolicy(); method @Nullable public android.os.PersistableBundle getTransferOwnershipBundle(); method @Nullable public java.util.List<android.os.PersistableBundle> getTrustAgentConfiguration(@Nullable android.content.ComponentName, @NonNull android.content.ComponentName); @@ -7706,6 +7723,7 @@ package android.app.admin { field public static final String EXTRA_PROVISIONING_SKIP_ENCRYPTION = "android.app.extra.PROVISIONING_SKIP_ENCRYPTION"; field @Deprecated public static final String EXTRA_PROVISIONING_SKIP_USER_CONSENT = "android.app.extra.PROVISIONING_SKIP_USER_CONSENT"; field public static final String EXTRA_PROVISIONING_TIME_ZONE = "android.app.extra.PROVISIONING_TIME_ZONE"; + field public static final String EXTRA_PROVISIONING_USE_MOBILE_DATA = "android.app.extra.PROVISIONING_USE_MOBILE_DATA"; field public static final String EXTRA_PROVISIONING_WIFI_ANONYMOUS_IDENTITY = "android.app.extra.PROVISIONING_WIFI_ANONYMOUS_IDENTITY"; field public static final String EXTRA_PROVISIONING_WIFI_CA_CERTIFICATE = "android.app.extra.PROVISIONING_WIFI_CA_CERTIFICATE"; field public static final String EXTRA_PROVISIONING_WIFI_DOMAIN = "android.app.extra.PROVISIONING_WIFI_DOMAIN"; @@ -9736,8 +9754,8 @@ package android.content { method @Nullable public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, @Nullable String, @Nullable android.os.Handler, int); method @Deprecated @RequiresPermission(android.Manifest.permission.BROADCAST_STICKY) public abstract void removeStickyBroadcast(@RequiresPermission android.content.Intent); method @Deprecated @RequiresPermission(allOf={"android.permission.INTERACT_ACROSS_USERS", android.Manifest.permission.BROADCAST_STICKY}) public abstract void removeStickyBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle); - method public void revokeOwnPermissionOnKill(@NonNull String); - method public void revokeOwnPermissionsOnKill(@NonNull java.util.Collection<java.lang.String>); + method public void revokeSelfPermissionOnKill(@NonNull String); + method public void revokeSelfPermissionsOnKill(@NonNull java.util.Collection<java.lang.String>); method public abstract void revokeUriPermission(android.net.Uri, int); method public abstract void revokeUriPermission(String, android.net.Uri, int); method public abstract void sendBroadcast(@RequiresPermission android.content.Intent); @@ -10125,12 +10143,16 @@ package android.content { method @Nullable public long[] getLongArrayExtra(String); method public long getLongExtra(String, long); method @Nullable public String getPackage(); - method @Nullable public android.os.Parcelable[] getParcelableArrayExtra(String); - method @Nullable public <T extends android.os.Parcelable> java.util.ArrayList<T> getParcelableArrayListExtra(String); - method @Nullable public <T extends android.os.Parcelable> T getParcelableExtra(String); + method @Deprecated @Nullable public android.os.Parcelable[] getParcelableArrayExtra(String); + method @Nullable public <T> T[] getParcelableArrayExtra(@Nullable String, @NonNull Class<T>); + method @Deprecated @Nullable public <T extends android.os.Parcelable> java.util.ArrayList<T> getParcelableArrayListExtra(String); + method @Nullable public <T> java.util.ArrayList<T> getParcelableArrayListExtra(@Nullable String, @NonNull Class<? extends T>); + method @Deprecated @Nullable public <T extends android.os.Parcelable> T getParcelableExtra(String); + method @Nullable public <T> T getParcelableExtra(@Nullable String, @NonNull Class<T>); method @Nullable public String getScheme(); method @Nullable public android.content.Intent getSelector(); - method @Nullable public java.io.Serializable getSerializableExtra(String); + method @Deprecated @Nullable public java.io.Serializable getSerializableExtra(String); + method @Nullable public <T extends java.io.Serializable> T getSerializableExtra(@Nullable String, @NonNull Class<T>); method @Nullable public short[] getShortArrayExtra(String); method public short getShortExtra(String, short); method @Nullable public android.graphics.Rect getSourceBounds(); @@ -11240,6 +11262,33 @@ package android.content.pm { field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.Attribution> CREATOR; } + public final class Capability implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public String getName(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.Capability> CREATOR; + } + + public static final class Capability.Builder { + ctor public Capability.Builder(@NonNull String); + method @NonNull public android.content.pm.Capability build(); + } + + public final class CapabilityParams implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.List<java.lang.String> getAliases(); + method @NonNull public String getName(); + method @NonNull public String getValue(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.CapabilityParams> CREATOR; + } + + public static final class CapabilityParams.Builder { + ctor public CapabilityParams.Builder(@NonNull String, @NonNull String); + method @NonNull public android.content.pm.CapabilityParams.Builder addAlias(@NonNull String); + method @NonNull public android.content.pm.CapabilityParams build(); + } + public final class ChangedPackages implements android.os.Parcelable { ctor public ChangedPackages(int, @NonNull java.util.List<java.lang.String>); method public int describeContents(); @@ -12301,7 +12350,8 @@ package android.content.pm { method @NonNull public static android.content.pm.ShortcutInfo createFromGenericDocument(@NonNull android.content.Context, @NonNull android.app.appsearch.GenericDocument); method public int describeContents(); method @Nullable public android.content.ComponentName getActivity(); - method @NonNull public java.util.List<java.lang.String> getCapabilityParameterValues(@NonNull String, @NonNull String); + method @NonNull public java.util.List<android.content.pm.Capability> getCapabilities(); + method @NonNull public java.util.List<android.content.pm.CapabilityParams> getCapabilityParams(@NonNull android.content.pm.Capability); method @Nullable public java.util.Set<java.lang.String> getCategories(); method @Nullable public CharSequence getDisabledMessage(); method public int getDisabledReason(); @@ -12316,7 +12366,6 @@ package android.content.pm { method public int getRank(); method @Nullable public CharSequence getShortLabel(); method public android.os.UserHandle getUserHandle(); - method public boolean hasCapability(@NonNull String); method public boolean hasKeyFieldsOnly(); method public boolean isCached(); method public boolean isDeclaredInManifest(); @@ -12341,7 +12390,7 @@ package android.content.pm { public static class ShortcutInfo.Builder { ctor public ShortcutInfo.Builder(android.content.Context, String); - method @NonNull public android.content.pm.ShortcutInfo.Builder addCapabilityBinding(@NonNull String, @Nullable String, @Nullable java.util.List<java.lang.String>); + method @NonNull public android.content.pm.ShortcutInfo.Builder addCapabilityBinding(@NonNull android.content.pm.Capability, @Nullable android.content.pm.CapabilityParams); method @NonNull public android.content.pm.ShortcutInfo build(); method @NonNull public android.content.pm.ShortcutInfo.Builder setActivity(@NonNull android.content.ComponentName); method @NonNull public android.content.pm.ShortcutInfo.Builder setCategories(java.util.Set<java.lang.String>); @@ -19487,26 +19536,26 @@ package android.location { method @NonNull public static String convert(@FloatRange double, int); method @FloatRange public static double convert(@NonNull String); method public int describeContents(); - method public static void distanceBetween(@FloatRange double, @FloatRange double, @FloatRange double, @FloatRange double, float[]); - method @FloatRange public float distanceTo(@NonNull android.location.Location); - method public void dump(@NonNull android.util.Printer, @Nullable String); - method @FloatRange public float getAccuracy(); + method public static void distanceBetween(@FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, float[]); + method @FloatRange(from=0.0) public float distanceTo(@NonNull android.location.Location); + method @Deprecated public void dump(@NonNull android.util.Printer, @Nullable String); + method @FloatRange(from=0.0) public float getAccuracy(); method @FloatRange public double getAltitude(); - method @FloatRange(from=0.0f, to=360.0f, toInclusive=false) public float getBearing(); - method @FloatRange public float getBearingAccuracyDegrees(); - method @IntRange public long getElapsedRealtimeAgeMillis(); - method @IntRange public long getElapsedRealtimeAgeMillis(@IntRange long); - method @IntRange public long getElapsedRealtimeMillis(); - method @IntRange public long getElapsedRealtimeNanos(); - method @FloatRange public double getElapsedRealtimeUncertaintyNanos(); + method @FloatRange(from=0.0, to=360.0, toInclusive=false) public float getBearing(); + method @FloatRange(from=0.0) public float getBearingAccuracyDegrees(); + method @IntRange(from=0) public long getElapsedRealtimeAgeMillis(); + method public long getElapsedRealtimeAgeMillis(@IntRange(from=0) long); + method @IntRange(from=0) public long getElapsedRealtimeMillis(); + method @IntRange(from=0) public long getElapsedRealtimeNanos(); + method @FloatRange(from=0.0) public double getElapsedRealtimeUncertaintyNanos(); method @Nullable public android.os.Bundle getExtras(); - method @FloatRange public double getLatitude(); - method @FloatRange public double getLongitude(); + method @FloatRange(from=-90.0, to=90.0) public double getLatitude(); + method @FloatRange(from=-180.0, to=180.0) public double getLongitude(); method @Nullable public String getProvider(); - method @FloatRange public float getSpeed(); - method @FloatRange public float getSpeedAccuracyMetersPerSecond(); - method @IntRange public long getTime(); - method @FloatRange public float getVerticalAccuracyMeters(); + method @FloatRange(from=0.0) public float getSpeed(); + method @FloatRange(from=0.0) public float getSpeedAccuracyMetersPerSecond(); + method @IntRange(from=0) public long getTime(); + method @FloatRange(from=0.0) public float getVerticalAccuracyMeters(); method public boolean hasAccuracy(); method public boolean hasAltitude(); method public boolean hasBearing(); @@ -19528,21 +19577,21 @@ package android.location { method public void removeVerticalAccuracy(); method public void reset(); method public void set(@NonNull android.location.Location); - method public void setAccuracy(@FloatRange float); + method public void setAccuracy(@FloatRange(from=0.0) float); method public void setAltitude(@FloatRange double); method public void setBearing(@FloatRange(fromInclusive=false, toInclusive=false) float); - method public void setBearingAccuracyDegrees(@FloatRange float); - method public void setElapsedRealtimeNanos(@IntRange long); - method public void setElapsedRealtimeUncertaintyNanos(@FloatRange double); + method public void setBearingAccuracyDegrees(@FloatRange(from=0.0) float); + method public void setElapsedRealtimeNanos(@IntRange(from=0) long); + method public void setElapsedRealtimeUncertaintyNanos(@FloatRange(from=0.0) double); method public void setExtras(@Nullable android.os.Bundle); - method public void setLatitude(@FloatRange double); - method public void setLongitude(@FloatRange double); + method public void setLatitude(@FloatRange(from=-90.0, to=90.0) double); + method public void setLongitude(@FloatRange(from=-180.0, to=180.0) double); method public void setMock(boolean); method public void setProvider(@Nullable String); - method public void setSpeed(@FloatRange float); - method public void setSpeedAccuracyMetersPerSecond(@FloatRange float); - method public void setTime(@IntRange long); - method public void setVerticalAccuracyMeters(@FloatRange float); + method public void setSpeed(@FloatRange(from=0.0) float); + method public void setSpeedAccuracyMetersPerSecond(@FloatRange(from=0.0) float); + method public void setTime(@IntRange(from=0) long); + method public void setVerticalAccuracyMeters(@FloatRange(from=0.0) float); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.location.Location> CREATOR; field public static final int FORMAT_DEGREES = 0; // 0x0 @@ -22186,7 +22235,7 @@ package android.media { field public static final String KEY_AAC_DRC_OUTPUT_LOUDNESS = "aac-drc-output-loudness"; field public static final String KEY_AAC_DRC_TARGET_REFERENCE_LEVEL = "aac-target-ref-level"; field public static final String KEY_AAC_ENCODED_TARGET_LEVEL = "aac-encoded-target-level"; - field public static final String KEY_AAC_MAX_OUTPUT_CHANNEL_COUNT = "aac-max-output-channel_count"; + field @Deprecated public static final String KEY_AAC_MAX_OUTPUT_CHANNEL_COUNT = "aac-max-output-channel_count"; field public static final String KEY_AAC_PROFILE = "aac-profile"; field public static final String KEY_AAC_SBR_MODE = "aac-sbr-mode"; field public static final String KEY_ALLOW_FRAME_DROP = "allow-frame-drop"; @@ -22205,6 +22254,10 @@ package android.media { field public static final String KEY_COLOR_TRANSFER_REQUEST = "color-transfer-request"; field public static final String KEY_COMPLEXITY = "complexity"; field public static final String KEY_CREATE_INPUT_SURFACE_SUSPENDED = "create-input-buffers-suspended"; + field public static final String KEY_CROP_BOTTOM = "crop-bottom"; + field public static final String KEY_CROP_LEFT = "crop-left"; + field public static final String KEY_CROP_RIGHT = "crop-right"; + field public static final String KEY_CROP_TOP = "crop-top"; field public static final String KEY_DURATION = "durationUs"; field public static final String KEY_ENCODER_DELAY = "encoder-delay"; field public static final String KEY_ENCODER_PADDING = "encoder-padding"; @@ -22894,7 +22947,6 @@ package android.media { method public int describeContents(); method @Nullable public String getClientPackageName(); method public int getConnectionState(); - method @NonNull public java.util.Set<java.lang.String> getDeduplicationIds(); method @Nullable public CharSequence getDescription(); method @Nullable public android.os.Bundle getExtras(); method @NonNull public java.util.List<java.lang.String> getFeatures(); @@ -22928,7 +22980,6 @@ package android.media { method @NonNull public android.media.MediaRoute2Info.Builder clearFeatures(); method @NonNull public android.media.MediaRoute2Info.Builder setClientPackageName(@Nullable String); method @NonNull public android.media.MediaRoute2Info.Builder setConnectionState(int); - method @NonNull public android.media.MediaRoute2Info.Builder setDeduplicationIds(@NonNull java.util.Set<java.lang.String>); method @NonNull public android.media.MediaRoute2Info.Builder setDescription(@Nullable CharSequence); method @NonNull public android.media.MediaRoute2Info.Builder setExtras(@Nullable android.os.Bundle); method @NonNull public android.media.MediaRoute2Info.Builder setIconUri(@Nullable android.net.Uri); @@ -23455,11 +23506,8 @@ package android.media { public final class RouteDiscoveryPreference implements android.os.Parcelable { method public int describeContents(); - method @NonNull public java.util.List<java.lang.String> getAllowedPackages(); - method @NonNull public java.util.List<java.lang.String> getDeduplicationPackageOrder(); method @NonNull public java.util.List<java.lang.String> getPreferredFeatures(); method public boolean shouldPerformActiveScan(); - method public boolean shouldRemoveDuplicates(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.media.RouteDiscoveryPreference> CREATOR; } @@ -23468,8 +23516,6 @@ package android.media { ctor public RouteDiscoveryPreference.Builder(@NonNull java.util.List<java.lang.String>, boolean); ctor public RouteDiscoveryPreference.Builder(@NonNull android.media.RouteDiscoveryPreference); method @NonNull public android.media.RouteDiscoveryPreference build(); - method @NonNull public android.media.RouteDiscoveryPreference.Builder setAllowedPackages(@NonNull java.util.List<java.lang.String>); - method @NonNull public android.media.RouteDiscoveryPreference.Builder setDeduplicationPackageOrder(@NonNull java.util.List<java.lang.String>); method @NonNull public android.media.RouteDiscoveryPreference.Builder setPreferredFeatures(@NonNull java.util.List<java.lang.String>); method @NonNull public android.media.RouteDiscoveryPreference.Builder setShouldPerformActiveScan(boolean); } @@ -23548,17 +23594,24 @@ package android.media { } public class Spatializer { + method public void addOnHeadTrackerAvailableListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.Spatializer.OnHeadTrackerAvailableListener); method public void addOnSpatializerStateChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.Spatializer.OnSpatializerStateChangedListener); method public boolean canBeSpatialized(@NonNull android.media.AudioAttributes, @NonNull android.media.AudioFormat); method public int getImmersiveAudioLevel(); method public boolean isAvailable(); method public boolean isEnabled(); + method public boolean isHeadTrackerAvailable(); + method public void removeOnHeadTrackerAvailableListener(@NonNull android.media.Spatializer.OnHeadTrackerAvailableListener); method public void removeOnSpatializerStateChangedListener(@NonNull android.media.Spatializer.OnSpatializerStateChangedListener); field public static final int SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL = 1; // 0x1 field public static final int SPATIALIZER_IMMERSIVE_LEVEL_NONE = 0; // 0x0 field public static final int SPATIALIZER_IMMERSIVE_LEVEL_OTHER = -1; // 0xffffffff } + public static interface Spatializer.OnHeadTrackerAvailableListener { + method public void onHeadTrackerAvailableChanged(@NonNull android.media.Spatializer, boolean); + } + public static interface Spatializer.OnSpatializerStateChangedListener { method public void onSpatializerAvailableChanged(@NonNull android.media.Spatializer, boolean); method public void onSpatializerEnabledChanged(@NonNull android.media.Spatializer, boolean); @@ -25191,17 +25244,23 @@ package android.media.tv { } public final class CommandRequest extends android.media.tv.BroadcastInfoRequest implements android.os.Parcelable { - ctor public CommandRequest(int, int, @NonNull String, @NonNull String, @NonNull String); + ctor public CommandRequest(int, int, @NonNull String, @NonNull String, @NonNull String, @NonNull String); + method @NonNull public String getArgumentType(); method @NonNull public String getArguments(); method @NonNull public String getName(); - method @NonNull public String getNameSpace(); + method @NonNull public String getNamespace(); + field public static final String ARGUMENT_TYPE_JSON = "json"; + field public static final String ARGUMENT_TYPE_XML = "xml"; field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.CommandRequest> CREATOR; } public final class CommandResponse extends android.media.tv.BroadcastInfoResponse implements android.os.Parcelable { - ctor public CommandResponse(int, int, int, @Nullable String); + ctor public CommandResponse(int, int, int, @Nullable String, @NonNull String); method @Nullable public String getResponse(); + method @NonNull public String getResponseType(); field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.CommandResponse> CREATOR; + field public static final String RESPONSE_TYPE_JSON = "json"; + field public static final String RESPONSE_TYPE_XML = "xml"; } public final class DsmccRequest extends android.media.tv.BroadcastInfoRequest implements android.os.Parcelable { @@ -25266,7 +25325,7 @@ package android.media.tv { ctor public StreamEventResponse(int, int, int, int, long, @Nullable byte[]); method @Nullable public byte[] getData(); method public int getEventId(); - method public long getNpt(); + method public long getNptMillis(); field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.StreamEventResponse> CREATOR; } @@ -25296,7 +25355,7 @@ package android.media.tv { public final class TimelineResponse extends android.media.tv.BroadcastInfoResponse implements android.os.Parcelable { ctor public TimelineResponse(int, int, int, @Nullable String, int, int, long, long); - method @Nullable public String getSelector(); + method @Nullable public android.net.Uri getSelector(); method public long getTicks(); method public int getUnitsPerSecond(); method public int getUnitsPerTick(); @@ -26059,7 +26118,7 @@ package android.media.tv { method public void onContentAllowed(String); method public void onContentBlocked(String, android.media.tv.TvContentRating); method public void onDisconnected(String); - method public void onSignalStrength(@NonNull String, int); + method public void onSignalStrengthUpdated(@NonNull String, int); method public void onTimeShiftStatusChanged(String, int); method public void onTrackSelected(String, int, String); method public void onTracksChanged(String, java.util.List<android.media.tv.TvTrackInfo>); @@ -26074,40 +26133,16 @@ package android.media.tv { package android.media.tv.interactive { public final class AppLinkInfo implements android.os.Parcelable { + ctor public AppLinkInfo(@NonNull String, @NonNull String, @NonNull String); method public int describeContents(); - method @NonNull public String getClassName(); - method @NonNull public String getPackageName(); - method @Nullable public String getUriHost(); - method @Nullable public String getUriPrefix(); - method @Nullable public String getUriScheme(); + method @NonNull public android.content.ComponentName getComponentName(); + method @NonNull public android.net.Uri getUri(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.interactive.AppLinkInfo> CREATOR; } - public static final class AppLinkInfo.Builder { - ctor public AppLinkInfo.Builder(@NonNull String, @NonNull String); - method @NonNull public android.media.tv.interactive.AppLinkInfo build(); - method @NonNull public android.media.tv.interactive.AppLinkInfo.Builder setUriHost(@NonNull String); - method @NonNull public android.media.tv.interactive.AppLinkInfo.Builder setUriPrefix(@NonNull String); - method @NonNull public android.media.tv.interactive.AppLinkInfo.Builder setUriScheme(@NonNull String); - } - - public final class TvInteractiveAppInfo implements android.os.Parcelable { - ctor public TvInteractiveAppInfo(@NonNull android.content.Context, @NonNull android.content.ComponentName); - method public int describeContents(); - method @NonNull public String getId(); - method @Nullable public android.content.pm.ServiceInfo getServiceInfo(); - method @NonNull public int getSupportedTypes(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.interactive.TvInteractiveAppInfo> CREATOR; - field public static final int INTERACTIVE_APP_TYPE_ATSC = 2; // 0x2 - field public static final int INTERACTIVE_APP_TYPE_GINGA = 4; // 0x4 - field public static final int INTERACTIVE_APP_TYPE_HBBTV = 1; // 0x1 - } - public final class TvInteractiveAppManager { - method @NonNull public java.util.List<android.media.tv.interactive.TvInteractiveAppInfo> getTvInteractiveAppServiceList(); - method public void prepare(@NonNull String, int); + method @NonNull public java.util.List<android.media.tv.interactive.TvInteractiveAppServiceInfo> getTvInteractiveAppServiceList(); method public void registerAppLinkInfo(@NonNull String, @NonNull android.media.tv.interactive.AppLinkInfo); method public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.interactive.TvInteractiveAppManager.TvInteractiveAppCallback); method public void sendAppLinkCommand(@NonNull String, @NonNull android.os.Bundle); @@ -26130,6 +26165,7 @@ package android.media.tv.interactive { field public static final String INTENT_KEY_BI_INTERACTIVE_APP_TYPE = "bi_interactive_app_type"; field public static final String INTENT_KEY_BI_INTERACTIVE_APP_URI = "bi_interactive_app_uri"; field public static final String INTENT_KEY_CHANNEL_URI = "channel_uri"; + field public static final String INTENT_KEY_COMMAND_TYPE = "command_type"; field public static final String INTENT_KEY_INTERACTIVE_APP_SERVICE_ID = "interactive_app_id"; field public static final String INTENT_KEY_TV_INPUT_ID = "tv_input_id"; field public static final int INTERACTIVE_APP_STATE_ERROR = 3; // 0x3 @@ -26158,7 +26194,6 @@ package android.media.tv.interactive { method public void onAppLinkCommand(@NonNull android.os.Bundle); method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent); method @Nullable public abstract android.media.tv.interactive.TvInteractiveAppService.Session onCreateSession(@NonNull String, int); - method public abstract void onPrepare(int); method public void onRegisterAppLinkInfo(@NonNull android.media.tv.interactive.AppLinkInfo); method public void onUnregisterAppLinkInfo(@NonNull android.media.tv.interactive.AppLinkInfo); field public static final String COMMAND_PARAMETER_KEY_CHANGE_CHANNEL_QUIETLY = "command_change_channel_quietly"; @@ -26179,6 +26214,7 @@ package android.media.tv.interactive { public abstract static class TvInteractiveAppService.Session implements android.view.KeyEvent.Callback { ctor public TvInteractiveAppService.Session(@NonNull android.content.Context); + method public boolean isMediaViewEnabled(); method @CallSuper public void layoutSurface(int, int, int, int); method @CallSuper public final void notifyBiInteractiveAppCreated(@NonNull android.net.Uri, @Nullable String); method @CallSuper public void notifySessionStateChanged(int, int); @@ -26187,23 +26223,24 @@ package android.media.tv.interactive { method public void onBroadcastInfoResponse(@NonNull android.media.tv.BroadcastInfoResponse); method public void onContentAllowed(); method public void onContentBlocked(@NonNull android.media.tv.TvContentRating); - method public void onCreateBiInteractiveApp(@NonNull android.net.Uri, @Nullable android.os.Bundle); + method public void onCreateBiInteractiveAppRequest(@NonNull android.net.Uri, @Nullable android.os.Bundle); method @Nullable public android.view.View onCreateMediaView(); method public void onCurrentChannelLcn(int); method public void onCurrentChannelUri(@Nullable android.net.Uri); method public void onCurrentTvInputId(@Nullable String); - method public void onDestroyBiInteractiveApp(@NonNull String); + method public void onDestroyBiInteractiveAppRequest(@NonNull String); method public boolean onGenericMotionEvent(@NonNull android.view.MotionEvent); method public boolean onKeyDown(int, @NonNull android.view.KeyEvent); method public boolean onKeyLongPress(int, @NonNull android.view.KeyEvent); method public boolean onKeyMultiple(int, int, @NonNull android.view.KeyEvent); method public boolean onKeyUp(int, @NonNull android.view.KeyEvent); - method public void onMediaViewSizeChanged(int, int); + method public void onMediaViewSizeChanged(@Px int, @Px int); method public abstract void onRelease(); method public void onResetInteractiveApp(); method public abstract boolean onSetSurface(@Nullable android.view.Surface); method public void onSetTeletextAppEnabled(boolean); method public void onSignalStrength(int); + method public void onSigningResult(@NonNull String, @NonNull byte[]); method public void onStartInteractiveApp(); method public void onStopInteractiveApp(); method public void onStreamVolume(float); @@ -26222,6 +26259,7 @@ package android.media.tv.interactive { method @CallSuper public void requestCurrentChannelLcn(); method @CallSuper public void requestCurrentChannelUri(); method @CallSuper public void requestCurrentTvInputId(); + method @CallSuper public void requestSigning(@NonNull String, @NonNull String, @NonNull String, @NonNull byte[]); method @CallSuper public void requestStreamVolume(); method @CallSuper public void requestTrackInfoList(); method @CallSuper public void sendPlaybackCommandRequest(@NonNull String, @Nullable android.os.Bundle); @@ -26229,6 +26267,19 @@ package android.media.tv.interactive { method @CallSuper public void setVideoBounds(@NonNull android.graphics.Rect); } + public final class TvInteractiveAppServiceInfo implements android.os.Parcelable { + ctor public TvInteractiveAppServiceInfo(@NonNull android.content.Context, @NonNull android.content.ComponentName); + method public int describeContents(); + method @NonNull public String getId(); + method @Nullable public android.content.pm.ServiceInfo getServiceInfo(); + method @NonNull public int getSupportedTypes(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.interactive.TvInteractiveAppServiceInfo> CREATOR; + field public static final int INTERACTIVE_APP_TYPE_ATSC = 2; // 0x2 + field public static final int INTERACTIVE_APP_TYPE_GINGA = 4; // 0x4 + field public static final int INTERACTIVE_APP_TYPE_HBBTV = 1; // 0x1 + } + public class TvInteractiveAppView extends android.view.ViewGroup { ctor public TvInteractiveAppView(@NonNull android.content.Context); ctor public TvInteractiveAppView(@NonNull android.content.Context, @Nullable android.util.AttributeSet); @@ -26238,6 +26289,7 @@ package android.media.tv.interactive { method public void createBiInteractiveApp(@NonNull android.net.Uri, @Nullable android.os.Bundle); method public void destroyBiInteractiveApp(@NonNull String); method public boolean dispatchUnhandledInputEvent(@NonNull android.view.InputEvent); + method @Nullable public android.media.tv.interactive.TvInteractiveAppView.OnUnhandledInputEventListener getOnUnhandledInputEventListener(); method public void onAttachedToWindow(); method public void onDetachedFromWindow(); method public void onLayout(boolean, int, int, int, int); @@ -26250,6 +26302,7 @@ package android.media.tv.interactive { method public void sendCurrentChannelLcn(int); method public void sendCurrentChannelUri(@Nullable android.net.Uri); method public void sendCurrentTvInputId(@Nullable String); + method public void sendSigningResult(@NonNull String, @NonNull byte[]); method public void sendStreamVolume(float); method public void sendTrackInfoList(@Nullable java.util.List<android.media.tv.TvTrackInfo>); method public void setCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.interactive.TvInteractiveAppView.TvInteractiveAppCallback); @@ -26258,6 +26311,11 @@ package android.media.tv.interactive { method public int setTvView(@Nullable android.media.tv.TvView); method public void startInteractiveApp(); method public void stopInteractiveApp(); + field public static final String BI_INTERACTIVE_APP_KEY_ALIAS = "alias"; + field public static final String BI_INTERACTIVE_APP_KEY_CERTIFICATE = "certificate"; + field public static final String BI_INTERACTIVE_APP_KEY_HTTP_ADDITIONAL_HEADERS = "http_additional_headers"; + field public static final String BI_INTERACTIVE_APP_KEY_HTTP_USER_AGENT = "http_user_agent"; + field public static final String BI_INTERACTIVE_APP_KEY_PRIVATE_KEY = "private_key"; } public static interface TvInteractiveAppView.OnUnhandledInputEventListener { @@ -26271,6 +26329,7 @@ package android.media.tv.interactive { method public void onRequestCurrentChannelLcn(@NonNull String); method public void onRequestCurrentChannelUri(@NonNull String); method public void onRequestCurrentTvInputId(@NonNull String); + method public void onRequestSigning(@NonNull String, @NonNull String, @NonNull String, @NonNull String, @NonNull byte[]); method public void onRequestStreamVolume(@NonNull String); method public void onRequestTrackInfoList(@NonNull String); method public void onSetVideoBounds(@NonNull String, @NonNull android.graphics.Rect); @@ -43796,6 +43855,7 @@ package android.telephony.data { method public int getNetworkTypeBitmask(); method public String getOperatorNumeric(); method public String getPassword(); + method public int getProfileId(); method public int getProtocol(); method @Deprecated public java.net.InetAddress getProxyAddress(); method public String getProxyAddressAsString(); @@ -43803,6 +43863,7 @@ package android.telephony.data { method public int getRoamingProtocol(); method public String getUser(); method public boolean isEnabled(); + method public boolean isPersistent(); method public void writeToParcel(@NonNull android.os.Parcel, int); field public static final int AUTH_TYPE_CHAP = 2; // 0x2 field public static final int AUTH_TYPE_NONE = 0; // 0x0 @@ -57906,7 +57967,7 @@ package android.window { } public interface OnBackInvokedDispatcher { - method public void registerOnBackInvokedCallback(@NonNull android.window.OnBackInvokedCallback, @IntRange(from=0) int); + method public void registerOnBackInvokedCallback(@IntRange(from=0) int, @NonNull android.window.OnBackInvokedCallback); method public void unregisterOnBackInvokedCallback(@NonNull android.window.OnBackInvokedCallback); field public static final int PRIORITY_DEFAULT = 0; // 0x0 field public static final int PRIORITY_OVERLAY = 1000000; // 0xf4240 diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 5df988153752..c8c94b5fca70 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -244,6 +244,7 @@ package android { field public static final String READ_APP_SPECIFIC_LOCALES = "android.permission.READ_APP_SPECIFIC_LOCALES"; field public static final String READ_CARRIER_APP_INFO = "android.permission.READ_CARRIER_APP_INFO"; field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS"; + field public static final String READ_CLIPBOARD_IN_BACKGROUND = "android.permission.READ_CLIPBOARD_IN_BACKGROUND"; field public static final String READ_CONTENT_RATING_SYSTEMS = "android.permission.READ_CONTENT_RATING_SYSTEMS"; field public static final String READ_DEVICE_CONFIG = "android.permission.READ_DEVICE_CONFIG"; field public static final String READ_DREAM_STATE = "android.permission.READ_DREAM_STATE"; @@ -1079,6 +1080,7 @@ package android.app.admin { method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public int checkProvisioningPrecondition(@NonNull String, @NonNull String); method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public android.os.UserHandle createAndProvisionManagedProfile(@NonNull android.app.admin.ManagedProfileProvisioningParams) throws android.app.admin.ProvisioningException; method @Nullable public android.content.Intent createProvisioningIntentFromNfcIntent(@NonNull android.content.Intent); + method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void finalizeWorkProfileProvisioning(@NonNull android.os.UserHandle, @Nullable android.accounts.Account); method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean getBluetoothContactSharingDisabled(@NonNull android.os.UserHandle); method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwner(); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public android.content.ComponentName getDeviceOwnerComponentOnAnyUser(); @@ -1089,8 +1091,6 @@ package android.app.admin { method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_ADMIN_POLICY}) public java.util.List<java.lang.String> getPermittedInputMethodsForCurrentUser(); method @Nullable public android.content.ComponentName getProfileOwner() throws java.lang.IllegalArgumentException; method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public String getProfileOwnerNameAsUser(int) throws java.lang.IllegalArgumentException; - method @Nullable public String getString(@NonNull String, @NonNull java.util.function.Supplier<java.lang.String>); - method @Nullable public String getString(@NonNull String, @NonNull java.util.function.Supplier<java.lang.String>, @NonNull java.lang.Object...); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public int getUserProvisioningState(); method public boolean isDeviceManaged(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioned(); @@ -1103,8 +1103,8 @@ package android.app.admin { method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long, boolean); method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public boolean packageHasActiveAdmins(String); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, android.Manifest.permission.PROVISION_DEMO_DEVICE}) public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException; - method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES) public void resetDrawables(@NonNull String[]); - method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES) public void resetStrings(@NonNull String[]); + method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES) public void resetDrawables(@NonNull java.util.Set<java.lang.String>); + method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES) public void resetStrings(@NonNull java.util.Set<java.lang.String>); method @RequiresPermission(android.Manifest.permission.SEND_LOST_MODE_LOCATION_UPDATES) public void sendLostModeLocationUpdate(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public boolean setActiveProfileOwner(@NonNull android.content.ComponentName, String) throws java.lang.IllegalArgumentException; method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setDeviceProvisioningConfigApplied(); @@ -1149,6 +1149,7 @@ package android.app.admin { field public static final String EXTRA_RESTRICTION = "android.app.extra.RESTRICTION"; field public static final String EXTRA_ROLE_HOLDER_PROVISIONING_INITIATOR_PACKAGE = "android.app.extra.ROLE_HOLDER_PROVISIONING_INITIATOR_PACKAGE"; field public static final String EXTRA_ROLE_HOLDER_STATE = "android.app.extra.ROLE_HOLDER_STATE"; + field public static final String EXTRA_ROLE_HOLDER_UPDATE_RESULT_CODE = "android.app.extra.ROLE_HOLDER_UPDATE_RESULT_CODE"; field public static final int FLAG_SUPPORTED_MODES_DEVICE_OWNER = 4; // 0x4 field public static final int FLAG_SUPPORTED_MODES_ORGANIZATION_OWNED = 1; // 0x1 field public static final int FLAG_SUPPORTED_MODES_PERSONALLY_OWNED = 2; // 0x2 @@ -1162,6 +1163,7 @@ package android.app.admin { field public static final String REQUIRED_APP_MANAGED_PROFILE = "android.app.REQUIRED_APP_MANAGED_PROFILE"; field public static final String REQUIRED_APP_MANAGED_USER = "android.app.REQUIRED_APP_MANAGED_USER"; field public static final int RESULT_DEVICE_OWNER_SET = 123; // 0x7b + field public static final int RESULT_UPDATE_DEVICE_POLICY_MANAGEMENT_ROLE_HOLDER_PROVISIONING_DISABLED = 3; // 0x3 field public static final int RESULT_UPDATE_DEVICE_POLICY_MANAGEMENT_ROLE_HOLDER_RECOVERABLE_ERROR = 1; // 0x1 field public static final int RESULT_UPDATE_DEVICE_POLICY_MANAGEMENT_ROLE_HOLDER_UNRECOVERABLE_ERROR = 2; // 0x2 field public static final int RESULT_UPDATE_ROLE_HOLDER = 2; // 0x2 @@ -3413,6 +3415,8 @@ package android.content.pm { field public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS"; field public static final String FEATURE_BROADCAST_RADIO = "android.hardware.broadcastradio"; field public static final String FEATURE_CONTEXT_HUB = "android.hardware.context_hub"; + field public static final String FEATURE_EROFS = "android.software.erofs"; + field public static final String FEATURE_EROFS_LEGACY = "android.software.erofs_legacy"; field public static final String FEATURE_GAME_SERVICE = "android.software.game_service"; field public static final String FEATURE_INCREMENTAL_DELIVERY = "android.software.incremental_delivery"; field public static final String FEATURE_REBOOT_ESCROW = "android.hardware.reboot_escrow"; @@ -5490,6 +5494,32 @@ package android.location { method @NonNull public android.location.GnssCapabilities.Builder setHasSatellitePvt(boolean); } + public final class GnssExcessPathInfo implements android.os.Parcelable { + method public int describeContents(); + method @FloatRange(from=0.0f) public float getAttenuationDb(); + method @FloatRange(from=0.0f) public float getExcessPathLengthMeters(); + method @FloatRange(from=0.0f) public float getExcessPathLengthUncertaintyMeters(); + method @NonNull public android.location.GnssReflectingPlane getReflectingPlane(); + method public boolean hasAttenuation(); + method public boolean hasExcessPathLength(); + method public boolean hasExcessPathLengthUncertainty(); + method public boolean hasReflectingPlane(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssExcessPathInfo> CREATOR; + } + + public static final class GnssExcessPathInfo.Builder { + ctor public GnssExcessPathInfo.Builder(); + method @NonNull public android.location.GnssExcessPathInfo build(); + method @NonNull public android.location.GnssExcessPathInfo.Builder clearAttenuationDb(); + method @NonNull public android.location.GnssExcessPathInfo.Builder clearExcessPathLengthMeters(); + method @NonNull public android.location.GnssExcessPathInfo.Builder clearExcessPathLengthUncertaintyMeters(); + method @NonNull public android.location.GnssExcessPathInfo.Builder setAttenuationDb(@FloatRange(from=0.0f) float); + method @NonNull public android.location.GnssExcessPathInfo.Builder setExcessPathLengthMeters(@FloatRange(from=0.0f) float); + method @NonNull public android.location.GnssExcessPathInfo.Builder setExcessPathLengthUncertaintyMeters(@FloatRange(from=0.0f) float); + method @NonNull public android.location.GnssExcessPathInfo.Builder setReflectingPlane(@Nullable android.location.GnssReflectingPlane); + } + public final class GnssMeasurement implements android.os.Parcelable { method @Nullable public java.util.Collection<android.location.CorrelationVector> getCorrelationVectors(); method @Nullable public android.location.SatellitePvt getSatellitePvt(); @@ -5571,15 +5601,18 @@ package android.location { public final class GnssSingleSatCorrection implements android.os.Parcelable { method public int describeContents(); method @FloatRange(from=0.0f, fromInclusive=false) public float getCarrierFrequencyHz(); + method @FloatRange(from=0.0f) public float getCombinedAttenuationDb(); method public int getConstellationType(); method @FloatRange(from=0.0f) public float getExcessPathLengthMeters(); method @FloatRange(from=0.0f) public float getExcessPathLengthUncertaintyMeters(); + method @NonNull public java.util.List<android.location.GnssExcessPathInfo> getGnssExcessPathInfoList(); method @FloatRange(from=0.0f, to=1.0f) public float getProbabilityLineOfSight(); - method @Nullable public android.location.GnssReflectingPlane getReflectingPlane(); + method @Deprecated @Nullable public android.location.GnssReflectingPlane getReflectingPlane(); method @IntRange(from=0) public int getSatelliteId(); + method public boolean hasCombinedAttenuation(); method public boolean hasExcessPathLength(); method public boolean hasExcessPathLengthUncertainty(); - method public boolean hasReflectingPlane(); + method @Deprecated public boolean hasReflectingPlane(); method public boolean hasValidSatelliteLineOfSight(); method public void writeToParcel(@NonNull android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.location.GnssSingleSatCorrection> CREATOR; @@ -5588,15 +5621,18 @@ package android.location { public static final class GnssSingleSatCorrection.Builder { ctor public GnssSingleSatCorrection.Builder(); method @NonNull public android.location.GnssSingleSatCorrection build(); + method @NonNull public android.location.GnssSingleSatCorrection.Builder clearCombinedAttenuationDb(); method @NonNull public android.location.GnssSingleSatCorrection.Builder clearExcessPathLengthMeters(); method @NonNull public android.location.GnssSingleSatCorrection.Builder clearExcessPathLengthUncertaintyMeters(); method @NonNull public android.location.GnssSingleSatCorrection.Builder clearProbabilityLineOfSight(); method @NonNull public android.location.GnssSingleSatCorrection.Builder setCarrierFrequencyHz(@FloatRange(from=0.0f, fromInclusive=false) float); + method @NonNull public android.location.GnssSingleSatCorrection.Builder setCombinedAttenuationDb(@FloatRange(from=0.0f) float); method @NonNull public android.location.GnssSingleSatCorrection.Builder setConstellationType(int); method @NonNull public android.location.GnssSingleSatCorrection.Builder setExcessPathLengthMeters(@FloatRange(from=0.0f) float); method @NonNull public android.location.GnssSingleSatCorrection.Builder setExcessPathLengthUncertaintyMeters(@FloatRange(from=0.0f) float); + method @NonNull public android.location.GnssSingleSatCorrection.Builder setGnssExcessPathInfoList(@NonNull java.util.List<android.location.GnssExcessPathInfo>); method @NonNull public android.location.GnssSingleSatCorrection.Builder setProbabilityLineOfSight(@FloatRange(from=0.0f, to=1.0f) float); - method @NonNull public android.location.GnssSingleSatCorrection.Builder setReflectingPlane(@Nullable android.location.GnssReflectingPlane); + method @Deprecated @NonNull public android.location.GnssSingleSatCorrection.Builder setReflectingPlane(@Nullable android.location.GnssReflectingPlane); method @NonNull public android.location.GnssSingleSatCorrection.Builder setSatelliteId(@IntRange(from=0) int); } @@ -10049,9 +10085,9 @@ package android.permission { method @BinderThread public void onOneTimePermissionSessionTimeout(@NonNull String); method @Deprecated @BinderThread public void onRestoreDelayedRuntimePermissionsBackup(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @Deprecated @BinderThread public void onRestoreRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable); - method @BinderThread public void onRevokeOwnPermissionsOnKill(@NonNull String, @NonNull java.util.List<java.lang.String>, @NonNull Runnable); method @BinderThread public abstract void onRevokeRuntimePermission(@NonNull String, @NonNull String, @NonNull Runnable); method @BinderThread public abstract void onRevokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull String, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,java.util.List<java.lang.String>>>); + method @BinderThread public void onRevokeSelfPermissionsOnKill(@NonNull String, @NonNull java.util.List<java.lang.String>, @NonNull Runnable); method @Deprecated @BinderThread public abstract void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @BinderThread public void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull android.permission.AdminPermissionControlParams, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @BinderThread public void onStageAndApplyRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable); diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 2e8bed32665c..a67d002cdddf 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -300,7 +300,6 @@ package android.app { } public class LocaleManager { - method @Nullable public android.os.LocaleList getSystemLocales(); method public void setSystemLocales(@NonNull android.os.LocaleList); } @@ -502,6 +501,7 @@ package android.app.admin { method public void forceUpdateUserSetupComplete(int); method @NonNull public java.util.Set<java.lang.String> getDefaultCrossProfilePackages(); method public int getDeviceOwnerType(@NonNull android.content.ComponentName); + method @Nullable public String getDevicePolicyManagementRoleHolderUpdaterPackage(); method @NonNull public java.util.Set<java.lang.String> getDisallowedSystemApps(@NonNull android.content.ComponentName, int, @NonNull String); method public long getLastBugReportRequestTime(); method public long getLastNetworkLogRetrievalTime(); @@ -512,7 +512,6 @@ package android.app.admin { method public boolean isFactoryResetProtectionPolicySupported(); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public boolean isNewUserDisclaimerAcknowledged(); method public boolean isRemovingAdmin(@NonNull android.content.ComponentName, int); - method @RequiresPermission(anyOf={android.Manifest.permission.MARK_DEVICE_ORGANIZATION_OWNED, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}, conditional=true) public void markProfileOwnerOnOrganizationOwnedDevice(@NonNull android.content.ComponentName); method @NonNull public static String operationSafetyReasonToString(int); method @NonNull public static String operationToString(int); method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void resetDefaultCrossProfileIntentFilters(int); @@ -521,6 +520,7 @@ package android.app.admin { method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean setDeviceOwnerOnly(@NonNull android.content.ComponentName, @Nullable String, int); method public void setDeviceOwnerType(@NonNull android.content.ComponentName, int); method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public void setNextOperationSafety(int, int); + method @RequiresPermission(anyOf={android.Manifest.permission.MARK_DEVICE_ORGANIZATION_OWNED, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}, conditional=true) public void setProfileOwnerOnOrganizationOwnedDevice(@NonNull android.content.ComponentName, boolean); field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED"; field public static final String ACTION_DEVICE_POLICY_CONSTANTS_CHANGED = "android.app.action.DEVICE_POLICY_CONSTANTS_CHANGED"; field public static final int DEVICE_OWNER_TYPE_DEFAULT = 0; // 0x0 @@ -2429,7 +2429,7 @@ package android.service.quicksettings { package android.service.voice { public class AlwaysOnHotwordDetector implements android.service.voice.HotwordDetector { - method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public void triggerHardwareRecognitionEventForTest(int, int, boolean, int, int, int, boolean, @NonNull android.media.AudioFormat, @Nullable byte[]); + method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public void triggerHardwareRecognitionEventForTest(int, int, boolean, int, int, int, boolean, @NonNull android.media.AudioFormat, @Nullable byte[], @NonNull java.util.List<android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra>); } public static final class AlwaysOnHotwordDetector.EventPayload.Builder { diff --git a/core/java/Android.bp b/core/java/Android.bp index a5526bc66431..f081a439c49c 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -145,16 +145,16 @@ genrule { out: ["com/android/internal/util/FrameworkStatsLog.java"], } +// Library that provides functionality to log UiEvents in framework space. +// If this functionality is needed outside the framework, the interfaces library +// can be re-used and a local implementation is needed. java_library { name: "uieventloggerlib", srcs: [ - "com/android/internal/logging/UiEvent.java", - "com/android/internal/logging/UiEventLogger.java", "com/android/internal/logging/UiEventLoggerImpl.java", - "com/android/internal/logging/InstanceId.java", - "com/android/internal/logging/InstanceIdSequence.java", ":statslog-framework-java-gen", ], + static_libs: ["modules-utils-uieventlogger-interface"], } filegroup { diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 9f1510526bae..0ff070185fcb 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -22,6 +22,7 @@ import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; import android.Manifest; +import android.annotation.ColorInt; import android.annotation.DrawableRes; import android.annotation.IntDef; import android.annotation.IntRange; @@ -1264,6 +1265,104 @@ public class ActivityManager { private int mMinWidth; private int mMinHeight; + /** + * Provides a convenient way to set the fields of a {@link TaskDescription} when creating a + * new instance. + */ + public static final class Builder { + /** + * Default values for the TaskDescription + */ + @Nullable + private String mLabel = null; + @DrawableRes + private int mIconRes = Resources.ID_NULL; + private int mPrimaryColor = 0; + private int mBackgroundColor = 0; + private int mStatusBarColor = 0; + private int mNavigationBarColor = 0; + + /** + * Set the label to use in the TaskDescription. + * @param label A label and description of the current state of this activity. + * @return The same instance of the builder. + */ + @NonNull + public Builder setLabel(@Nullable String label) { + this.mLabel = label; + return this; + } + + /** + * Set the drawable resource of the icon to use in the TaskDescription. + * @param iconRes A drawable resource of an icon that represents the current state of + * this activity. + * @return The same instance of the builder. + */ + @NonNull + public Builder setIcon(@DrawableRes int iconRes) { + this.mIconRes = iconRes; + return this; + } + + /** + * Set the primary color to use in the TaskDescription. + * @param color A color to override the theme's primary color. The color must be opaque. + * @return The same instance of the builder. + */ + @NonNull + public Builder setPrimaryColor(@ColorInt int color) { + this.mPrimaryColor = color; + return this; + } + + /** + * Set the background color to use in the TaskDescription. + * @param color A color to override the theme's background color. The color must be + * opaque. + * @return The same instance of the builder. + */ + @NonNull + public Builder setBackgroundColor(@ColorInt int color) { + this.mBackgroundColor = color; + return this; + } + + /** + * Set the status bar color to use in the TaskDescription. + * @param color A color to override the theme's status bar color. + * @return The same instance of the builder. + */ + @NonNull + public Builder setStatusBarColor(@ColorInt int color) { + this.mStatusBarColor = color; + return this; + } + + /** + * Set the navigation bar color to use in the TaskDescription. + * @param color A color to override the theme's navigation bar color. + * @return The same instance of the builder. + */ + @NonNull + public Builder setNavigationBarColor(@ColorInt int color) { + this.mNavigationBarColor = color; + return this; + } + + /** + * Build the TaskDescription. + * @return the TaskDescription object. + */ + @NonNull + public TaskDescription build() { + final Icon icon = mIconRes == Resources.ID_NULL ? null : + Icon.createWithResource(ActivityThread.currentPackageName(), mIconRes); + return new TaskDescription(mLabel, icon, mPrimaryColor, mBackgroundColor, + mStatusBarColor, mNavigationBarColor, false, false, RESIZE_MODE_RESIZEABLE, + -1, -1, 0); + } + } /** * Creates the TaskDescription to the specified values. @@ -1273,7 +1372,10 @@ public class ActivityManager { * activity. * @param colorPrimary A color to override the theme's primary color. This color must be * opaque. + * + * @deprecated Use {@link Builder} instead. */ + @Deprecated public TaskDescription(String label, @DrawableRes int iconRes, int colorPrimary) { this(label, Icon.createWithResource(ActivityThread.currentPackageName(), iconRes), colorPrimary, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0); @@ -1288,7 +1390,10 @@ public class ActivityManager { * @param label A label and description of the current state of this activity. * @param iconRes A drawable resource of an icon that represents the current state of this * activity. + * + * @deprecated Use {@link Builder} instead. */ + @Deprecated public TaskDescription(String label, @DrawableRes int iconRes) { this(label, Icon.createWithResource(ActivityThread.currentPackageName(), iconRes), 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0); @@ -1298,14 +1403,20 @@ public class ActivityManager { * Creates the TaskDescription to the specified values. * * @param label A label and description of the current state of this activity. + * + * @deprecated Use {@link Builder} instead. */ + @Deprecated public TaskDescription(String label) { this(label, null, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0); } /** * Creates an empty TaskDescription. + * + * @deprecated Use {@link Builder} instead. */ + @Deprecated public TaskDescription() { this(null, null, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0); } @@ -1317,7 +1428,8 @@ public class ActivityManager { * @param icon An icon that represents the current state of this task. * @param colorPrimary A color to override the theme's primary color. This color must be * opaque. - * @deprecated use TaskDescription constructor with icon resource instead + * + * @deprecated Use {@link Builder} instead. */ @Deprecated public TaskDescription(String label, Bitmap icon, int colorPrimary) { @@ -1333,7 +1445,8 @@ public class ActivityManager { * * @param label A label and description of the current state of this activity. * @param icon An icon that represents the current state of this activity. - * @deprecated use TaskDescription constructor with icon resource instead + * + * @deprecated Use {@link Builder} instead. */ @Deprecated public TaskDescription(String label, Bitmap icon) { @@ -1635,15 +1748,15 @@ public class ActivityManager { /** * @return The color override on the theme's primary color. */ + @ColorInt public int getPrimaryColor() { return mColorPrimary; } /** - * @return The background color. - * @hide + * @return The color override on the theme's background color. */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) + @ColorInt public int getBackgroundColor() { return mColorBackground; } @@ -1657,15 +1770,17 @@ public class ActivityManager { } /** - * @hide + * @return The color override on the theme's status bar color. */ + @ColorInt public int getStatusBarColor() { return mStatusBarColor; } /** - * @hide + * @return The color override on the theme's navigation bar color. */ + @ColorInt public int getNavigationBarColor() { return mNavigationBarColor; } diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index f5eb1f6e24ed..ac46066997ff 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -77,6 +77,7 @@ import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManager; +import android.permission.PermissionControllerManager; import android.permission.PermissionManager; import android.system.ErrnoException; import android.system.Os; @@ -216,6 +217,12 @@ class ContextImpl extends Context { @UnsupportedAppUsage private @Nullable ClassLoader mClassLoader; + /** + * The {@link com.android.server.wm.WindowToken} representing this instance if it is + * {@link #CONTEXT_TYPE_WINDOW_CONTEXT} or {@link #CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI}. + * If the type is {@link #CONTEXT_TYPE_ACTIVITY}, then represents the + * {@link android.window.WindowContainerToken} of the activity. + */ private final @Nullable IBinder mToken; private final @NonNull UserHandle mUser; @@ -2180,8 +2187,9 @@ class ContextImpl extends Context { } @Override - public void revokeOwnPermissionsOnKill(@NonNull Collection<String> permissions) { - getSystemService(PermissionManager.class).revokeOwnPermissionsOnKill(permissions); + public void revokeSelfPermissionsOnKill(@NonNull Collection<String> permissions) { + getSystemService(PermissionControllerManager.class).revokeSelfPermissionsOnKill( + getPackageName(), new ArrayList<String>(permissions)); } @Override diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index 82ff42b41799..a763b1464b6d 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -465,7 +465,8 @@ public class Dialog implements DialogInterface, Window.Callback, onBackPressed(); } }; - getOnBackInvokedDispatcher().registerSystemOnBackInvokedCallback(mDefaultBackCallback); + getOnBackInvokedDispatcher().registerOnBackInvokedCallback( + OnBackInvokedDispatcher.PRIORITY_DEFAULT, mDefaultBackCallback); mDefaultBackCallback = null; } } diff --git a/core/java/android/app/GameManager.java b/core/java/android/app/GameManager.java index 6f49c9e647a4..a138fa1f6fd5 100644 --- a/core/java/android/app/GameManager.java +++ b/core/java/android/app/GameManager.java @@ -191,7 +191,7 @@ public final class GameManager { */ @TestApi @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE) - public @GameMode boolean isAngleEnabled(@NonNull String packageName) { + public boolean isAngleEnabled(@NonNull String packageName) { try { return mService.isAngleEnabled(packageName, mContext.getUserId()); } catch (RemoteException e) { diff --git a/core/java/android/app/ILocaleManager.aidl b/core/java/android/app/ILocaleManager.aidl index 348cb2d30739..3002c8bb9c3e 100644 --- a/core/java/android/app/ILocaleManager.aidl +++ b/core/java/android/app/ILocaleManager.aidl @@ -40,4 +40,9 @@ import android.os.LocaleList; */ LocaleList getApplicationLocales(String packageName, int userId); + /** + * Returns the current system locales. + */ + LocaleList getSystemLocales(); + }
\ No newline at end of file diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java index cedf483eb076..e9c29b8aa0a5 100644 --- a/core/java/android/app/KeyguardManager.java +++ b/core/java/android/app/KeyguardManager.java @@ -1103,9 +1103,12 @@ public class KeyguardManager { } /** - * Registers a listener to execute when the keyguard visibility changes. + * Registers a listener to execute when the keyguard locked state changes. * - * @param listener The listener to add to receive keyguard visibility changes. + * @param listener The listener to add to receive keyguard locked state changes. + * + * @see #isKeyguardLocked() + * @see #removeKeyguardLockedStateListener(KeyguardLockedStateListener) */ @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE) public void addKeyguardLockedStateListener(@NonNull @CallbackExecutor Executor executor, @@ -1124,7 +1127,12 @@ public class KeyguardManager { } /** - * Unregisters a listener that executes when the keyguard visibility changes. + * Unregisters a listener that executes when the keyguard locked state changes. + * + * @param listener The listener to remove. + * + * @see #isKeyguardLocked() + * @see #addKeyguardLockedStateListener(Executor, KeyguardLockedStateListener) */ @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE) public void removeKeyguardLockedStateListener(@NonNull KeyguardLockedStateListener listener) { diff --git a/core/java/android/app/LocaleManager.java b/core/java/android/app/LocaleManager.java index 522dc845f57c..efe9e35d4c64 100644 --- a/core/java/android/app/LocaleManager.java +++ b/core/java/android/app/LocaleManager.java @@ -18,7 +18,6 @@ package android.app; import android.Manifest; import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; @@ -127,31 +126,36 @@ public class LocaleManager { } /** - * Sets the current system locales to the provided value. + * Returns the current system locales, ignoring app-specific overrides. * - * @hide + * <p><b>Note:</b> Apps should generally access the user's locale preferences as indicated in + * their in-process {@link LocaleList}s. However, in case an app-specific locale is set, this + * method helps cater to rare use-cases which might require specifically knowing the system + * locale. + * + * <p><b>Note:</b> This API is not user-aware. It returns the system locales for the foreground + * user. */ - @TestApi - public void setSystemLocales(@NonNull LocaleList locales) { + @NonNull + public LocaleList getSystemLocales() { try { - Configuration conf = ActivityManager.getService().getConfiguration(); - conf.setLocales(locales); - ActivityManager.getService().updatePersistentConfiguration(conf); + return mService.getSystemLocales(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** - * Returns the current system locales for the device. + * Sets the current system locales to the provided value. * * @hide */ @TestApi - @Nullable - public LocaleList getSystemLocales() { + public void setSystemLocales(@NonNull LocaleList locales) { try { - return ActivityManager.getService().getConfiguration().getLocales(); + Configuration conf = ActivityManager.getService().getConfiguration(); + conf.setLocales(locales); + ActivityManager.getService().updatePersistentConfiguration(conf); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index 89854bbab3e8..ae0fc09e35a6 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -24,6 +24,9 @@ import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; +import android.app.compat.CompatChanges; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledSince; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; @@ -39,6 +42,7 @@ import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.UserHandle; import android.util.Pair; import android.util.Slog; import android.view.View; @@ -520,6 +524,27 @@ public class StatusBarManager { private final Map<NearbyMediaDevicesProvider, NearbyMediaDevicesProviderWrapper> nearbyMediaDevicesProviderMap = new HashMap<>(); + /** + * Media controls based on {@link android.app.Notification.MediaStyle} notifications will have + * actions based on the media session's {@link android.media.session.PlaybackState}, rather than + * the notification's actions. + * + * These actions will be: + * - Play/Pause (depending on whether the current state is a playing state) + * - Previous (if declared), or a custom action if the slot is not reserved with + * {@code SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV} + * - Next (if declared), or a custom action if the slot is not reserved with + * {@code SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT} + * - Custom action + * - Custom action + * + * @see androidx.media.utils.MediaConstants#SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV + * @see androidx.media.utils.MediaConstants#SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT + */ + @ChangeId + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) + private static final long MEDIA_CONTROL_SESSION_ACTIONS = 203800354L; + @UnsupportedAppUsage private Context mContext; private IStatusBarService mService; @@ -1127,6 +1152,21 @@ public class StatusBarManager { } } + /** + * Checks whether the given package should use session-based actions for its media controls. + * + * @param packageName App posting media controls + * @param user Current user handle + * @return true if the app supports session actions + * + * @hide + */ + @RequiresPermission(allOf = {android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG, + android.Manifest.permission.LOG_COMPAT_CHANGE}) + public static boolean useMediaSessionActionsForApp(String packageName, UserHandle user) { + return CompatChanges.isChangeEnabled(MEDIA_CONTROL_SESSION_ACTIONS, packageName, user); + } + /** @hide */ public static String windowStateToString(int state) { if (state == WINDOW_STATE_HIDING) return "WINDOW_STATE_HIDING"; diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING index 32207af22dc1..649f90442536 100644 --- a/core/java/android/app/TEST_MAPPING +++ b/core/java/android/app/TEST_MAPPING @@ -146,15 +146,6 @@ } ], "file_patterns": ["(/|^)ContextImpl.java"] - }, - { - "file_patterns": ["(/|^)LocaleManager.java"], - "name": "CtsLocaleManagerTestCases", - "options": [ - { - "exclude-annotation": "androidx.test.filters.FlakyTest" - } - ] } ], "presubmit-large": [ @@ -182,6 +173,16 @@ { "file_patterns": ["(/|^)ActivityThreadTest.java"], "name": "FrameworksCoreTests" + }, + // TODO(b/225192026): Move back to presubmit after b/225192026 is fixed + { + "file_patterns": ["(/|^)LocaleManager.java"], + "name": "CtsLocaleManagerTestCases", + "options": [ + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ] } ] } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 7269b0d91d02..223e5da5bc6d 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -21,6 +21,7 @@ import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import android.Manifest.permission; +import android.accounts.Account; import android.annotation.CallbackExecutor; import android.annotation.ColorInt; import android.annotation.IntDef; @@ -1623,14 +1624,26 @@ public class DevicePolicyManager { "android.app.extra.PROVISIONING_SKIP_EDUCATION_SCREENS"; /** - * A boolean extra indicating if mobile data should be used during NFC device owner provisioning - * for downloading the mobile device management application. If {@link - * #EXTRA_PROVISIONING_WIFI_SSID} is also specified, wifi network will be used instead. + * A boolean extra indicating if mobile data should be used during the provisioning flow + * for downloading the admin app. If {@link #EXTRA_PROVISIONING_WIFI_SSID} is also specified, + * wifi network will be used instead. * - * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner - * provisioning via an NFC bump. + * <p>Default value is {@code false}. * - * @hide + * <p>If this extra is set to {@code true} and {@link #EXTRA_PROVISIONING_WIFI_SSID} is not + * specified, this extra has different behaviour depending on the way provisioning is triggered: + * <ul> + * <li> + * For provisioning started via a QR code or an NFC tag, mobile data is always used for + * downloading the admin app. + * </li> + * <li> + * For all other provisioning methods, a mobile data connection check is made at the start + * of provisioning. If mobile data is connected at that point, the admin app download will + * happen using mobile data. If mobile data is not connected at that point, the end-user + * will be asked to pick a wifi network and the admin app download will proceed over wifi. + * </li> + * </ul> */ public static final String EXTRA_PROVISIONING_USE_MOBILE_DATA = "android.app.extra.PROVISIONING_USE_MOBILE_DATA"; @@ -3283,9 +3296,11 @@ public class DevicePolicyManager { * <p>The activity must handle the device policy management role holder update and set the * intent result to either {@link Activity#RESULT_OK} if the update was successful, {@link * #RESULT_UPDATE_DEVICE_POLICY_MANAGEMENT_ROLE_HOLDER_RECOVERABLE_ERROR} if it encounters a - * problem that may be solved by relaunching it again, or {@link - * #RESULT_UPDATE_DEVICE_POLICY_MANAGEMENT_ROLE_HOLDER_UNRECOVERABLE_ERROR} if it encounters a - * problem that will not be solved by relaunching it again. + * problem that may be solved by relaunching it again, {@link + * #RESULT_UPDATE_DEVICE_POLICY_MANAGEMENT_ROLE_HOLDER_PROVISIONING_DISABLED} if role holder + * provisioning is disabled, or {@link + * #RESULT_UPDATE_DEVICE_POLICY_MANAGEMENT_ROLE_HOLDER_UNRECOVERABLE_ERROR} if it encounters + * any other problem that will not be solved by relaunching it again. * * <p>If this activity has additional internal conditions which are not met, it should return * {@link #RESULT_UPDATE_DEVICE_POLICY_MANAGEMENT_ROLE_HOLDER_UNRECOVERABLE_ERROR}. @@ -3321,6 +3336,45 @@ public class DevicePolicyManager { 2; /** + * Result code that can be returned by the {@link + * #ACTION_UPDATE_DEVICE_POLICY_MANAGEMENT_ROLE_HOLDER} handler if role holder provisioning + * is disabled. + * + * @hide + */ + @SystemApi + public static final int + RESULT_UPDATE_DEVICE_POLICY_MANAGEMENT_ROLE_HOLDER_PROVISIONING_DISABLED = 3; + + /** + * An {@code int} extra which contains the result code of the last attempt to update + * the device policy management role holder. + * + * <p>This extra is provided to the device policy management role holder via either {@link + * #ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} or {@link + * #ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE} when started after the role holder + * had previously returned {@link #RESULT_UPDATE_ROLE_HOLDER}. + * + * <p>If the role holder update had failed, the role holder can use the value of this extra to + * make a decision whether to fail the provisioning flow or to carry on with the older version + * of the role holder. + * + * <p>Possible values can be: + * <ul> + * <li>{@link Activity#RESULT_OK} if the update was successful + * <li>{@link #RESULT_UPDATE_DEVICE_POLICY_MANAGEMENT_ROLE_HOLDER_RECOVERABLE_ERROR} if it + * encounters a problem that may be solved by relaunching it again. + * <li>{@link #RESULT_UPDATE_DEVICE_POLICY_MANAGEMENT_ROLE_HOLDER_UNRECOVERABLE_ERROR} if + * it encounters a problem that will not be solved by relaunching it again. + * </ul> + * + * @hide + */ + @SystemApi + public static final String EXTRA_ROLE_HOLDER_UPDATE_RESULT_CODE = + "android.app.extra.ROLE_HOLDER_UPDATE_RESULT_CODE"; + + /** * An {@link Intent} extra which resolves to a custom user consent screen. * * <p>If this extra is provided to the device policy management role holder via either {@link @@ -3340,6 +3394,16 @@ public class DevicePolicyManager { * <li>General disclaimer relevant to the provisioning mode</li> * </ul> * + * <p>When this {@link Intent} is started, the following intent extras will be supplied: + * <ul> + * <li>{@link #EXTRA_PROVISIONING_DISCLAIMERS}</li> + * <li>{@link #EXTRA_PROVISIONING_MODE}</li> + * <li>{@link #EXTRA_PROVISIONING_LOCALE}</li> + * <li>{@link #EXTRA_PROVISIONING_LOCAL_TIME}</li> + * <li>{@link #EXTRA_PROVISIONING_TIME_ZONE}</li> + * <li>{@link #EXTRA_PROVISIONING_SKIP_EDUCATION_SCREENS}</li> + * </ul> + * * <p>If the custom consent screens are granted by the user {@link Activity#RESULT_OK} is * returned, otherwise {@link Activity#RESULT_CANCELED} is returned. The device policy * management role holder should ensure that the provisioning flow terminates immediately if @@ -14009,12 +14073,12 @@ public class DevicePolicyManager { /** * Deprecated. Use {@code markProfileOwnerOnOrganizationOwnedDevice} instead. * When called by an app targeting SDK level {@link android.os.Build.VERSION_CODES#Q} or - * below, will behave the same as {@link #markProfileOwnerOnOrganizationOwnedDevice}. + * below, will behave the same as {@link #setProfileOwnerOnOrganizationOwnedDevice}. * * When called by an app targeting SDK level {@link android.os.Build.VERSION_CODES#R} * or above, will throw an UnsupportedOperationException when called. * - * @deprecated Use {@link #markProfileOwnerOnOrganizationOwnedDevice} instead. + * @deprecated Use {@link #setProfileOwnerOnOrganizationOwnedDevice} instead. * * @hide */ @@ -14029,14 +14093,14 @@ public class DevicePolicyManager { "This method is deprecated. use markProfileOwnerOnOrganizationOwnedDevice" + " instead."); } else { - markProfileOwnerOnOrganizationOwnedDevice(who); + setProfileOwnerOnOrganizationOwnedDevice(who, true); } } /** - * Marks the profile owner of the given user as managing an organization-owned device. - * That will give it access to device identifiers (such as serial number, IMEI and MEID) - * as well as other privileges. + * Sets whether the profile owner of the given user as managing an organization-owned device. + * Managing an organization-owned device will give it access to device identifiers (such as + * serial number, IMEI and MEID) as well as other privileges. * * @hide */ @@ -14044,13 +14108,15 @@ public class DevicePolicyManager { @RequiresPermission(anyOf = { android.Manifest.permission.MARK_DEVICE_ORGANIZATION_OWNED, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS - }, conditional = true) - public void markProfileOwnerOnOrganizationOwnedDevice(@NonNull ComponentName who) { + }, conditional = true) + public void setProfileOwnerOnOrganizationOwnedDevice(@NonNull ComponentName who, + boolean isProfileOwnerOnOrganizationOwnedDevice) { if (mService == null) { return; } try { - mService.markProfileOwnerOnOrganizationOwnedDevice(who, myUserId()); + mService.setProfileOwnerOnOrganizationOwnedDevice(who, myUserId(), + isProfileOwnerOnOrganizationOwnedDevice); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -14800,6 +14866,28 @@ public class DevicePolicyManager { } /** + * Called when a managed profile has been provisioned. + * + * @throws SecurityException if the caller does not hold + * {@link android.Manifest.permission#MANAGE_PROFILE_AND_DEVICE_OWNERS}. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) + public void finalizeWorkProfileProvisioning( + @NonNull UserHandle managedProfileUser, @Nullable Account migratedAccount) { + Objects.requireNonNull(managedProfileUser, "managedProfileUser can't be null"); + if (mService == null) { + throw new IllegalStateException("Could not find DevicePolicyManagerService"); + } + try { + mService.finalizeWorkProfileProvisioning(managedProfileUser, migratedAccount); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * The localized error message to show to the end-user. If {@code null}, a generic error * message will be shown. */ @@ -15264,16 +15352,16 @@ public class DevicePolicyManager { * <p>Sends a broadcast with action {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to * registered receivers when a resource has been reset successfully. * - * @param drawableIds The list of IDs to remove. + * @param drawableIds The list of IDs to remove. * * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES) - public void resetDrawables(@NonNull String[] drawableIds) { + public void resetDrawables(@NonNull Set<String> drawableIds) { if (mService != null) { try { - mService.resetDrawables(drawableIds); + mService.resetDrawables(new ArrayList<>(drawableIds)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -15573,10 +15661,10 @@ public class DevicePolicyManager { */ @SystemApi @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES) - public void resetStrings(@NonNull String[] stringIds) { + public void resetStrings(@NonNull Set<String> stringIds) { if (mService != null) { try { - mService.resetStrings(stringIds); + mService.resetStrings(new ArrayList<>(stringIds)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -15586,7 +15674,7 @@ public class DevicePolicyManager { /** * Returns the appropriate updated string for the {@code stringId} (see * {@link DevicePolicyResources.Strings}) if one was set using - * {@link #setStrings}, otherwise returns the string from {@code defaultStringLoader}. + * {@code setStrings}, otherwise returns the string from {@code defaultStringLoader}. * * <p>Also returns the string from {@code defaultStringLoader} if * {@link DevicePolicyResources.Strings#UNDEFINED} was passed. @@ -15598,15 +15686,12 @@ public class DevicePolicyManager { * notified when a resource has been updated. * * <p>Note that each call to this API loads the resource from the package that called - * {@link #setStrings} to set the updated resource. + * {@code setStrings} to set the updated resource. * * @param stringId The IDs to get the updated resource for. * @param defaultStringLoader To get the default string if no updated string was set for * {@code stringId}. - * - * @hide */ - @SystemApi @Nullable public String getString( @NonNull @DevicePolicyResources.UpdatableStringId String stringId, @@ -15649,10 +15734,7 @@ public class DevicePolicyManager { * @param defaultStringLoader To get the default string if no updated string was set for * {@code stringId}. * @param formatArgs The format arguments that will be used for substitution. - * - * @hide */ - @SystemApi @Nullable @SuppressLint("SamShouldBeLast") public String getString( @@ -15737,9 +15819,28 @@ public class DevicePolicyManager { */ @Nullable public String getDevicePolicyManagementRoleHolderPackage() { - String deviceManagerConfig = mContext.getString( + String devicePolicyManagementConfig = mContext.getString( com.android.internal.R.string.config_devicePolicyManagement); - return extractPackageNameFromDeviceManagerConfig(deviceManagerConfig); + return extractPackageNameFromDeviceManagerConfig(devicePolicyManagementConfig); + } + + /** + * Returns the package name of the device policy management role holder updater. + * + * <p>If the device policy management role holder updater is not configured for this device, + * returns {@code null}. + * + * @hide + */ + @Nullable + @TestApi + public String getDevicePolicyManagementRoleHolderUpdaterPackage() { + String devicePolicyManagementUpdaterConfig = mContext.getString( + com.android.internal.R.string.config_devicePolicyManagementUpdater); + if (TextUtils.isEmpty(devicePolicyManagementUpdaterConfig)) { + return null; + } + return devicePolicyManagementUpdaterConfig; } /** diff --git a/core/java/android/app/admin/FactoryResetProtectionPolicy.java b/core/java/android/app/admin/FactoryResetProtectionPolicy.java index 40ae1f0c11ea..7e951779d2a6 100644 --- a/core/java/android/app/admin/FactoryResetProtectionPolicy.java +++ b/core/java/android/app/admin/FactoryResetProtectionPolicy.java @@ -25,6 +25,7 @@ import android.annotation.Nullable; import android.content.ComponentName; import android.os.Parcel; import android.os.Parcelable; +import android.util.IndentingPrintWriter; import android.util.Log; import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; @@ -254,4 +255,18 @@ public final class FactoryResetProtectionPolicy implements Parcelable { return !mFactoryResetProtectionAccounts.isEmpty() && mFactoryResetProtectionEnabled; } + /** + * @hide + */ + public void dump(IndentingPrintWriter pw) { + pw.print("factoryResetProtectionEnabled="); + pw.println(mFactoryResetProtectionEnabled); + + pw.print("factoryResetProtectionAccounts="); + pw.increaseIndent(); + for (int i = 0; i < mFactoryResetProtectionAccounts.size(); i++) { + pw.println(mFactoryResetProtectionAccounts.get(i)); + } + pw.decreaseIndent(); + } } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index fb1ca41ccc56..64241aa3312f 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -17,6 +17,7 @@ package android.app.admin; +import android.accounts.Account; import android.app.admin.DevicePolicyDrawableResource; import android.app.admin.DevicePolicyStringResource; import android.app.admin.ParcelableResource; @@ -476,7 +477,7 @@ interface IDevicePolicyManager { int getGlobalPrivateDnsMode(in ComponentName admin); String getGlobalPrivateDnsHost(in ComponentName admin); - void markProfileOwnerOnOrganizationOwnedDevice(in ComponentName who, int userId); + void setProfileOwnerOnOrganizationOwnedDevice(in ComponentName who, int userId, boolean isProfileOwnerOnOrganizationOwnedDevice); void installUpdateFromFile(in ComponentName admin, in ParcelFileDescriptor updateFileDescriptor, in StartInstallingUpdateCallback listener); @@ -528,6 +529,8 @@ interface IDevicePolicyManager { UserHandle createAndProvisionManagedProfile(in ManagedProfileProvisioningParams provisioningParams, in String callerPackage); void provisionFullyManagedDevice(in FullyManagedDeviceProvisioningParams provisioningParams, in String callerPackage); + void finalizeWorkProfileProvisioning(in UserHandle managedProfileUser, in Account migratedAccount); + void setDeviceOwnerType(in ComponentName admin, in int deviceOwnerType); int getDeviceOwnerType(in ComponentName admin); @@ -549,14 +552,14 @@ interface IDevicePolicyManager { List<UserHandle> listForegroundAffiliatedUsers(); void setDrawables(in List<DevicePolicyDrawableResource> drawables); - void resetDrawables(in String[] drawableIds); + void resetDrawables(in List<String> drawableIds); ParcelableResource getDrawable(String drawableId, String drawableStyle, String drawableSource); boolean isDpcDownloaded(); void setDpcDownloaded(boolean downloaded); void setStrings(in List<DevicePolicyStringResource> strings); - void resetStrings(in String[] stringIds); + void resetStrings(in List<String> stringIds); ParcelableResource getString(String stringId); boolean shouldAllowBypassingDevicePolicyManagementRoleQualification(); diff --git a/core/java/android/app/usage/BroadcastResponseStats.java b/core/java/android/app/usage/BroadcastResponseStats.java index e1d37e1b1ae0..572c45355e42 100644 --- a/core/java/android/app/usage/BroadcastResponseStats.java +++ b/core/java/android/app/usage/BroadcastResponseStats.java @@ -29,6 +29,8 @@ import java.util.Objects; * Class containing a collection of stats related to response events started from an app * after receiving a broadcast. * + * @see UsageStatsManager#queryBroadcastResponseStats(String, long) + * @see UsageStatsManager#clearBroadcastResponseStats(String, long) * @hide */ @SystemApi diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl index 2a2a9c6e703e..7e142221b15d 100644 --- a/core/java/android/app/usage/IUsageStatsManager.aidl +++ b/core/java/android/app/usage/IUsageStatsManager.aidl @@ -82,4 +82,6 @@ interface IUsageStatsManager { int userId); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)") void clearBroadcastEvents(String callingPackage, int userId); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG)") + String getAppStandbyConstant(String key); } diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java index 3a335f9d151b..b88a51312cd1 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -16,6 +16,7 @@ package android.app.usage; +import android.Manifest; import android.annotation.CurrentTimeMillisLong; import android.annotation.IntDef; import android.annotation.IntRange; @@ -1505,4 +1506,15 @@ public final class UsageStatsManager { throw re.rethrowFromSystemServer(); } } + + /** @hide */ + @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG) + @Nullable + public String getAppStandbyConstant(@NonNull String key) { + try { + return mService.getAppStandbyConstant(key); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 60efb4d3ec81..24b1b6adb450 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -6508,22 +6508,22 @@ public abstract class Context { /** - * Triggers the asynchronous revocation of a permission. + * Triggers the asynchronous revocation of a runtime permission. If the permission is not + * currently granted, nothing happens (even if later granted by the user). * * @param permName The name of the permission to be revoked. - * @see #revokeOwnPermissionsOnKill(Collection) + * @see #revokeSelfPermissionsOnKill(Collection) + * @throws IllegalArgumentException if the permission is not a runtime permission */ - public void revokeOwnPermissionOnKill(@NonNull String permName) { - revokeOwnPermissionsOnKill(Collections.singletonList(permName)); + public void revokeSelfPermissionOnKill(@NonNull String permName) { + revokeSelfPermissionsOnKill(Collections.singletonList(permName)); } /** * Triggers the revocation of one or more permissions for the calling package. A package is only - * able to revoke a permission under the following conditions: - * <ul> - * <li>Each permission in {@code permissions} must be granted to the calling package. - * <li>Each permission in {@code permissions} must be a runtime permission. - * </ul> + * able to revoke runtime permissions. If a permission is not currently granted, it is ignored + * and will not get revoked (even if later granted by the user). Ultimately, you should never + * make assumptions about a permission status as users may grant or revoke them at any time. * <p> * Background permissions which have no corresponding foreground permission still granted once * the revocation is effective will also be revoked. @@ -6549,8 +6549,9 @@ public abstract class Context { * @param permissions Collection of permissions to be revoked. * @see PackageManager#getGroupOfPlatformPermission(String, Executor, Consumer) * @see PackageManager#getPlatformPermissionsForGroup(String, Executor, Consumer) + * @throws IllegalArgumentException if any of the permissions is not a runtime permission */ - public void revokeOwnPermissionsOnKill(@NonNull Collection<String> permissions) { + public void revokeSelfPermissionsOnKill(@NonNull Collection<String> permissions) { throw new AbstractMethodError("Must be overridden in implementing class"); } @@ -7145,8 +7146,9 @@ public abstract class Context { } /** - * Returns token if the {@link Context} is a {@link android.app.WindowContext}. Returns - * {@code null} otherwise. + * Returns the {@link IBinder} representing the associated + * {@link com.android.server.wm.WindowToken} if the {@link Context} is a + * {@link android.app.WindowContext}. Returns {@code null} otherwise. * * @hide */ diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index 9adf17367039..4ecd7761ac4f 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -1036,8 +1036,8 @@ public class ContextWrapper extends Context { } @Override - public void revokeOwnPermissionsOnKill(@NonNull Collection<String> permissions) { - mBase.revokeOwnPermissionsOnKill(permissions); + public void revokeSelfPermissionsOnKill(@NonNull Collection<String> permissions) { + mBase.revokeSelfPermissionsOnKill(permissions); } @Override diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 478befd9c26d..2c207bc90444 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -8903,8 +8903,12 @@ public class Intent implements Parcelable, Cloneable { * @return the value of an item previously added with putExtra(), * or null if no Parcelable value was found. * + * @deprecated Use the type-safer {@link #getParcelableExtra(String, Class)} starting from + * Android {@link Build.VERSION_CODES#TIRAMISU}. + * * @see #putExtra(String, Parcelable) */ + @Deprecated public @Nullable <T extends Parcelable> T getParcelableExtra(String name) { return mExtras == null ? null : mExtras.<T>getParcelable(name); } @@ -8913,12 +8917,31 @@ public class Intent implements Parcelable, Cloneable { * Retrieve extended data from the intent. * * @param name The name of the desired item. + * @param clazz The type of the object expected. + * + * @return the value of an item previously added with putExtra(), + * or null if no Parcelable value was found. + * + * @see #putExtra(String, Parcelable) + */ + public @Nullable <T> T getParcelableExtra(@Nullable String name, @NonNull Class<T> clazz) { + return mExtras == null ? null : mExtras.getParcelable(name, clazz); + } + + /** + * Retrieve extended data from the intent. + * + * @param name The name of the desired item. * * @return the value of an item previously added with putExtra(), * or null if no Parcelable[] value was found. * + * @deprecated Use the type-safer {@link #getParcelableArrayExtra(String, Class)} starting from + * Android {@link Build.VERSION_CODES#TIRAMISU}. + * * @see #putExtra(String, Parcelable[]) */ + @Deprecated public @Nullable Parcelable[] getParcelableArrayExtra(String name) { return mExtras == null ? null : mExtras.getParcelableArray(name); } @@ -8927,13 +8950,34 @@ public class Intent implements Parcelable, Cloneable { * Retrieve extended data from the intent. * * @param name The name of the desired item. + * @param clazz The type of the items inside the array. This is only verified when unparceling. + * + * @return the value of an item previously added with putExtra(), + * or null if no Parcelable[] value was found. + * + * @see #putExtra(String, Parcelable[]) + */ + @SuppressLint({"ArrayReturn", "NullableCollection"}) + public @Nullable <T> T[] getParcelableArrayExtra(@Nullable String name, + @NonNull Class<T> clazz) { + return mExtras == null ? null : mExtras.getParcelableArray(name, clazz); + } + + /** + * Retrieve extended data from the intent. + * + * @param name The name of the desired item. * * @return the value of an item previously added with * putParcelableArrayListExtra(), or null if no * ArrayList<Parcelable> value was found. * + * @deprecated Use the type-safer {@link #getParcelableArrayListExtra(String, Class)} starting + * from Android {@link Build.VERSION_CODES#TIRAMISU}. + * * @see #putParcelableArrayListExtra(String, ArrayList) */ + @Deprecated public @Nullable <T extends Parcelable> ArrayList<T> getParcelableArrayListExtra(String name) { return mExtras == null ? null : mExtras.<T>getParcelableArrayList(name); } @@ -8942,10 +8986,32 @@ public class Intent implements Parcelable, Cloneable { * Retrieve extended data from the intent. * * @param name The name of the desired item. + * @param clazz The type of the items inside the array list. This is only verified when + * unparceling. + * + * @return the value of an item previously added with + * putParcelableArrayListExtra(), or null if no + * ArrayList<Parcelable> value was found. + * + * @see #putParcelableArrayListExtra(String, ArrayList) + */ + @SuppressLint({"ConcreteCollection", "NullableCollection"}) + public @Nullable <T> ArrayList<T> getParcelableArrayListExtra(@Nullable String name, + @NonNull Class<? extends T> clazz) { + return mExtras == null ? null : mExtras.<T>getParcelableArrayList(name, clazz); + } + + /** + * Retrieve extended data from the intent. + * + * @param name The name of the desired item. * * @return the value of an item previously added with putExtra(), * or null if no Serializable value was found. * + * @deprecated Use the type-safer {@link #getSerializableExtra(String, Class)} starting from + * Android {@link Build.VERSION_CODES#TIRAMISU}. + * * @see #putExtra(String, Serializable) */ public @Nullable Serializable getSerializableExtra(String name) { @@ -8956,6 +9022,22 @@ public class Intent implements Parcelable, Cloneable { * Retrieve extended data from the intent. * * @param name The name of the desired item. + * @param clazz The type of the object expected. + * + * @return the value of an item previously added with putExtra(), + * or null if no Serializable value was found. + * + * @see #putExtra(String, Serializable) + */ + public @Nullable <T extends Serializable> T getSerializableExtra(@Nullable String name, + @NonNull Class<T> clazz) { + return mExtras == null ? null : mExtras.getSerializable(name, clazz); + } + + /** + * Retrieve extended data from the intent. + * + * @param name The name of the desired item. * * @return the value of an item previously added with * putIntegerArrayListExtra(), or null if no diff --git a/core/java/android/content/pm/AppSearchShortcutInfo.java b/core/java/android/content/pm/AppSearchShortcutInfo.java index f20d1e62e8e7..1b84686bbfcf 100644 --- a/core/java/android/content/pm/AppSearchShortcutInfo.java +++ b/core/java/android/content/pm/AppSearchShortcutInfo.java @@ -349,7 +349,7 @@ public class AppSearchShortcutInfo extends GenericDocument { .setDisabledReason(shortcutInfo.getDisabledReason()) .setPersons(shortcutInfo.getPersons()) .setLocusId(shortcutInfo.getLocusId()) - .setCapabilityBindings(shortcutInfo.getCapabilityBindings()) + .setCapabilityBindings(shortcutInfo.getCapabilityBindingsInternal()) .setTtlMillis(SHORTCUT_TTL) .build(); } diff --git a/core/java/android/content/pm/Capability.aidl b/core/java/android/content/pm/Capability.aidl new file mode 100644 index 000000000000..df3b1be1ce31 --- /dev/null +++ b/core/java/android/content/pm/Capability.aidl @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.content.pm; + +parcelable Capability;
\ No newline at end of file diff --git a/core/java/android/content/pm/Capability.java b/core/java/android/content/pm/Capability.java new file mode 100644 index 000000000000..1597d31828a1 --- /dev/null +++ b/core/java/android/content/pm/Capability.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * Represents a capability that can be performed by an app, also known as App Action. + * Capabilities can be associated with a {@link ShortcutInfo}. + * + * @see ShortcutInfo.Builder#addCapabilityBinding(Capability, CapabilityParams) + */ +public final class Capability implements Parcelable { + + @NonNull + private final String mName; + + /** + * Constructor. + * @param name Name of the capability, usually maps to a built-in intent, + * e.g. actions.intent.GET_MESSAGE. Note the character "/" is not permitted. + * @throws IllegalArgumentException If specified capability name contains the character "/". + * + * @hide + */ + Capability(@NonNull final String name) { + Objects.requireNonNull(name); + if (name.contains("/")) { + throw new IllegalArgumentException("'/' is not permitted in the capability name"); + } + mName = name; + } + + /** + * Copy constructor. + * + * @hide + */ + Capability(@NonNull final Capability orig) { + this(orig.mName); + } + + private Capability(@NonNull final Builder builder) { + this(builder.mName); + } + + private Capability(@NonNull final Parcel in) { + mName = in.readString(); + } + + /** + * Returns the name of the capability. e.g. actions.intent.GET_MESSAGE. + */ + @NonNull + public String getName() { + return mName; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Capability)) { + return false; + } + return mName.equals(((Capability) obj).mName); + } + + @Override + public int hashCode() { + return mName.hashCode(); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString(mName); + } + @Override + public int describeContents() { + return 0; + } + + @NonNull + public static final Parcelable.Creator<Capability> CREATOR = + new Parcelable.Creator<Capability>() { + @Override + public Capability[] newArray(int size) { + return new Capability[size]; + } + + @Override + public Capability createFromParcel(@NonNull Parcel in) { + return new Capability(in); + } + }; + + /** + * Builder class for {@link Capability}. + */ + public static final class Builder { + + @NonNull + private final String mName; + + /** + * Constructor. + * @param name Name of the capability, usually maps to a built-in intent, + * e.g. actions.intent.GET_MESSAGE. Note the character "/" is not permitted. + * @throws IllegalArgumentException If specified capability name contains the character "/". + */ + public Builder(@NonNull final String name) { + Objects.requireNonNull(name); + if (name.contains("/")) { + throw new IllegalArgumentException("'/' is not permitted in the capability name"); + } + mName = name; + } + + /** + * Creates an instance of {@link Capability} + */ + @NonNull + public Capability build() { + return new Capability(this); + } + } +} diff --git a/core/java/android/content/pm/CapabilityParams.aidl b/core/java/android/content/pm/CapabilityParams.aidl new file mode 100644 index 000000000000..39f12387404f --- /dev/null +++ b/core/java/android/content/pm/CapabilityParams.aidl @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.content.pm; + +parcelable CapabilityParams; diff --git a/core/java/android/content/pm/CapabilityParams.java b/core/java/android/content/pm/CapabilityParams.java new file mode 100644 index 000000000000..7239bacf1221 --- /dev/null +++ b/core/java/android/content/pm/CapabilityParams.java @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; +import android.util.ArraySet; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +/** + * Represents the parameters and its matching names which can be associated with a + * {@link Capability}. + * + * @see ShortcutInfo.Builder#addCapabilityBinding(Capability, CapabilityParams) + */ +public final class CapabilityParams implements Parcelable { + + @NonNull + private final String mName; + @NonNull + private final String mPrimaryValue; + @NonNull + private final List<String> mAliases; + + /** + * Constructor. + * @param name Name of the capability parameter. + * Note the character "/" is not permitted. + * @param primaryValue The primary value of the parameter. + * @param aliases Alternative values of the parameter. + */ + private CapabilityParams(@NonNull final String name, + @NonNull final String primaryValue, @Nullable final Collection<String> aliases) { + Objects.requireNonNull(name); + Objects.requireNonNull(primaryValue); + mName = name; + mPrimaryValue = primaryValue; + mAliases = aliases == null ? Collections.emptyList() + : Collections.unmodifiableList(new ArrayList<>(aliases)); + } + + /** + * Copy constructor. + * @hide + */ + CapabilityParams(@NonNull final CapabilityParams orig) { + this(orig.mName, orig.mPrimaryValue, orig.mAliases); + } + + private CapabilityParams(@NonNull final Builder builder) { + this(builder.mKey, builder.mPrimaryValue, builder.mAliases); + } + + private CapabilityParams(@NonNull final Parcel in) { + mName = in.readString(); + mPrimaryValue = in.readString(); + final List<String> values = new ArrayList<>(); + in.readStringList(values); + mAliases = Collections.unmodifiableList(values); + } + + /** + * Name of the parameter. + */ + @NonNull + public String getName() { + return mName; + } + + /** + * Returns the primary name of values in this parameter. + */ + @NonNull + public String getValue() { + return mPrimaryValue; + } + + /** + * Returns the aliases of the values in ths parameter. Returns an empty list if there are no + * aliases. + */ + @NonNull + public List<String> getAliases() { + return new ArrayList<>(mAliases); + } + + /** + * A list of values for this parameter. The first value will be the primary name, while the + * rest will be alternative names. + * @hide + */ + @NonNull + List<String> getValues() { + if (mAliases == null) { + return new ArrayList<>(Collections.singletonList(mPrimaryValue)); + } + final List<String> ret = new ArrayList<>(mAliases.size() + 1); + ret.add(mPrimaryValue); + ret.addAll(mAliases); + return ret; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof CapabilityParams)) { + return false; + } + final CapabilityParams target = (CapabilityParams) obj; + return mName.equals(target.mName) && mPrimaryValue.equals(target.mPrimaryValue) + && mAliases.equals(target.mAliases); + } + + @Override + public int hashCode() { + return Objects.hash(mName, mPrimaryValue, mAliases); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString(mName); + dest.writeString(mPrimaryValue); + dest.writeStringList(mAliases); + } + + @NonNull + public static final Parcelable.Creator<CapabilityParams> CREATOR = + new Parcelable.Creator<CapabilityParams>() { + @Override + public CapabilityParams[] newArray(int size) { + return new CapabilityParams[size]; + } + + @Override + public CapabilityParams createFromParcel(@NonNull Parcel in) { + return new CapabilityParams(in); + } + }; + + /** + * Builder class for {@link CapabilityParams}. + */ + public static final class Builder { + + @NonNull + private final String mKey; + @NonNull + private String mPrimaryValue; + @NonNull + private Set<String> mAliases; + + /** + * Constructor. + * @param key key of the capability parameter. + * Note the character "/" is not permitted. + * @param value The primary name of value in the {@link CapabilityParams}, cannot be empty. + */ + public Builder(@NonNull final String key, @NonNull final String value) { + Objects.requireNonNull(key); + if (TextUtils.isEmpty(value)) { + throw new IllegalArgumentException("Primary value cannot be empty or null"); + } + mPrimaryValue = value; + mKey = key; + } + + /** + * Add an alias in the {@link CapabilityParams}. + */ + @NonNull + public Builder addAlias(@NonNull final String alias) { + if (mAliases == null) { + mAliases = new ArraySet<>(1); + } + mAliases.add(alias); + return this; + } + + /** + * Creates an instance of {@link CapabilityParams} + * @throws IllegalArgumentException If the specified value is empty. + */ + @NonNull + public CapabilityParams build() { + return new CapabilityParams(this); + } + } +} diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java index eefa63f5b8fa..f4de82946253 100644 --- a/core/java/android/content/pm/PackageInfo.java +++ b/core/java/android/content/pm/PackageInfo.java @@ -213,7 +213,8 @@ public class PackageInfo implements Parcelable { * or null if there were none. This is only filled in if the flag * {@link PackageManager#GET_PERMISSIONS} was set. Each value matches * the corresponding entry in {@link #requestedPermissions}, and will have - * the flag {@link #REQUESTED_PERMISSION_GRANTED} set as appropriate. + * the flags {@link #REQUESTED_PERMISSION_GRANTED} and + * {@link #REQUESTED_PERMISSION_NEVER_FOR_LOCATION} set as appropriate. */ public int[] requestedPermissionsFlags; diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 81c941eedef0..c91ee1395687 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -4087,6 +4087,28 @@ public abstract class PackageManager { "android.software.incremental_delivery"; /** + * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device + * has the requisite kernel support for the EROFS filesystem present in 4.19 kernels as a + * staging driver, which lacks 0padding and big pcluster support. + * + * @hide + */ + @SystemApi + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_EROFS_LEGACY = "android.software.erofs_legacy"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device + * has the requisite kernel support for the EROFS filesystem present in 5.10 kernels, which + * has 0padding, big pcluster, and chunked index support. + * + * @hide + */ + @SystemApi + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_EROFS = "android.software.erofs"; + + /** * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: * The device has tuner hardware to support tuner operations. * diff --git a/core/java/android/content/pm/SHORTCUT_OWNERS b/core/java/android/content/pm/SHORTCUT_OWNERS index 3688d5a3a4c7..f8bba473336d 100644 --- a/core/java/android/content/pm/SHORTCUT_OWNERS +++ b/core/java/android/content/pm/SHORTCUT_OWNERS @@ -1,7 +1,6 @@ set noparent +pinyaoting@google.com +sunnygoyal@google.com omakoto@google.com yamasani@google.com -sunnygoyal@google.com -mett@google.com -pinyaoting@google.com diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java index 41dd5bb3f21d..56d092d8319d 100644 --- a/core/java/android/content/pm/ShortcutInfo.java +++ b/core/java/android/content/pm/ShortcutInfo.java @@ -52,7 +52,7 @@ import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Collection; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -60,7 +60,6 @@ import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; -import java.util.stream.Stream; /** * Represents a shortcut that can be published via {@link ShortcutManager}. @@ -501,7 +500,8 @@ public final class ShortcutInfo implements Parcelable { mRank = b.mRank; mExtras = b.mExtras; mLocusId = b.mLocusId; - mCapabilityBindings = b.mCapabilityBindings; + mCapabilityBindings = + cloneCapabilityBindings(b.mCapabilityBindings); mStartingThemeResName = b.mStartingThemeResId != 0 ? b.mContext.getResources().getResourceName(b.mStartingThemeResId) : null; updateTimestamp(); @@ -652,7 +652,8 @@ public final class ShortcutInfo implements Parcelable { // Set this bit. mFlags |= FLAG_KEY_FIELDS_ONLY; } - mCapabilityBindings = source.mCapabilityBindings; + mCapabilityBindings = cloneCapabilityBindings( + source.mCapabilityBindings); mStartingThemeResName = source.mStartingThemeResName; } @@ -1003,7 +1004,8 @@ public final class ShortcutInfo implements Parcelable { mStartingThemeResName = source.mStartingThemeResName; } if (source.mCapabilityBindings != null) { - mCapabilityBindings = source.mCapabilityBindings; + mCapabilityBindings = + cloneCapabilityBindings(source.mCapabilityBindings); } } @@ -1447,43 +1449,25 @@ public final class ShortcutInfo implements Parcelable { * <P>This method can be called multiple times to add multiple parameters to the same * capability. * - * @param capability capability associated with the shortcut. e.g. actions.intent - * .START_EXERCISE. - * @param parameterName name of the parameter associated with given capability. - * e.g. exercise.name. - * @param parameterValues a list of values for that parameters. The first value will be - * the primary name, while the rest will be alternative names. If - * the values are empty, then the parameter will not be saved in - * the shortcut. + * @param capability {@link Capability} associated with the shortcut. + * @param capabilityParams Optional {@link CapabilityParams} associated with given + * capability. */ @NonNull - public Builder addCapabilityBinding(@NonNull String capability, - @Nullable String parameterName, @Nullable List<String> parameterValues) { + public Builder addCapabilityBinding(@NonNull final Capability capability, + @Nullable final CapabilityParams capabilityParams) { Objects.requireNonNull(capability); - if (capability.contains("/")) { - throw new IllegalArgumentException("Illegal character '/' is found in capability"); - } if (mCapabilityBindings == null) { mCapabilityBindings = new ArrayMap<>(1); } - if (!mCapabilityBindings.containsKey(capability)) { - mCapabilityBindings.put(capability, new ArrayMap<>(0)); - } - if (parameterName == null || parameterValues == null || parameterValues.isEmpty()) { - return this; - } - if (parameterName.contains("/")) { - throw new IllegalArgumentException( - "Illegal character '/' is found in parameter name"); + if (!mCapabilityBindings.containsKey(capability.getName())) { + mCapabilityBindings.put(capability.getName(), new ArrayMap<>(0)); } - final Map<String, List<String>> params = mCapabilityBindings.get(capability); - if (!params.containsKey(parameterName)) { - params.put(parameterName, parameterValues); + if (capabilityParams == null) { return this; } - params.put(parameterName, - Stream.of(params.get(parameterName), parameterValues) - .flatMap(Collection::stream).collect(Collectors.toList())); + final Map<String, List<String>> params = mCapabilityBindings.get(capability.getName()); + params.put(capabilityParams.getName(), capabilityParams.getValues()); return this; } @@ -2264,41 +2248,78 @@ public final class ShortcutInfo implements Parcelable { } /** + * Returns an immutable copy of the capability bindings using internal data structure. * @hide */ - public Map<String, Map<String, List<String>>> getCapabilityBindings() { - return mCapabilityBindings; + @Nullable + public Map<String, Map<String, List<String>>> getCapabilityBindingsInternal() { + return cloneCapabilityBindings(mCapabilityBindings); + } + + @Nullable + private static Map<String, Map<String, List<String>>> cloneCapabilityBindings( + @Nullable final Map<String, Map<String, List<String>>> orig) { + if (orig == null) { + return null; + } + final Map<String, Map<String, List<String>>> ret = new ArrayMap<>(); + for (String capability : orig.keySet()) { + final Map<String, List<String>> params = orig.get(capability); + final Map<String, List<String>> clone; + if (params == null) { + clone = null; + } else { + clone = new ArrayMap<>(params.size()); + for (String paramName : params.keySet()) { + final List<String> paramValues = params.get(paramName); + clone.put(paramName, Collections.unmodifiableList(paramValues)); + } + } + ret.put(capability, Collections.unmodifiableMap(clone)); + } + return Collections.unmodifiableMap(ret); } /** - * Return true if the shortcut is or can be used in specified capability. + * Return a list of {@link Capability} associated with the shortcut. */ - public boolean hasCapability(@NonNull String capability) { - Objects.requireNonNull(capability); - return mCapabilityBindings != null && mCapabilityBindings.containsKey(capability); + @NonNull + public List<Capability> getCapabilities() { + if (mCapabilityBindings == null) { + return new ArrayList<>(0); + } + return mCapabilityBindings.keySet().stream().map(Capability::new) + .collect(Collectors.toList()); } /** - * Returns the values of specified parameter in associated with given capability. + * Returns the {@link CapabilityParams} in associated with given capability. * - * @param capability capability associated with the shortcut. e.g. actions.intent - * .START_EXERCISE. - * @param parameterName name of the parameter associated with given capability. - * e.g. exercise.name. + * @param capability {@link Capability} associated with the shortcut. */ @NonNull - public List<String> getCapabilityParameterValues( - @NonNull String capability, @NonNull String parameterName) { + public List<CapabilityParams> getCapabilityParams(@NonNull final Capability capability) { Objects.requireNonNull(capability); - Objects.requireNonNull(parameterName); if (mCapabilityBindings == null) { - return Collections.emptyList(); - } - final Map<String, List<String>> param = mCapabilityBindings.get(capability); - if (param == null || !param.containsKey(parameterName)) { - return Collections.emptyList(); + return new ArrayList<>(0); + } + final Map<String, List<String>> param = mCapabilityBindings.get(capability.getName()); + if (param == null) { + return new ArrayList<>(0); + } + final List<CapabilityParams> ret = new ArrayList<>(param.size()); + for (String key : param.keySet()) { + final List<String> values = param.get(key); + final String primaryValue = values.get(0); + final List<String> aliases = values.size() == 1 + ? Collections.emptyList() : values.subList(1, values.size()); + CapabilityParams.Builder builder = new CapabilityParams.Builder(key, primaryValue); + for (String alias : aliases) { + builder = builder.addAlias(alias); + } + ret.add(builder.build()); } - return param.get(parameterName); + return ret; } private ShortcutInfo(Parcel source) { @@ -2357,7 +2378,7 @@ public final class ShortcutInfo implements Parcelable { final Map<String, Map<String, List<String>>> capabilityBindings = new ArrayMap<>(rawCapabilityBindings.size()); rawCapabilityBindings.forEach(capabilityBindings::put); - mCapabilityBindings = capabilityBindings; + mCapabilityBindings = cloneCapabilityBindings(capabilityBindings); } } @@ -2695,6 +2716,6 @@ public final class ShortcutInfo implements Parcelable { mPersons = persons; mLocusId = locusId; mStartingThemeResName = startingThemeResName; - mCapabilityBindings = capabilityBindings; + mCapabilityBindings = cloneCapabilityBindings(capabilityBindings); } } diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index a05f5c927b29..c8bbb0c1994d 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -79,6 +79,13 @@ public final class AssetManager implements AutoCloseable { @GuardedBy("sSync") private static ArraySet<ApkAssets> sSystemApkAssetsSet; /** + * Cookie value to use when the actual cookie is unknown. This value tells the system to search + * all the ApkAssets for the asset. + * @hide + */ + public static final int COOKIE_UNKNOWN = -1; + + /** * Mode for {@link #open(String, int)}: no specific information about how * data will be accessed. */ diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index 7e070bc06056..29221b801ef6 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -923,14 +923,15 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing * @hide */ @RequiresPermission(USE_BIOMETRIC_INTERNAL) - public void onPointerDown(int sensorId, int x, int y, float minor, float major) { + public void onPointerDown(long requestId, int sensorId, int x, int y, + float minor, float major) { if (mService == null) { Slog.w(TAG, "onFingerDown: no fingerprint service"); return; } try { - mService.onPointerDown(sensorId, x, y, minor, major); + mService.onPointerDown(requestId, sensorId, x, y, minor, major); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -940,14 +941,14 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing * @hide */ @RequiresPermission(USE_BIOMETRIC_INTERNAL) - public void onPointerUp(int sensorId) { + public void onPointerUp(long requestId, int sensorId) { if (mService == null) { Slog.w(TAG, "onFingerDown: no fingerprint service"); return; } try { - mService.onPointerUp(sensorId); + mService.onPointerUp(requestId, sensorId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -957,14 +958,14 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing * @hide */ @RequiresPermission(USE_BIOMETRIC_INTERNAL) - public void onUiReady(int sensorId) { + public void onUiReady(long requestId, int sensorId) { if (mService == null) { Slog.w(TAG, "onUiReady: no fingerprint service"); return; } try { - mService.onUiReady(sensorId); + mService.onUiReady(requestId, sensorId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index cbff8b11a72a..12114aa3fa33 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -155,13 +155,13 @@ interface IFingerprintService { void addAuthenticatorsRegisteredCallback(IFingerprintAuthenticatorsRegisteredCallback callback); // Notifies about a finger touching the sensor area. - void onPointerDown(int sensorId, int x, int y, float minor, float major); + void onPointerDown(long requestId, int sensorId, int x, int y, float minor, float major); // Notifies about a finger leaving the sensor area. - void onPointerUp(int sensorId); + void onPointerUp(long requestId, int sensorId); // Notifies about the fingerprint UI being ready (e.g. HBM illumination is enabled). - void onUiReady(int sensorId); + void onUiReady(long requestId, int sensorId); // Sets the controller for managing the UDFPS overlay. void setUdfpsOverlayController(in IUdfpsOverlayController controller); diff --git a/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl index 3cca1b38e5e2..dbb8e40f3a71 100644 --- a/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl +++ b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl @@ -23,7 +23,7 @@ import android.hardware.fingerprint.IUdfpsOverlayControllerCallback; */ oneway interface IUdfpsOverlayController { // Shows the overlay for the given sensor with a reason from BiometricOverlayConstants. - void showUdfpsOverlay(int sensorId, int reason, IUdfpsOverlayControllerCallback callback); + void showUdfpsOverlay(long requestId, int sensorId, int reason, IUdfpsOverlayControllerCallback callback); // Hides the overlay. void hideUdfpsOverlay(int sensorId); diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java index 0f9075b498ae..03d11515c0a8 100644 --- a/core/java/android/inputmethodservice/NavigationBarController.java +++ b/core/java/android/inputmethodservice/NavigationBarController.java @@ -294,8 +294,8 @@ final class NavigationBarController { dest.setTouchableInsets( ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); - // TODO(b/205803355): See if we can use View#OnLayoutChangeListener(). - // TODO(b/205803355): See if we can replace DecorView#mNavigationColorViewState.view + // TODO(b/215443343): See if we can use View#OnLayoutChangeListener(). + // TODO(b/215443343): See if we can replace DecorView#mNavigationColorViewState.view boolean zOrderChanged = false; if (decor instanceof ViewGroup) { ViewGroup decorGroup = (ViewGroup) decor; diff --git a/core/java/android/inputmethodservice/navigationbar/DeadZone.java b/core/java/android/inputmethodservice/navigationbar/DeadZone.java index 4cfd8139d912..382b6b074962 100644 --- a/core/java/android/inputmethodservice/navigationbar/DeadZone.java +++ b/core/java/android/inputmethodservice/navigationbar/DeadZone.java @@ -148,7 +148,7 @@ final class DeadZone { if (DEBUG) { Log.v(TAG, this + " ACTION_DOWN: " + event.getX() + "," + event.getY()); } - //TODO(b/205803355): call mNavBarController.touchAutoDim(mDisplayId); here + //TODO(b/215443343): call mNavBarController.touchAutoDim(mDisplayId); here int size = (int) getSize(event.getEventTime()); // In the vertical orientation consume taps along the left edge. // In horizontal orientation consume taps along the top edge. diff --git a/core/java/android/inputmethodservice/navigationbar/KeyButtonView.java b/core/java/android/inputmethodservice/navigationbar/KeyButtonView.java index cfdb6caab9d3..92d358fe1663 100644 --- a/core/java/android/inputmethodservice/navigationbar/KeyButtonView.java +++ b/core/java/android/inputmethodservice/navigationbar/KeyButtonView.java @@ -89,7 +89,7 @@ public class KeyButtonView extends ImageView implements ButtonInterface { public KeyButtonView(Context context, AttributeSet attrs) { super(context, attrs); - // TODO(b/205803355): Figure out better place to set this. + // TODO(b/215443343): Figure out better place to set this. switch (getId()) { case com.android.internal.R.id.input_method_nav_back: mCode = KEYCODE_BACK; @@ -285,11 +285,11 @@ public class KeyButtonView extends ImageView implements ButtonInterface { private void sendEvent(int action, int flags, long when) { if (mCode == KeyEvent.KEYCODE_BACK && flags != KeyEvent.FLAG_LONG_PRESS) { if (action == MotionEvent.ACTION_UP) { - // TODO(b/205803355): Implement notifyBackAction(); + // TODO(b/215443343): Implement notifyBackAction(); } } - // TODO(b/205803355): Consolidate this logic to somewhere else. + // TODO(b/215443343): Consolidate this logic to somewhere else. if (mContext instanceof InputMethodService) { final int repeatCount = (flags & KeyEvent.FLAG_LONG_PRESS) != 0 ? 1 : 0; final KeyEvent ev = new KeyEvent(mDownTime, when, action, mCode, repeatCount, diff --git a/core/java/android/net/SntpClient.java b/core/java/android/net/SntpClient.java index 0eb4cf3ecadf..b05f7cf241e3 100644 --- a/core/java/android/net/SntpClient.java +++ b/core/java/android/net/SntpClient.java @@ -60,7 +60,7 @@ public class SntpClient { private static final int TRANSMIT_TIME_OFFSET = 40; private static final int NTP_PACKET_SIZE = 48; - private static final int NTP_PORT = 123; + public static final int STANDARD_NTP_PORT = 123; private static final int NTP_MODE_CLIENT = 3; private static final int NTP_MODE_SERVER = 4; private static final int NTP_MODE_BROADCAST = 5; @@ -108,18 +108,21 @@ public class SntpClient { * Sends an SNTP request to the given host and processes the response. * * @param host host name of the server. + * @param port port of the server. * @param timeout network timeout in milliseconds. the timeout doesn't include the DNS lookup * time, and it applies to each individual query to the resolved addresses of * the NTP server. * @param network network over which to send the request. * @return true if the transaction was successful. */ - public boolean requestTime(String host, int timeout, Network network) { + public boolean requestTime(String host, int port, int timeout, Network network) { final Network networkForResolv = network.getPrivateDnsBypassingCopy(); try { final InetAddress[] addresses = networkForResolv.getAllByName(host); for (int i = 0; i < addresses.length; i++) { - if (requestTime(addresses[i], NTP_PORT, timeout, networkForResolv)) return true; + if (requestTime(addresses[i], port, timeout, networkForResolv)) { + return true; + } } } catch (UnknownHostException e) { Log.w(TAG, "Unknown host: " + host); diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl index c021c079ae2e..a0f6598640ba 100644 --- a/core/java/android/os/storage/IStorageManager.aidl +++ b/core/java/android/os/storage/IStorageManager.aidl @@ -125,7 +125,6 @@ interface IStorageManager { boolean isUserKeyUnlocked(int userId) = 65; void prepareUserStorage(in String volumeUuid, int userId, int serialNumber, int flags) = 66; void destroyUserStorage(in String volumeUuid, int userId, int flags) = 67; - boolean isConvertibleToFBE() = 68; void addUserKeyAuth(int userId, int serialNumber, in byte[] secret) = 70; void fixateNewestUserKeyAuth(int userId) = 71; void fstrim(int flags, IVoldTaskListener listener) = 72; diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl index c9dd06cfaa43..e3f02e73a41f 100644 --- a/core/java/android/permission/IPermissionController.aidl +++ b/core/java/android/permission/IPermissionController.aidl @@ -59,6 +59,6 @@ oneway interface IPermissionController { void getHibernationEligibility( in String packageName, in AndroidFuture callback); - void revokeOwnPermissionsOnKill(in String packageName, in List<String> permissions, + void revokeSelfPermissionsOnKill(in String packageName, in List<String> permissions, in AndroidFuture callback); } diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl index 619c8705ddae..6a93b354f4da 100644 --- a/core/java/android/permission/IPermissionManager.aidl +++ b/core/java/android/permission/IPermissionManager.aidl @@ -76,8 +76,6 @@ interface IPermissionManager { List<SplitPermissionInfoParcelable> getSplitPermissions(); - void revokeOwnPermissionsOnKill(String packageName, in List<String> permissions); - void startOneTimePermissionSession(String packageName, int userId, long timeout, long revokeAfterKilledDelay, int importanceToResetTimer, int importanceToKeepSessionAlive); diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java index a005ab4e6ac7..3c2c7f03761b 100644 --- a/core/java/android/permission/PermissionControllerManager.java +++ b/core/java/android/permission/PermissionControllerManager.java @@ -916,15 +916,15 @@ public final class PermissionControllerManager { * @param packageName The name of the package for which the permissions will be revoked. * @param permissions List of permissions to be revoked. * - * @see Context#revokeOwnPermissionsOnKill(java.util.Collection) + * @see Context#revokeSelfPermissionsOnKill(java.util.Collection) * * @hide */ - public void revokeOwnPermissionsOnKill(@NonNull String packageName, + public void revokeSelfPermissionsOnKill(@NonNull String packageName, @NonNull List<String> permissions) { mRemoteService.postAsync(service -> { AndroidFuture<Void> callback = new AndroidFuture<>(); - service.revokeOwnPermissionsOnKill(packageName, permissions, callback); + service.revokeSelfPermissionsOnKill(packageName, permissions, callback); return callback; }).whenComplete((result, err) -> { if (err != null) { diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java index 3292e7110ee5..4efffc5a11ef 100644 --- a/core/java/android/permission/PermissionControllerService.java +++ b/core/java/android/permission/PermissionControllerService.java @@ -40,6 +40,7 @@ import android.compat.annotation.Disabled; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.ParcelFileDescriptor; @@ -339,10 +340,10 @@ public abstract class PermissionControllerService extends Service { * @param permissions List of permissions to be revoked. * @param callback Callback waiting for operation to be complete. * - * @see PermissionManager#revokeOwnPermissionsOnKill(java.util.Collection) + * @see android.content.Context#revokeSelfPermissionsOnKill(java.util.Collection) */ @BinderThread - public void onRevokeOwnPermissionsOnKill(@NonNull String packageName, + public void onRevokeSelfPermissionsOnKill(@NonNull String packageName, @NonNull List<String> permissions, @NonNull Runnable callback) { throw new AbstractMethodError("Must be overridden in implementing class"); } @@ -703,13 +704,19 @@ public abstract class PermissionControllerService extends Service { } @Override - public void revokeOwnPermissionsOnKill(@NonNull String packageName, + public void revokeSelfPermissionsOnKill(@NonNull String packageName, @NonNull List<String> permissions, @NonNull AndroidFuture callback) { try { - enforceSomePermissionsGrantedToCaller( - Manifest.permission.REVOKE_RUNTIME_PERMISSIONS); Objects.requireNonNull(callback); - onRevokeOwnPermissionsOnKill(packageName, permissions, + + final int callingUid = Binder.getCallingUid(); + int targetPackageUid = getPackageManager().getPackageUid(packageName, + PackageManager.PackageInfoFlags.of(0)); + if (targetPackageUid != callingUid) { + enforceSomePermissionsGrantedToCaller( + Manifest.permission.REVOKE_RUNTIME_PERMISSIONS); + } + onRevokeSelfPermissionsOnKill(packageName, permissions, () -> callback.complete(null)); } catch (Throwable t) { callback.completeExceptionally(t); diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java index c509de6bb5e1..7a797ce28870 100644 --- a/core/java/android/permission/PermissionManager.java +++ b/core/java/android/permission/PermissionManager.java @@ -76,7 +76,6 @@ import com.android.internal.annotations.Immutable; import com.android.internal.util.CollectionUtils; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Objects; @@ -626,19 +625,6 @@ public final class PermissionManager { } /** - * @see Context#revokeOwnPermissionsOnKill(Collection) - * @hide - */ - public void revokeOwnPermissionsOnKill(@NonNull Collection<String> permissions) { - try { - mPermissionManager.revokeOwnPermissionsOnKill(mContext.getPackageName(), - new ArrayList<String>(permissions)); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** * Gets the state flags associated with a permission. * * @param packageName the package name for which to get the flags diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java index 0b57842cde11..4ed939c48bd7 100644 --- a/core/java/android/permission/PermissionUsageHelper.java +++ b/core/java/android/permission/PermissionUsageHelper.java @@ -47,6 +47,7 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.res.Resources; import android.icu.text.ListFormatter; +import android.location.LocationManager; import android.media.AudioManager; import android.os.Process; import android.os.UserHandle; @@ -411,10 +412,13 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis } /** - * Returns true if the app supports subattribution. + * Returns true if the app satisfies subattribution policies and supports it */ private boolean isSubattributionSupported(String packageName, int uid) { try { + if (!isLocationProvider(packageName)) { + return false; + } PackageManager userPkgManager = getUserContext(UserHandle.getUserHandleForUid(uid)).getPackageManager(); ApplicationInfo appInfo = userPkgManager.getApplicationInfoAsUser(packageName, @@ -430,6 +434,15 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis } /** + * @param packageName + * @return If the package is location provider + */ + private boolean isLocationProvider(String packageName) { + return Objects.requireNonNull( + mContext.getSystemService(LocationManager.class)).isProviderPackage(packageName); + } + + /** * Get the raw usages from the system, and then parse out the ones that are not recent enough, * determine which permission group each belongs in, and removes duplicates (if the same app * uses multiple permissions of the same group). Stores the package name, attribution tag, user, diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index a6ad5e5863df..5191c9583379 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -2143,10 +2143,10 @@ public final class Settings { /** * Intent extra: The id of a setting restricted by supervisors. * <p> - * Type: Integer with a value from the SupervisorVerificationSetting annotation below. + * Type: Integer with a value from the one of the SUPERVISOR_VERIFICATION_* constants below. * <ul> - * <li>{@link #SUPERVISOR_VERIFICATION_SETTING_UNKNOWN} - * <li>{@link #SUPERVISOR_VERIFICATION_SETTING_BIOMETRICS} + * <li>{@see #SUPERVISOR_VERIFICATION_SETTING_UNKNOWN} + * <li>{@see #SUPERVISOR_VERIFICATION_SETTING_BIOMETRICS} * </ul> * </p> */ @@ -2154,12 +2154,14 @@ public final class Settings { "android.provider.extra.SUPERVISOR_RESTRICTED_SETTING_KEY"; /** - * Unknown setting. + * The unknown setting can usually be ignored and is used for compatibility with future + * supervisor settings. */ public static final int SUPERVISOR_VERIFICATION_SETTING_UNKNOWN = 0; /** - * Biometric settings for supervisors. + * Settings for supervisors to control what kinds of biometric sensors, such a face and + * fingerprint scanners, can be used on the device. */ public static final int SUPERVISOR_VERIFICATION_SETTING_BIOMETRICS = 1; diff --git a/core/java/android/service/displayhash/DisplayHashingService.java b/core/java/android/service/displayhash/DisplayHashingService.java index f22d40ea4d5e..3fac23b61a4b 100644 --- a/core/java/android/service/displayhash/DisplayHashingService.java +++ b/core/java/android/service/displayhash/DisplayHashingService.java @@ -68,9 +68,6 @@ public abstract class DisplayHashingService extends Service { private DisplayHashingServiceWrapper mWrapper; private Handler mHandler; - public DisplayHashingService() { - } - @Override public void onCreate() { super.onCreate(); diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java index bec5d1be57fd..bc42da6b4c97 100644 --- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java +++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java @@ -851,7 +851,8 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD}) public void triggerHardwareRecognitionEventForTest(int status, int soundModelHandle, boolean captureAvailable, int captureSession, int captureDelayMs, int capturePreambleMs, - boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data) { + boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data, + @NonNull List<KeyphraseRecognitionExtra> keyphraseRecognitionExtras) { Log.d(TAG, "triggerHardwareRecognitionEventForTest()"); synchronized (mLock) { if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) { @@ -862,7 +863,8 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { mModelManagementService.triggerHardwareRecognitionEventForTest( new KeyphraseRecognitionEvent(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs, capturePreambleMs, triggerInData, - captureFormat, data, null /* keyphraseExtras */), + captureFormat, data, keyphraseRecognitionExtras.toArray( + new KeyphraseRecognitionExtra[0])), mInternalCallback); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java index aebc5e86605a..01a037ae3495 100644 --- a/core/java/android/util/NtpTrustedTime.java +++ b/core/java/android/util/NtpTrustedTime.java @@ -140,6 +140,10 @@ public class NtpTrustedTime implements TrustedTime { /** An in-memory config override for use during tests. */ @Nullable + private Integer mPortForTests; + + /** An in-memory config override for use during tests. */ + @Nullable private Duration mTimeoutForTests; // Declared volatile and accessed outside of synchronized blocks to avoid blocking reads during @@ -163,9 +167,11 @@ public class NtpTrustedTime implements TrustedTime { * Overrides the NTP server config for tests. Passing {@code null} to a parameter clears the * test value, i.e. so the normal value will be used next time. */ - public void setServerConfigForTests(@Nullable String hostname, @Nullable Duration timeout) { + public void setServerConfigForTests( + @Nullable String hostname, @Nullable Integer port, @Nullable Duration timeout) { synchronized (this) { mHostnameForTests = hostname; + mPortForTests = port; mTimeoutForTests = timeout; } } @@ -195,8 +201,9 @@ public class NtpTrustedTime implements TrustedTime { if (LOGD) Log.d(TAG, "forceRefresh() from cache miss"); final SntpClient client = new SntpClient(); final String serverName = connectionInfo.getServer(); + final int port = connectionInfo.getPort(); final int timeoutMillis = connectionInfo.getTimeoutMillis(); - if (client.requestTime(serverName, timeoutMillis, network)) { + if (client.requestTime(serverName, port, timeoutMillis, network)) { long ntpCertainty = client.getRoundTripTime() / 2; mTimeResult = new TimeResult( client.getNtpTime(), client.getNtpTimeReference(), ntpCertainty); @@ -297,10 +304,12 @@ public class NtpTrustedTime implements TrustedTime { private static class NtpConnectionInfo { @NonNull private final String mServer; + private final int mPort; private final int mTimeoutMillis; - NtpConnectionInfo(@NonNull String server, int timeoutMillis) { + NtpConnectionInfo(@NonNull String server, int port, int timeoutMillis) { mServer = Objects.requireNonNull(server); + mPort = port; mTimeoutMillis = timeoutMillis; } @@ -309,6 +318,11 @@ public class NtpTrustedTime implements TrustedTime { return mServer; } + @NonNull + public int getPort() { + return mPort; + } + int getTimeoutMillis() { return mTimeoutMillis; } @@ -317,6 +331,7 @@ public class NtpTrustedTime implements TrustedTime { public String toString() { return "NtpConnectionInfo{" + "mServer='" + mServer + '\'' + + ", mPort='" + mPort + '\'' + ", mTimeoutMillis=" + mTimeoutMillis + '}'; } @@ -341,6 +356,13 @@ public class NtpTrustedTime implements TrustedTime { } } + final Integer port; + if (mPortForTests != null) { + port = mPortForTests; + } else { + port = SntpClient.STANDARD_NTP_PORT; + } + final int timeoutMillis; if (mTimeoutForTests != null) { timeoutMillis = (int) mTimeoutForTests.toMillis(); @@ -350,7 +372,8 @@ public class NtpTrustedTime implements TrustedTime { timeoutMillis = Settings.Global.getInt( resolver, Settings.Global.NTP_TIMEOUT, defaultTimeoutMillis); } - return TextUtils.isEmpty(hostname) ? null : new NtpConnectionInfo(hostname, timeoutMillis); + return TextUtils.isEmpty(hostname) ? null : + new NtpConnectionInfo(hostname, port, timeoutMillis); } /** Prints debug information. */ diff --git a/core/java/android/util/TimingsTraceLog.java b/core/java/android/util/TimingsTraceLog.java index 066709fd8744..48a5ceae1aef 100644 --- a/core/java/android/util/TimingsTraceLog.java +++ b/core/java/android/util/TimingsTraceLog.java @@ -147,7 +147,7 @@ public class TimingsTraceLog { * Logs a duration so it can be parsed by external tools for performance reporting. */ public void logDuration(String name, long timeMs) { - Slog.d(mTag, name + " took to complete: " + timeMs + "ms"); + Slog.v(mTag, name + " took to complete: " + timeMs + "ms"); } /** diff --git a/core/java/android/view/ContentRecordingSession.java b/core/java/android/view/ContentRecordingSession.java index db4ec1155e64..c66c70af0656 100644 --- a/core/java/android/view/ContentRecordingSession.java +++ b/core/java/android/view/ContentRecordingSession.java @@ -66,10 +66,11 @@ public final class ContentRecordingSession implements Parcelable { private int mContentToRecord = RECORD_CONTENT_DISPLAY; /** - * The window token of the layer of the hierarchy to record. - * The display content if {@link #getContentToRecord()} is - * {@link RecordContent#RECORD_CONTENT_DISPLAY}, or task if {@link #getContentToRecord()} is - * {@link RecordContent#RECORD_CONTENT_TASK}. + * The token of the layer of the hierarchy to record. + * If {@link #getContentToRecord()} is @link RecordContent#RECORD_CONTENT_DISPLAY}, then + * represents the WindowToken corresponding to the DisplayContent to record. + * If {@link #getContentToRecord()} is {@link RecordContent#RECORD_CONTENT_TASK}, then + * represents the {@link android.window.WindowContainerToken} of the Task to record. */ @VisibleForTesting @Nullable @@ -192,10 +193,11 @@ public final class ContentRecordingSession implements Parcelable { } /** - * The window token of the layer of the hierarchy to record. - * The display content if {@link #getContentToRecord()} is - * {@link RecordContent#RECORD_CONTENT_DISPLAY}, or task if {@link #getContentToRecord()} is - * {@link RecordContent#RECORD_CONTENT_TASK}. + * {The token of the layer of the hierarchy to record. + * If {@link #getContentToRecord()} is @link RecordContent#RECORD_CONTENT_DISPLAY}, then + * represents the WindowToken corresponding to the DisplayContent to record. + * If {@link #getContentToRecord()} is {@link RecordContent#RECORD_CONTENT_TASK}, then + * represents the {@link android.window.WindowContainerToken} of the Task to record. */ @DataClass.Generated.Member public @VisibleForTesting @Nullable IBinder getTokenToRecord() { @@ -231,10 +233,11 @@ public final class ContentRecordingSession implements Parcelable { } /** - * The window token of the layer of the hierarchy to record. - * The display content if {@link #getContentToRecord()} is - * {@link RecordContent#RECORD_CONTENT_DISPLAY}, or task if {@link #getContentToRecord()} is - * {@link RecordContent#RECORD_CONTENT_TASK}. + * {The token of the layer of the hierarchy to record. + * If {@link #getContentToRecord()} is @link RecordContent#RECORD_CONTENT_DISPLAY}, then + * represents the WindowToken corresponding to the DisplayContent to record. + * If {@link #getContentToRecord()} is {@link RecordContent#RECORD_CONTENT_TASK}, then + * represents the {@link android.window.WindowContainerToken} of the Task to record. */ @DataClass.Generated.Member public @NonNull ContentRecordingSession setTokenToRecord(@VisibleForTesting @NonNull IBinder value) { @@ -390,10 +393,11 @@ public final class ContentRecordingSession implements Parcelable { } /** - * The window token of the layer of the hierarchy to record. - * The display content if {@link #getContentToRecord()} is - * {@link RecordContent#RECORD_CONTENT_DISPLAY}, or task if {@link #getContentToRecord()} is - * {@link RecordContent#RECORD_CONTENT_TASK}. + * {The token of the layer of the hierarchy to record. + * If {@link #getContentToRecord()} is @link RecordContent#RECORD_CONTENT_DISPLAY}, then + * represents the WindowToken corresponding to the DisplayContent to record. + * If {@link #getContentToRecord()} is {@link RecordContent#RECORD_CONTENT_TASK}, then + * represents the {@link android.window.WindowContainerToken} of the Task to record. */ @DataClass.Generated.Member public @NonNull Builder setTokenToRecord(@VisibleForTesting @NonNull IBinder value) { @@ -433,7 +437,7 @@ public final class ContentRecordingSession implements Parcelable { } @DataClass.Generated( - time = 1644843382972L, + time = 1645803878639L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/view/ContentRecordingSession.java", inputSignatures = "public static final int RECORD_CONTENT_DISPLAY\npublic static final int RECORD_CONTENT_TASK\nprivate int mDisplayId\nprivate @android.view.ContentRecordingSession.RecordContent int mContentToRecord\nprivate @com.android.internal.annotations.VisibleForTesting @android.annotation.Nullable android.os.IBinder mTokenToRecord\npublic static android.view.ContentRecordingSession createDisplaySession(android.os.IBinder)\npublic static android.view.ContentRecordingSession createTaskSession(android.os.IBinder)\npublic static boolean isValid(android.view.ContentRecordingSession)\npublic static boolean isSameDisplay(android.view.ContentRecordingSession,android.view.ContentRecordingSession)\nclass ContentRecordingSession extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genToString=true, genSetters=true, genEqualsHashCode=true)") diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java index 190adbdfd5db..61098d60566f 100644 --- a/core/java/android/view/HandwritingInitiator.java +++ b/core/java/android/view/HandwritingInitiator.java @@ -55,10 +55,10 @@ public class HandwritingInitiator { */ private final int mTouchSlop; /** - * The timeout used to distinguish tap from handwriting. If the stylus doesn't move before this - * timeout, it's not considered as handwriting. + * The timeout used to distinguish tap or long click from handwriting. If the stylus doesn't + * move before this timeout, it's not considered as handwriting. */ - private final long mTapTimeoutInMillis; + private final long mHandwritingTimeoutInMillis; private State mState = new State(); private final HandwritingAreaTracker mHandwritingAreasTracker = new HandwritingAreaTracker(); @@ -90,7 +90,7 @@ public class HandwritingInitiator { public HandwritingInitiator(@NonNull ViewConfiguration viewConfiguration, @NonNull InputMethodManager inputMethodManager) { mTouchSlop = viewConfiguration.getScaledTouchSlop(); - mTapTimeoutInMillis = ViewConfiguration.getTapTimeout(); + mHandwritingTimeoutInMillis = ViewConfiguration.getLongPressTimeout(); mImm = inputMethodManager; } @@ -145,7 +145,7 @@ public class HandwritingInitiator { final long timeElapsed = motionEvent.getEventTime() - mState.mStylusDownTimeInMillis; - if (timeElapsed > mTapTimeoutInMillis) { + if (timeElapsed > mHandwritingTimeoutInMillis) { reset(); return; } diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java index a13579d0acad..406281d4cade 100644 --- a/core/java/android/view/SurfaceControlViewHost.java +++ b/core/java/android/view/SurfaceControlViewHost.java @@ -401,7 +401,7 @@ public class SurfaceControlViewHost { public void relayout(WindowManager.LayoutParams attrs, WindowlessWindowManager.ResizeCompleteCallback callback) { mViewRoot.setLayoutParams(attrs, false); - mViewRoot.setReportNextDraw(); + mViewRoot.setReportNextDraw(true /* syncBuffer */); mWm.setCompletionCallback(mViewRoot.mWindow.asBinder(), callback); } diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 413855639d09..22ccaae9e3c3 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -50,6 +50,7 @@ import android.view.accessibility.IAccessibilityEmbeddedConnection; import com.android.internal.view.SurfaceCallbackHelper; import java.util.ArrayList; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; @@ -203,8 +204,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall private int mSurfaceFlags = SurfaceControl.HIDDEN; - private int mPendingReportDraws; - /** * Transaction that should be used from the render thread. This transaction is only thread safe * with other calls directly from the render thread. @@ -212,11 +211,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall private final SurfaceControl.Transaction mRtTransaction = new SurfaceControl.Transaction(); /** - * Used on the main thread to set the transaction that will be synced with the main window. - */ - private final Transaction mSyncTransaction = new Transaction(); - - /** * Transaction that should be used whe * {@link HardwareRenderer.FrameDrawingCallback#onFrameDraw} is invoked. All * frame callbacks can use the same transaction since they will be thread safe @@ -391,31 +385,12 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } } - private void performDrawFinished(@Nullable Transaction t) { - if (t != null) { - mSyncTransaction.merge(t); - } - - if (mPendingReportDraws > 0) { - mDrawFinished = true; - if (mAttachedToWindow) { - mParent.requestTransparentRegion(SurfaceView.this); - notifyDrawFinished(); - invalidate(); - } - } else { - Log.e(TAG, System.identityHashCode(this) + "finished drawing" - + " but no pending report draw (extra call" - + " to draw completion runnable?)"); - } - } - - void notifyDrawFinished() { - ViewRootImpl viewRoot = getViewRootImpl(); - if (viewRoot != null) { - viewRoot.pendingDrawFinished(mSyncTransaction); + private void performDrawFinished() { + mDrawFinished = true; + if (mAttachedToWindow) { + mParent.requestTransparentRegion(SurfaceView.this); + invalidate(); } - mPendingReportDraws--; } @Override @@ -438,10 +413,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall mGlobalListenersAdded = false; } - while (mPendingReportDraws > 0) { - notifyDrawFinished(); - } - mRequestedVisible = false; updateSurface(); @@ -993,10 +964,17 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall return; } - final boolean realSizeChanged = performSurfaceTransaction(viewRoot, - translator, creating, sizeChanged, hintChanged, surfaceUpdateTransaction); final boolean redrawNeeded = sizeChanged || creating || hintChanged || (mVisible && !mDrawFinished); + final TransactionCallback transactionCallback = + redrawNeeded ? new TransactionCallback() : null; + if (redrawNeeded && viewRoot.wasRelayoutRequested()) { + mBlastBufferQueue.syncNextTransaction( + false /* acquireSingleBuffer */, + transactionCallback::onTransactionReady); + } + final boolean realSizeChanged = performSurfaceTransaction(viewRoot, + translator, creating, sizeChanged, hintChanged, surfaceUpdateTransaction); try { SurfaceHolder.Callback[] callbacks = null; @@ -1015,9 +993,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall mIsCreating = true; if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "visibleChanged -- surfaceCreated"); - if (callbacks == null) { - callbacks = getSurfaceCallbacks(); - } + callbacks = getSurfaceCallbacks(); for (SurfaceHolder.Callback c : callbacks) { c.surfaceCreated(mSurfaceHolder); } @@ -1035,32 +1011,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } } if (redrawNeeded) { - if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " - + "surfaceRedrawNeeded"); - if (callbacks == null) { - callbacks = getSurfaceCallbacks(); - } - - final boolean wasRelayoutRequested = viewRoot.wasRelayoutRequested(); - if (wasRelayoutRequested && (mBlastBufferQueue != null)) { - mBlastBufferQueue.syncNextTransaction( - false /* acquireSingleBuffer */, - this::onDrawFinished); - } - mPendingReportDraws++; - viewRoot.drawPending(); - SurfaceCallbackHelper sch = new SurfaceCallbackHelper(() -> { - if (mBlastBufferQueue != null) { - mBlastBufferQueue.stopContinuousSyncTransaction(); - } - // If relayout was requested, then a callback from BBQ will - // be invoked with the sync transaction. onDrawFinished will be - // called in there - if (!wasRelayoutRequested) { - onDrawFinished(null); - } - }); - sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks); + redrawNeeded(callbacks, transactionCallback); } } } finally { @@ -1079,6 +1030,64 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } } + private void redrawNeeded(SurfaceHolder.Callback[] callbacks, + @Nullable TransactionCallback transactionCallback) { + if (DEBUG) { + Log.i(TAG, System.identityHashCode(this) + " surfaceRedrawNeeded"); + } + final SurfaceHolder.Callback[] capturedCallbacks = + callbacks == null ? getSurfaceCallbacks() : callbacks; + + ViewRootImpl viewRoot = getViewRootImpl(); + boolean isVriSync = viewRoot.addToSync(syncBufferCallback -> + redrawNeededAsync(capturedCallbacks, () -> { + if (mBlastBufferQueue != null) { + mBlastBufferQueue.stopContinuousSyncTransaction(); + } + + Transaction t = null; + if (transactionCallback != null && mBlastBufferQueue != null) { + t = transactionCallback.waitForTransaction(); + } + // If relayout was requested, then a callback from BBQ will + // be invoked with the sync transaction. onDrawFinished will be + // called in there + syncBufferCallback.onBufferReady(t); + onDrawFinished(); + })); + + // If isVriSync, then everything was setup in the addToSync. + if (isVriSync) { + return; + } + + redrawNeededAsync(capturedCallbacks, this::onDrawFinished); + } + + private void redrawNeededAsync(SurfaceHolder.Callback[] callbacks, + Runnable callbacksCollected) { + SurfaceCallbackHelper sch = new SurfaceCallbackHelper(callbacksCollected); + sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks); + } + + private static class TransactionCallback { + private final CountDownLatch mCountDownLatch = new CountDownLatch(1); + private Transaction mTransaction; + + Transaction waitForTransaction() { + try { + mCountDownLatch.await(); + } catch (InterruptedException e) { + } + return mTransaction; + } + + void onTransactionReady(Transaction t) { + mTransaction = t; + mCountDownLatch.countDown(); + } + } + /** * Copy the Surface from the SurfaceControl or the blast adapter. * @@ -1189,13 +1198,13 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall mBlastBufferQueue.update(mBlastSurfaceControl, mSurfaceWidth, mSurfaceHeight, mFormat); } - private void onDrawFinished(@Nullable Transaction t) { + private void onDrawFinished() { if (DEBUG) { Log.i(TAG, System.identityHashCode(this) + " " + "finishedDrawing"); } - runOnUiThread(() -> performDrawFinished(t)); + runOnUiThread(this::performDrawFinished); } /** diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index b72725ad2c32..17887d3447be 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -583,11 +583,15 @@ public final class ViewRootImpl implements ViewParent, boolean mReportNextDraw; + /** - * Set whether the draw should use blast sync. This is in case the draw is canceled, - * but will be rescheduled. We still want the next draw to be sync. + * Set whether the draw should send the buffer to system server. When set to true, VRI will + * create a sync transaction with BBQ and send the resulting buffer to system server. If false, + * VRI will not try to sync a buffer in BBQ, but still report when a draw occurred. + * + * Default is true since we normally want to sync the buffer. */ - boolean mNextDrawUseBlastSync; + private boolean mSyncBuffer = true; boolean mFullRedrawNeeded; boolean mNewSurfaceNeeded; @@ -808,6 +812,10 @@ public final class ViewRootImpl implements ViewParent, return mHandwritingInitiator; } + private final SurfaceSyncer mSurfaceSyncer = new SurfaceSyncer(); + private int mLastSyncId = -1; + private SurfaceSyncer.SyncBufferCallback mSyncBufferCallback; + /** * Keeps track of the last frame number that was attempted to draw. Should only be accessed on * the RenderThread. @@ -2880,8 +2888,6 @@ public final class ViewRootImpl implements ViewParent, mView.onSystemBarAppearanceChanged(mDispatchedSystemBarAppearance); } } - final boolean wasReportNextDraw = mReportNextDraw; - boolean useBlastSync = mNextDrawUseBlastSync; if (mFirst || windowShouldResize || viewVisibilityChanged || params != null || mForceNextWindowRelayout) { @@ -2923,9 +2929,6 @@ public final class ViewRootImpl implements ViewParent, Log.d(mTag, "Relayout called with blastSync"); } reportNextDraw(); - if (isHardwareEnabled()) { - useBlastSync = true; - } } final boolean surfaceControlChanged = @@ -3164,7 +3167,7 @@ public final class ViewRootImpl implements ViewParent, // done to achieve a more hermetic fix for S, but it's entirely // possible that checking the most recent value is actually more // correct here. - if (!mStopped || wasReportNextDraw) { + if (!mStopped || mReportNextDraw) { if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight() || dispatchApplyInsets || updatedConfiguration) { int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width, @@ -3233,7 +3236,7 @@ public final class ViewRootImpl implements ViewParent, prepareSurfaces(); } - final boolean didLayout = layoutRequested && (!mStopped || wasReportNextDraw); + final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw); boolean triggerGlobalLayoutListener = didLayout || mAttachInfo.mRecomputeGlobalAttributes; if (didLayout) { @@ -3426,51 +3429,40 @@ public final class ViewRootImpl implements ViewParent, mImeFocusController.onTraversal(hasWindowFocus, mWindowAttributes); - // Remember if we must report the next draw. - if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) { + // If we already got a request for blast sync, then we don't want to unset mSyncBuffer + if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0 + && !mReportNextDraw) { reportNextDraw(); + mSyncBuffer = false; } - boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible; - if (mBLASTDrawConsumer != null) { - useBlastSync = true; + boolean cancelAndRedraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw(); + if (!cancelAndRedraw) { + createSyncIfNeeded(); } - if (!cancelDraw) { + if (!isViewVisible) { if (mPendingTransitions != null && mPendingTransitions.size() > 0) { for (int i = 0; i < mPendingTransitions.size(); ++i) { - mPendingTransitions.get(i).startChangingAnimations(); + mPendingTransitions.get(i).endChangingAnimations(); } mPendingTransitions.clear(); } - performDraw(useBlastSync); - mNextDrawUseBlastSync = false; - } else { - if (isViewVisible) { - // Try again - mNextDrawUseBlastSync = useBlastSync; - scheduleTraversals(); - } else { - if (mPendingTransitions != null && mPendingTransitions.size() > 0) { - for (int i = 0; i < mPendingTransitions.size(); ++i) { - mPendingTransitions.get(i).endChangingAnimations(); - } - mPendingTransitions.clear(); - } - // We may never draw since it's not visible. Report back that we're finished - // drawing. - if (!wasReportNextDraw && mReportNextDraw) { - mReportNextDraw = false; - pendingDrawFinished(); - } - - // Make sure the consumer is not waiting if the view root was just made invisible. - if (mBLASTDrawConsumer != null) { - mBLASTDrawConsumer.accept(null); - mBLASTDrawConsumer = null; + if (mSyncBufferCallback != null) { + mSyncBufferCallback.onBufferReady(null); + } + } else if (cancelAndRedraw) { + // Try again + scheduleTraversals(); + } else { + if (mPendingTransitions != null && mPendingTransitions.size() > 0) { + for (int i = 0; i < mPendingTransitions.size(); ++i) { + mPendingTransitions.get(i).startChangingAnimations(); } + mPendingTransitions.clear(); } + performDraw(); } if (mAttachInfo.mContentCaptureEvents != null) { @@ -3479,6 +3471,46 @@ public final class ViewRootImpl implements ViewParent, mIsInTraversal = false; mRelayoutRequested = false; + + if (!cancelAndRedraw) { + mReportNextDraw = false; + mSyncBufferCallback = null; + mSyncBuffer = true; + if (mLastSyncId != -1) { + mSurfaceSyncer.markSyncReady(mLastSyncId); + mLastSyncId = -1; + } + } + } + + private void createSyncIfNeeded() { + // Started a sync already. + if (mLastSyncId != -1) { + return; + } + + Consumer<Transaction> syncConsumer = null; + if (mBLASTDrawConsumer != null) { + syncConsumer = mBLASTDrawConsumer; + mBLASTDrawConsumer = null; + } else if (mReportNextDraw) { + syncConsumer = transaction -> { + mSurfaceChangedTransaction.merge(transaction); + reportDrawFinished(); + }; + } + + if (syncConsumer != null) { + final Consumer<Transaction> capturedSyncConsumer = syncConsumer; + mLastSyncId = mSurfaceSyncer.setupSync(transaction -> { + // Callback will be invoked on executor thread so post to main thread. + mHandler.postAtFrontOfQueue(() -> capturedSyncConsumer.accept(transaction)); + }); + if (DEBUG_BLAST) { + Log.d(mTag, "Setup new sync id=" + mLastSyncId); + } + mSurfaceSyncer.addToSync(mLastSyncId, mSyncTarget); + } } private void notifyContentCatpureEvents() { @@ -4092,54 +4124,10 @@ public final class ViewRootImpl implements ViewParent, } } - /** - * A count of the number of calls to pendingDrawFinished we - * require to notify the WM drawing is complete. - */ - int mDrawsNeededToReport = 0; - - /** - * Delay notifying WM of draw finished until - * a balanced call to pendingDrawFinished. - */ - void drawPending() { - mDrawsNeededToReport++; - } - - void pendingDrawFinished(Transaction t) { - if (mDrawsNeededToReport == 0) { - throw new RuntimeException("Unbalanced drawPending/pendingDrawFinished calls"); - } - - if (t != null) { - if (DEBUG_BLAST) { - Log.d(mTag, "Merging transaction into main window transaction"); - } - mSurfaceChangedTransaction.merge(t); - } - - mDrawsNeededToReport--; - if (mDrawsNeededToReport == 0) { - reportDrawFinished(); - } else if (DEBUG_BLAST) { - Log.d(mTag, "pendingDrawFinished. Waiting on draw reported mDrawsNeededToReport=" - + mDrawsNeededToReport); - } - } - - void pendingDrawFinished() { - pendingDrawFinished(null); - } - - private void postDrawFinished() { - mHandler.sendEmptyMessage(MSG_DRAW_FINISHED); - } - private void reportDrawFinished() { if (DEBUG_BLAST) { - Log.d(mTag, "reportDrawFinished"); + Log.d(mTag, "reportDrawFinished " + Debug.getCallers(5)); } - mDrawsNeededToReport = 0; try { mWindowSession.finishDrawing(mWindow, mSurfaceChangedTransaction, Integer.MAX_VALUE); @@ -4158,6 +4146,14 @@ public final class ViewRootImpl implements ViewParent, return mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled(); } + boolean addToSync(SurfaceSyncer.SyncTarget syncable) { + if (mLastSyncId == -1) { + return false; + } + mSurfaceSyncer.addToSync(mLastSyncId, syncable); + return true; + } + private void addFrameCommitCallbackIfNeeded() { if (!isHardwareEnabled()) { return; @@ -4188,188 +4184,81 @@ public final class ViewRootImpl implements ViewParent, }); } - private HardwareRenderer.FrameCommitCallback createFrameCommitCallbackForSync( - boolean useBlastSync, boolean reportNextDraw, Consumer<Transaction> blastSyncConsumer) { - return didProduceBuffer -> { - if (DEBUG_BLAST) { - Log.d(mTag, "Received frameCommittedCallback " - + " lastAttemptedDrawFrameNum=" + mRtLastAttemptedDrawFrameNum - + " didProduceBuffer=" + didProduceBuffer); - } - - // If frame wasn't drawn, clear out the next transaction so it doesn't affect the next - // draw attempt. The next transaction and transaction complete callback were only set - // for the current draw attempt. - final Transaction pendingTransactions; - if (!didProduceBuffer) { - mBlastBufferQueue.syncNextTransaction(null); - // Get the transactions that were sent to mergeWithNextTransaction since the - // frame didn't draw on this vsync. It's possible the frame will draw later, but - // it's better to not be sync than to block on a frame that may never come. - pendingTransactions = mBlastBufferQueue.gatherPendingTransactions( - mRtLastAttemptedDrawFrameNum); - if (!useBlastSync && !reportNextDraw) { - pendingTransactions.apply(); - } - } else { - pendingTransactions = null; - } - // Post at front of queue so the buffer can be processed immediately and allow RT - // to continue processing new buffers. If RT tries to process buffers before the sync - // buffer is applied, the new buffers will not get acquired and could result in a - // deadlock. UI thread would wait on RT, but RT would be blocked waiting for a free - // buffer. - mHandler.postAtFrontOfQueue(() -> { - if (!didProduceBuffer && useBlastSync) { - mSurfaceChangedTransaction.merge(pendingTransactions); - if (blastSyncConsumer != null) { - blastSyncConsumer.accept(mSurfaceChangedTransaction); - } - } - - // This is to ensure pendingDrawFinished is only called exactly one time per draw - // attempt when reportNextDraw is true. Since, we sometimes create a sync - // transaction callback, the callback will handle calling pendingDrawFinished. - // However, there are cases where the transaction callback may not be called. - // 1. If useBlastSync is false, then we know that a sync transaction callback was - // not created so we won't invoke pendingDrawFinished there. - // 2. If the draw didn't produce a frame, didProduceBuffer == false, then we know - // the sync transaction callback will not be invoked even if one was set up. - if (reportNextDraw && (!didProduceBuffer || !useBlastSync)) { - pendingDrawFinished(); - } - }); - - }; - } - @Nullable - private FrameDrawingCallback createFrameDrawingCallbackIfNeeded(boolean useBlastSync, - boolean reportNextDraw) { + private void registerFrameDrawingCallbackForBlur() { if (!isHardwareEnabled()) { - return null; + return; } final boolean hasBlurUpdates = mBlurRegionAggregator.hasUpdates(); final boolean needsCallbackForBlur = hasBlurUpdates || mBlurRegionAggregator.hasRegions(); - if (!useBlastSync && !needsCallbackForBlur && !reportNextDraw && !mHasPendingTransactions) { - return null; - } - - final Consumer<SurfaceControl.Transaction> blastSyncConsumer = mBLASTDrawConsumer; - mBLASTDrawConsumer = null; - - if (DEBUG_BLAST) { - Log.d(mTag, "Creating frameDrawingCallback" - + " nextDrawUseBlastSync=" + useBlastSync - + " reportNextDraw=" + reportNextDraw - + " hasBlurUpdates=" + hasBlurUpdates - + " hasBlastSyncConsumer=" + (blastSyncConsumer != null) - + " mHasPendingTransactions=" + mHasPendingTransactions); + if (!needsCallbackForBlur) { + return; } final BackgroundBlurDrawable.BlurRegion[] blurRegionsForFrame = - needsCallbackForBlur ? mBlurRegionAggregator.getBlurRegionsCopyForRT() : null; - final boolean hasPendingTransactions = mHasPendingTransactions; - mHasPendingTransactions = false; - + mBlurRegionAggregator.getBlurRegionsCopyForRT(); // The callback will run on the render thread. - return new FrameDrawingCallback() { - @Override - public void onFrameDraw(long frame) { - } + registerRtFrameCallback((frame) -> mBlurRegionAggregator + .dispatchBlurTransactionIfNeeded(frame, blurRegionsForFrame, hasBlurUpdates)); + } + private void registerCallbackForPendingTransactions() { + registerRtFrameCallback(new FrameDrawingCallback() { @Override public HardwareRenderer.FrameCommitCallback onFrameDraw(int syncResult, long frame) { - if (DEBUG_BLAST) { - Log.d(mTag, - "Received frameDrawingCallback syncResult=" + syncResult + " frameNum=" - + frame + "."); - } - - mRtLastAttemptedDrawFrameNum = frame; - - if (needsCallbackForBlur) { - mBlurRegionAggregator.dispatchBlurTransactionIfNeeded(frame, - blurRegionsForFrame, hasBlurUpdates); - } - - if (mBlastBufferQueue == null) { - return null; - } - - if (!useBlastSync && !reportNextDraw && !hasPendingTransactions) { - return null; - } - - // If the syncResults are SYNC_LOST_SURFACE_REWARD_IF_FOUND or - // SYNC_CONTEXT_IS_STOPPED it means nothing will draw. There's no need to set up - // any blast sync or commit callback, and the code should directly call - // pendingDrawFinished. if ((syncResult & (SYNC_LOST_SURFACE_REWARD_IF_FOUND | SYNC_CONTEXT_IS_STOPPED)) != 0) { - if (reportNextDraw) { - mHandler.postAtFrontOfQueue(() -> pendingDrawFinished()); - } + mBlastBufferQueue.applyPendingTransactions(frame); return null; } - if (DEBUG_BLAST) { - Log.d(mTag, "Setting up sync and frameCommitCallback"); - } - - if (useBlastSync) { - // Frame callbacks will always occur after submitting draw requests and before - // the draw actually occurs. This will ensure that we set the next transaction - // for the frame that's about to get drawn and not on a previous frame. - mBlastBufferQueue.syncNextTransaction( - t -> { - mHandler.postAtFrontOfQueue(() -> { - mSurfaceChangedTransaction.merge(t); - if (blastSyncConsumer != null) { - blastSyncConsumer.accept(mSurfaceChangedTransaction); - } + return didProduceBuffer -> { + if (!didProduceBuffer) { + mBlastBufferQueue.applyPendingTransactions(frame); + } + }; - if (reportNextDraw) { - pendingDrawFinished(); - } - }); - }); - } + } - return createFrameCommitCallbackForSync(useBlastSync, reportNextDraw, - blastSyncConsumer); + @Override + public void onFrameDraw(long frame) { } - }; + }); } - private void performDraw(boolean useBlastSync) { + private void performDraw() { if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) { return; } else if (mView == null) { return; } - final boolean fullRedrawNeeded = mFullRedrawNeeded || mReportNextDraw || useBlastSync; + final boolean fullRedrawNeeded = mFullRedrawNeeded || mSyncBufferCallback != null; mFullRedrawNeeded = false; mIsDrawing = true; Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw"); - FrameDrawingCallback frameDrawingCallback = createFrameDrawingCallbackIfNeeded(useBlastSync, - mReportNextDraw); - if (frameDrawingCallback != null) { - mAttachInfo.mThreadedRenderer.registerRtFrameCallback(frameDrawingCallback); - } + registerFrameDrawingCallbackForBlur(); addFrameCommitCallbackIfNeeded(); - boolean usingAsyncReport = isHardwareEnabled() && (useBlastSync || mReportNextDraw); + + boolean usingAsyncReport = isHardwareEnabled() && mSyncBufferCallback != null; + if (usingAsyncReport) { + registerCallbacksForSync(mSyncBuffer, mSyncBufferCallback); + } else if (mHasPendingTransactions) { + // These callbacks are only needed if there's no sync involved and there were calls to + // applyTransactionOnDraw. These callbacks check if the draw failed for any reason and + // apply those transactions directly so they don't get stuck forever. + registerCallbackForPendingTransactions(); + } + mHasPendingTransactions = false; try { boolean canUseAsync = draw(fullRedrawNeeded); if (usingAsyncReport && !canUseAsync) { mAttachInfo.mThreadedRenderer.setFrameCallback(null); usingAsyncReport = false; - mAttachInfo.mThreadedRenderer.unregisterRtFrameCallback(frameDrawingCallback); } } finally { mIsDrawing = false; @@ -4387,7 +4276,6 @@ public final class ViewRootImpl implements ViewParent, } if (mReportNextDraw) { - mReportNextDraw = false; // if we're using multi-thread renderer, wait for the window frame draws if (mWindowDrawCountDown != null) { @@ -4408,7 +4296,11 @@ public final class ViewRootImpl implements ViewParent, } if (mSurfaceHolder != null && mSurface.isValid()) { - SurfaceCallbackHelper sch = new SurfaceCallbackHelper(this::postDrawFinished); + final SurfaceSyncer.SyncBufferCallback syncBufferCallback = mSyncBufferCallback; + SurfaceCallbackHelper sch = new SurfaceCallbackHelper(() -> + mHandler.post(() -> syncBufferCallback.onBufferReady(null))); + mSyncBufferCallback = null; + SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks); @@ -4416,9 +4308,11 @@ public final class ViewRootImpl implements ViewParent, if (mAttachInfo.mThreadedRenderer != null) { mAttachInfo.mThreadedRenderer.fence(); } - pendingDrawFinished(); } } + if (mSyncBufferCallback != null && !usingAsyncReport) { + mSyncBufferCallback.onBufferReady(null); + } if (mPerformContentCapture) { performContentCaptureInitialReport(); } @@ -5427,7 +5321,6 @@ public final class ViewRootImpl implements ViewParent, private static final int MSG_REQUEST_KEYBOARD_SHORTCUTS = 26; private static final int MSG_UPDATE_POINTER_ICON = 27; private static final int MSG_POINTER_CAPTURE_CHANGED = 28; - private static final int MSG_DRAW_FINISHED = 29; private static final int MSG_INSETS_CHANGED = 30; private static final int MSG_INSETS_CONTROL_CHANGED = 31; private static final int MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED = 32; @@ -5490,8 +5383,6 @@ public final class ViewRootImpl implements ViewParent, return "MSG_UPDATE_POINTER_ICON"; case MSG_POINTER_CAPTURE_CHANGED: return "MSG_POINTER_CAPTURE_CHANGED"; - case MSG_DRAW_FINISHED: - return "MSG_DRAW_FINISHED"; case MSG_INSETS_CHANGED: return "MSG_INSETS_CHANGED"; case MSG_INSETS_CONTROL_CHANGED: @@ -5724,9 +5615,6 @@ public final class ViewRootImpl implements ViewParent, final boolean hasCapture = msg.arg1 != 0; handlePointerCaptureChanged(hasCapture); } break; - case MSG_DRAW_FINISHED: { - pendingDrawFinished(); - } break; case MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED: { systemGestureExclusionChanged(); } break; @@ -9908,8 +9796,8 @@ public final class ViewRootImpl implements ViewParent, } private void reportNextDraw() { - if (mReportNextDraw == false) { - drawPending(); + if (DEBUG_BLAST) { + Log.d(mTag, "reportNextDraw " + Debug.getCallers(5)); } mReportNextDraw = true; } @@ -9920,9 +9808,14 @@ public final class ViewRootImpl implements ViewParent, * This method is only supposed to be used to speed up the interaction from SystemUI and window * manager when waiting for the first frame to be drawn when turning on the screen. DO NOT USE * unless you fully understand this interaction. + * + * @param syncBuffer If true, the transaction that contains the buffer from the draw should be + * sent to system to be synced. If false, VRI will not try to sync the buffer, + * but only report back that a buffer was drawn. * @hide */ - public void setReportNextDraw() { + public void setReportNextDraw(boolean syncBuffer) { + mSyncBuffer = syncBuffer; reportNextDraw(); invalidate(); } @@ -10923,7 +10816,7 @@ public final class ViewRootImpl implements ViewParent, } }; mOnBackInvokedDispatcher.registerOnBackInvokedCallback( - mCompatOnBackInvokedCallback, OnBackInvokedDispatcher.PRIORITY_DEFAULT); + OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCompatOnBackInvokedCallback); } private void unregisterCompatOnBackInvokedCallback() { @@ -10948,14 +10841,15 @@ public final class ViewRootImpl implements ViewParent, return mWindowSession; } - private void registerCallbacksForSync( + private void registerCallbacksForSync(boolean syncBuffer, final SurfaceSyncer.SyncBufferCallback syncBufferCallback) { if (!isHardwareEnabled()) { - // TODO: correctly handle when hardware disabled - syncBufferCallback.onBufferReady(null); return; } + if (DEBUG_BLAST) { + Log.d(mTag, "registerCallbacksForSync syncBuffer=" + syncBuffer); + } mAttachInfo.mThreadedRenderer.registerRtFrameCallback(new FrameDrawingCallback() { @Override public void onFrameDraw(long frame) { @@ -10984,7 +10878,9 @@ public final class ViewRootImpl implements ViewParent, Log.d(mTag, "Setting up sync and frameCommitCallback"); } - mBlastBufferQueue.syncNextTransaction(t -> syncBufferCallback.onBufferReady(t)); + if (syncBuffer) { + mBlastBufferQueue.syncNextTransaction(syncBufferCallback::onBufferReady); + } return didProduceBuffer -> { if (DEBUG_BLAST) { @@ -10998,18 +10894,40 @@ public final class ViewRootImpl implements ViewParent, // were only set for the current draw attempt. if (!didProduceBuffer) { mBlastBufferQueue.syncNextTransaction(null); + // Gather the transactions that were sent to mergeWithNextTransaction // since the frame didn't draw on this vsync. It's possible the frame will // draw later, but it's better to not be sync than to block on a frame that // may never come. syncBufferCallback.onBufferReady( mBlastBufferQueue.gatherPendingTransactions(frame)); + return; + } + + // If we didn't request to sync a buffer, then we won't get the + // syncNextTransaction callback. Instead, just report back to the Syncer so it + // knows that this sync request is complete. + if (!syncBuffer) { + syncBufferCallback.onBufferReady(null); } }; } }); } - public final SurfaceSyncer.SyncTarget mSyncTarget = - syncBufferCallback -> registerCallbacksForSync(syncBufferCallback); + public final SurfaceSyncer.SyncTarget mSyncTarget = this::readyToSync; + + private void readyToSync(SurfaceSyncer.SyncBufferCallback syncBufferCallback) { + if (mSyncBufferCallback != null) { + Log.d(mTag, "Already set sync for the next draw."); + mSyncBufferCallback.onBufferReady(null); + } + if (DEBUG_BLAST) { + Log.d(mTag, "Setting syncFrameCallback"); + } + mSyncBufferCallback = syncBufferCallback; + if (!mIsInTraversal && !mTraversalScheduled) { + scheduleTraversals(); + } + } } diff --git a/core/java/android/window/OnBackInvokedCallback.java b/core/java/android/window/OnBackInvokedCallback.java index dcd80fd76e09..400a56f2c485 100644 --- a/core/java/android/window/OnBackInvokedCallback.java +++ b/core/java/android/window/OnBackInvokedCallback.java @@ -33,7 +33,7 @@ import android.view.View; * within the same priority. Between different pirorities, callbacks with higher priority * are invoked first. * - * See {@link OnBackInvokedDispatcher#registerOnBackInvokedCallback(OnBackInvokedCallback, int)} + * See {@link OnBackInvokedDispatcher#registerOnBackInvokedCallback(int, OnBackInvokedCallback)} * for specifying callback priority. */ public interface OnBackInvokedCallback { diff --git a/core/java/android/window/OnBackInvokedDispatcher.java b/core/java/android/window/OnBackInvokedDispatcher.java index 63e6d30c8c0b..5eed8cde8c7c 100644 --- a/core/java/android/window/OnBackInvokedDispatcher.java +++ b/core/java/android/window/OnBackInvokedDispatcher.java @@ -94,15 +94,15 @@ public interface OnBackInvokedDispatcher { * Within the same priority level, callbacks are invoked in the reverse order in which * they are registered. Higher priority callbacks are invoked before lower priority ones. * + * @param priority The priority of the callback. * @param callback The callback to be registered. If the callback instance has been already * registered, the existing instance (no matter its priority) will be * unregistered and registered again. - * @param priority The priority of the callback. * @throws {@link IllegalArgumentException} if the priority is negative. */ - @SuppressLint({"SamShouldBeLast", "ExecutorRegistration"}) + @SuppressLint({"ExecutorRegistration"}) void registerOnBackInvokedCallback( - @NonNull OnBackInvokedCallback callback, @Priority @IntRange(from = 0) int priority); + @Priority @IntRange(from = 0) int priority, @NonNull OnBackInvokedCallback callback); /** * Unregisters a {@link OnBackInvokedCallback}. diff --git a/core/java/android/window/ProxyOnBackInvokedDispatcher.java b/core/java/android/window/ProxyOnBackInvokedDispatcher.java index cf17a2116aac..2b2f5e945710 100644 --- a/core/java/android/window/ProxyOnBackInvokedDispatcher.java +++ b/core/java/android/window/ProxyOnBackInvokedDispatcher.java @@ -44,7 +44,7 @@ public class ProxyOnBackInvokedDispatcher implements OnBackInvokedDispatcher { /** * List of pair representing an {@link OnBackInvokedCallback} and its associated priority. * - * @see OnBackInvokedDispatcher#registerOnBackInvokedCallback(OnBackInvokedCallback, int) + * @see OnBackInvokedDispatcher#registerOnBackInvokedCallback(int, OnBackInvokedCallback) */ private final List<Pair<OnBackInvokedCallback, Integer>> mCallbacks = new ArrayList<>(); private final Object mLock = new Object(); @@ -52,7 +52,7 @@ public class ProxyOnBackInvokedDispatcher implements OnBackInvokedDispatcher { @Override public void registerOnBackInvokedCallback( - @NonNull OnBackInvokedCallback callback, int priority) { + int priority, @NonNull OnBackInvokedCallback callback) { if (DEBUG) { Log.v(TAG, String.format("Pending register %s. Actual=%s", callback, mActualDispatcherOwner)); @@ -91,7 +91,7 @@ public class ProxyOnBackInvokedDispatcher implements OnBackInvokedDispatcher { mCallbacks.add(Pair.create(callback, priority)); if (mActualDispatcherOwner != null) { mActualDispatcherOwner.getOnBackInvokedDispatcher().registerOnBackInvokedCallback( - callback, priority); + priority, callback); } } } @@ -115,7 +115,7 @@ public class ProxyOnBackInvokedDispatcher implements OnBackInvokedDispatcher { for (Pair<OnBackInvokedCallback, Integer> callbackPair : mCallbacks) { int priority = callbackPair.second; if (priority >= 0) { - dispatcher.registerOnBackInvokedCallback(callbackPair.first, priority); + dispatcher.registerOnBackInvokedCallback(priority, callbackPair.first); } else { dispatcher.registerSystemOnBackInvokedCallback(callbackPair.first); } diff --git a/core/java/android/window/WindowInfosListener.java b/core/java/android/window/WindowInfosListener.java index 9d4545c6e25d..8db5a5eb0e47 100644 --- a/core/java/android/window/WindowInfosListener.java +++ b/core/java/android/window/WindowInfosListener.java @@ -17,6 +17,7 @@ package android.window; import android.graphics.Matrix; +import android.util.Pair; import android.util.Size; import android.view.InputWindowHandle; @@ -47,9 +48,13 @@ public abstract class WindowInfosListener { /** * Register the WindowInfosListener. + * + * @return The cached values for InputWindowHandles and DisplayInfos. This is the last updated + * value that was sent from SurfaceFlinger to this particular process. If there was nothing + * registered previously, then the data can be empty. */ - public void register() { - nativeRegister(mNativeListener); + public Pair<InputWindowHandle[], DisplayInfo[]> register() { + return nativeRegister(mNativeListener); } /** @@ -60,7 +65,7 @@ public abstract class WindowInfosListener { } private static native long nativeCreate(WindowInfosListener thiz); - private static native void nativeRegister(long ptr); + private static native Pair<InputWindowHandle[], DisplayInfo[]> nativeRegister(long ptr); private static native void nativeUnregister(long ptr); private static native long nativeGetFinalizer(); diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java index d046cefee5f7..97573c291340 100644 --- a/core/java/android/window/WindowOnBackInvokedDispatcher.java +++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java @@ -83,7 +83,7 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { // TODO: Take an Executor for the callback to run on. @Override public void registerOnBackInvokedCallback( - @NonNull OnBackInvokedCallback callback, @Priority int priority) { + @Priority int priority, @NonNull OnBackInvokedCallback callback) { if (priority < 0) { throw new IllegalArgumentException("Application registered OnBackInvokedCallback " + "cannot have negative priority. Priority: " + priority); diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java index 34c47ede99f0..06bc4b56901a 100644 --- a/core/java/com/android/internal/jank/FrameTracker.java +++ b/core/java/com/android/internal/jank/FrameTracker.java @@ -33,6 +33,7 @@ import android.annotation.Nullable; import android.graphics.HardwareRendererObserver; import android.os.Handler; import android.os.Trace; +import android.text.TextUtils; import android.util.Log; import android.util.SparseArray; import android.view.Choreographer; @@ -188,6 +189,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener if (mBeginVsyncId != INVALID_ID) { mSurfaceControlWrapper.addJankStatsListener( FrameTracker.this, mSurfaceControl); + markEvent("FT#deferMonitoring"); postTraceStartMarker(); } } @@ -241,8 +243,9 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener } if (mSurfaceControl != null) { if (mDeferMonitoring) { + markEvent("FT#deferMonitoring"); // Normal case, we begin the instrument from the very beginning, - // except the first frame. + // will exclude the first frame. postTraceStartMarker(); } else { // If we don't begin the instrument from the very beginning, @@ -272,6 +275,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener return; } mTracingStarted = true; + markEvent("FT#begin"); Trace.beginAsyncSection(mSession.getName(), (int) mBeginVsyncId); } } @@ -295,6 +299,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener Log.d(TAG, "end: " + mSession.getName() + ", end=" + mEndVsyncId + ", reason=" + reason); } + markEvent("FT#end#" + reason); Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId); mSession.setReason(reason); @@ -322,6 +327,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener reason == REASON_CANCEL_NOT_BEGUN || reason == REASON_CANCEL_SAME_VSYNC; if (mCancelled || (mEndVsyncId != INVALID_ID && !cancelFromEnd)) return false; mCancelled = true; + markEvent("FT#cancel#" + reason); // We don't need to end the trace section if it never begun. if (mTracingStarted) { Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId); @@ -343,6 +349,11 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener } } + private void markEvent(String desc) { + Trace.beginSection(TextUtils.formatSimple("%s#%s", mSession.getName(), desc)); + Trace.endSection(); + } + private void notifyCujEvent(String action) { if (mListener == null) return; mListener.onCujEvents(mSession, action); diff --git a/core/java/com/android/internal/logging/InstanceId.java b/core/java/com/android/internal/logging/InstanceId.java deleted file mode 100644 index c90d851201a2..000000000000 --- a/core/java/com/android/internal/logging/InstanceId.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.logging; - -import static java.lang.Math.max; -import static java.lang.Math.min; - -import android.annotation.Nullable; -import android.os.Parcel; -import android.os.Parcelable; - -import com.android.internal.annotations.VisibleForTesting; - -/** - * An opaque identifier used to disambiguate which logs refer to a particular instance of some - * UI element. Useful when there might be multiple instances simultaneously active. - * Obtain from InstanceIdSequence. Clipped to range [0, INSTANCE_ID_MAX]. - */ -public final class InstanceId implements Parcelable { - // At most 20 bits: ~1m possibilities, ~0.5% probability of collision in 100 values - static final int INSTANCE_ID_MAX = 1 << 20; - - private final int mId; - InstanceId(int id) { - mId = min(max(0, id), INSTANCE_ID_MAX); - } - - private InstanceId(Parcel in) { - this(in.readInt()); - } - - @VisibleForTesting - public int getId() { - return mId; - } - - /** - * Create a fake instance ID for testing purposes. Not for production use. See also - * InstanceIdSequenceFake, which is a testing replacement for InstanceIdSequence. - * @param id The ID you want to assign. - * @return new InstanceId. - */ - @VisibleForTesting - public static InstanceId fakeInstanceId(int id) { - return new InstanceId(id); - } - - @Override - public int hashCode() { - return mId; - } - - @Override - public boolean equals(@Nullable Object obj) { - if (!(obj instanceof InstanceId)) { - return false; - } - return mId == ((InstanceId) obj).mId; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeInt(mId); - } - - public static final Parcelable.Creator<InstanceId> CREATOR = - new Parcelable.Creator<InstanceId>() { - @Override - public InstanceId createFromParcel(Parcel in) { - return new InstanceId(in); - } - - @Override - public InstanceId[] newArray(int size) { - return new InstanceId[size]; - } - }; - -} diff --git a/core/java/com/android/internal/logging/InstanceIdSequence.java b/core/java/com/android/internal/logging/InstanceIdSequence.java deleted file mode 100644 index 34643105b965..000000000000 --- a/core/java/com/android/internal/logging/InstanceIdSequence.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.logging; - -import static java.lang.Math.max; -import static java.lang.Math.min; - -import com.android.internal.annotations.VisibleForTesting; - -import java.security.SecureRandom; -import java.util.Random; - -/** - * Generates random InstanceIds in range [1, instanceIdMax] for passing to - * UiEventLogger.logWithInstanceId(). Holds a SecureRandom, which self-seeds on - * first use; try to give it a long lifetime. Safe for concurrent use. - */ -public class InstanceIdSequence { - protected final int mInstanceIdMax; - private final Random mRandom = new SecureRandom(); - - /** - * Constructs a sequence with identifiers [1, instanceIdMax]. Capped at INSTANCE_ID_MAX. - * @param instanceIdMax Limiting value of identifiers. Normally positive: otherwise you get - * an all-1 sequence. - */ - public InstanceIdSequence(int instanceIdMax) { - mInstanceIdMax = min(max(1, instanceIdMax), InstanceId.INSTANCE_ID_MAX); - } - - /** - * Gets the next instance from the sequence. Safe for concurrent use. - * @return new InstanceId - */ - public InstanceId newInstanceId() { - return newInstanceIdInternal(1 + mRandom.nextInt(mInstanceIdMax)); - } - - /** - * Factory function for instance IDs, used for testing. - * @param id - * @return new InstanceId(id) - */ - @VisibleForTesting - protected InstanceId newInstanceIdInternal(int id) { - return new InstanceId(id); - } -} diff --git a/core/java/com/android/internal/logging/UiEventLogger.java b/core/java/com/android/internal/logging/UiEventLogger.java deleted file mode 100644 index 5378b03fe1c5..000000000000 --- a/core/java/com/android/internal/logging/UiEventLogger.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.logging; - -import android.annotation.NonNull; -import android.annotation.Nullable; - -/** - * Logging interface for UI events. Normal implementation is UiEventLoggerImpl. - * For testing, use fake implementation UiEventLoggerFake. - * - * See go/sysui-event-logs and UiEventReported atom in atoms.proto. - */ -public interface UiEventLogger { - /** Put your Event IDs in enums that implement this interface, and document them using the - * UiEventEnum annotation. - * Event IDs must be globally unique. This will be enforced by tooling (forthcoming). - * OEMs should use event IDs above 100000 and below 1000000 (1 million). - */ - interface UiEventEnum { - - /** - * Tag used to request new UI Event IDs via presubmit analysis. - * - * <p>Use RESERVE_NEW_UI_EVENT_ID as the constructor parameter for a new {@link EventEnum} - * to signal the presubmit analyzer to reserve a new ID for the event. The new ID will be - * returned as a Gerrit presubmit finding. Do not submit {@code RESERVE_NEW_UI_EVENT_ID} as - * the constructor parameter for any event. - * - * <pre> - * @UiEvent(doc = "Briefly describe the interaction when this event will be logged") - * UNIQUE_EVENT_NAME(RESERVE_NEW_UI_EVENT_ID); - * </pre> - */ - int RESERVE_NEW_UI_EVENT_ID = Integer.MIN_VALUE; // Negative IDs are ignored by the logger. - - int getId(); - } - - /** - * Log a simple event, with no package information. Does nothing if event.getId() <= 0. - * @param event an enum implementing UiEventEnum interface. - */ - void log(@NonNull UiEventEnum event); - - /** - * Log a simple event with an instance id, without package information. - * Does nothing if event.getId() <= 0. - * @param event an enum implementing UiEventEnum interface. - * @param instance An identifier obtained from an InstanceIdSequence. If null, reduces to log(). - */ - void log(@NonNull UiEventEnum event, @Nullable InstanceId instance); - - /** - * Log an event with package information. Does nothing if event.getId() <= 0. - * Give both uid and packageName if both are known, but one may be omitted if unknown. - * @param event an enum implementing UiEventEnum interface. - * @param uid the uid of the relevant app, if known (0 otherwise). - * @param packageName the package name of the relevant app, if known (null otherwise). - */ - void log(@NonNull UiEventEnum event, int uid, @Nullable String packageName); - - /** - * Log an event with package information and an instance ID. - * Does nothing if event.getId() <= 0. - * @param event an enum implementing UiEventEnum interface. - * @param uid the uid of the relevant app, if known (0 otherwise). - * @param packageName the package name of the relevant app, if known (null otherwise). - * @param instance An identifier obtained from an InstanceIdSequence. If null, reduces to log(). - */ - void logWithInstanceId(@NonNull UiEventEnum event, int uid, @Nullable String packageName, - @Nullable InstanceId instance); - - /** - * Log an event with ranked-choice information along with package. - * Does nothing if event.getId() <= 0. - * @param event an enum implementing UiEventEnum interface. - * @param uid the uid of the relevant app, if known (0 otherwise). - * @param packageName the package name of the relevant app, if known (null otherwise). - * @param position the position picked. - */ - void logWithPosition(@NonNull UiEventEnum event, int uid, @Nullable String packageName, - int position); - - /** - * Log an event with ranked-choice information along with package and instance ID. - * Does nothing if event.getId() <= 0. - * @param event an enum implementing UiEventEnum interface. - * @param uid the uid of the relevant app, if known (0 otherwise). - * @param packageName the package name of the relevant app, if known (null otherwise). - * @param instance An identifier obtained from an InstanceIdSequence. If null, reduces to - * logWithPosition(). - * @param position the position picked. - */ - void logWithInstanceIdAndPosition(@NonNull UiEventEnum event, int uid, - @Nullable String packageName, @Nullable InstanceId instance, int position); -} diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 3b46f627265b..8901c0774783 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -4763,8 +4763,11 @@ public class BatteryStatsImpl extends BatteryStats { if (Process.isSdkSandboxUid(uid)) { return Process.getAppUidForSdkSandboxUid(uid); } - int isolated = mIsolatedUids.get(uid, -1); - return isolated > 0 ? isolated : uid; + return mapIsolatedUid(uid); + } + + private int mapIsolatedUid(int uid) { + return mIsolatedUids.get(/*key=*/uid, /*valueIfKeyNotFound=*/uid); } @GuardedBy("this") @@ -5215,7 +5218,7 @@ public class BatteryStatsImpl extends BatteryStats { FrameworkStatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE); } else { FrameworkStatsLog.write_non_chained(FrameworkStatsLog.WAKELOCK_STATE_CHANGED, - mappedUid, null, getPowerManagerWakeLockLevel(type), name, + mapIsolatedUid(uid), null, getPowerManagerWakeLockLevel(type), name, FrameworkStatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE); } } @@ -5269,7 +5272,7 @@ public class BatteryStatsImpl extends BatteryStats { FrameworkStatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE); } else { FrameworkStatsLog.write_non_chained(FrameworkStatsLog.WAKELOCK_STATE_CHANGED, - mappedUid, null, getPowerManagerWakeLockLevel(type), name, + mapIsolatedUid(uid), null, getPowerManagerWakeLockLevel(type), name, FrameworkStatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE); } @@ -5693,7 +5696,10 @@ public class BatteryStatsImpl extends BatteryStats { @GuardedBy("this") private void noteStartGpsLocked(int uid, WorkChain workChain, long elapsedRealtimeMs, long uptimeMs) { - uid = getAttributionUid(uid, workChain); + if (workChain != null) { + uid = workChain.getAttributionUid(); + } + final int mappedUid = mapUid(uid); if (mGpsNesting == 0) { mHistoryCur.states |= HistoryItem.STATE_GPS_ON_FLAG; if (DEBUG_HISTORY) Slog.v(TAG, "Start GPS to: " @@ -5703,21 +5709,24 @@ public class BatteryStatsImpl extends BatteryStats { mGpsNesting++; if (workChain == null) { - FrameworkStatsLog.write_non_chained(FrameworkStatsLog.GPS_SCAN_STATE_CHANGED, uid, null, - FrameworkStatsLog.GPS_SCAN_STATE_CHANGED__STATE__ON); + FrameworkStatsLog.write_non_chained(FrameworkStatsLog.GPS_SCAN_STATE_CHANGED, + mapIsolatedUid(uid), null, FrameworkStatsLog.GPS_SCAN_STATE_CHANGED__STATE__ON); } else { FrameworkStatsLog.write(FrameworkStatsLog.GPS_SCAN_STATE_CHANGED, workChain.getUids(), workChain.getTags(), FrameworkStatsLog.GPS_SCAN_STATE_CHANGED__STATE__ON); } - getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs).noteStartGps(elapsedRealtimeMs); + getUidStatsLocked(mappedUid, elapsedRealtimeMs, uptimeMs).noteStartGps(elapsedRealtimeMs); } @GuardedBy("this") private void noteStopGpsLocked(int uid, WorkChain workChain, long elapsedRealtimeMs, long uptimeMs) { - uid = getAttributionUid(uid, workChain); + if (workChain != null) { + uid = workChain.getAttributionUid(); + } + final int mappedUid = mapUid(uid); mGpsNesting--; if (mGpsNesting == 0) { mHistoryCur.states &= ~HistoryItem.STATE_GPS_ON_FLAG; @@ -5729,14 +5738,15 @@ public class BatteryStatsImpl extends BatteryStats { } if (workChain == null) { - FrameworkStatsLog.write_non_chained(FrameworkStatsLog.GPS_SCAN_STATE_CHANGED, uid, null, + FrameworkStatsLog.write_non_chained(FrameworkStatsLog.GPS_SCAN_STATE_CHANGED, + mapIsolatedUid(uid), null, FrameworkStatsLog.GPS_SCAN_STATE_CHANGED__STATE__OFF); } else { FrameworkStatsLog.write(FrameworkStatsLog.GPS_SCAN_STATE_CHANGED, workChain.getUids(), workChain.getTags(), FrameworkStatsLog.GPS_SCAN_STATE_CHANGED__STATE__OFF); } - getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs).noteStopGps(elapsedRealtimeMs); + getUidStatsLocked(mappedUid, elapsedRealtimeMs, uptimeMs).noteStopGps(elapsedRealtimeMs); } @GuardedBy("this") @@ -7154,7 +7164,10 @@ public class BatteryStatsImpl extends BatteryStats { @GuardedBy("this") private void noteBluetoothScanStartedLocked(WorkChain workChain, int uid, boolean isUnoptimized, long elapsedRealtimeMs, long uptimeMs) { - uid = getAttributionUid(uid, workChain); + if (workChain != null) { + uid = workChain.getAttributionUid(); + } + uid = mapUid(uid); if (mBluetoothScanNesting == 0) { mHistoryCur.states2 |= HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG; if (DEBUG_HISTORY) Slog.v(TAG, "BLE scan started for: " @@ -7194,7 +7207,10 @@ public class BatteryStatsImpl extends BatteryStats { @GuardedBy("this") private void noteBluetoothScanStoppedLocked(WorkChain workChain, int uid, boolean isUnoptimized, long elapsedRealtimeMs, long uptimeMs) { - uid = getAttributionUid(uid, workChain); + if (workChain != null) { + uid = workChain.getAttributionUid(); + } + uid = mapUid(uid); mBluetoothScanNesting--; if (mBluetoothScanNesting == 0) { mHistoryCur.states2 &= ~HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG; @@ -7207,14 +7223,6 @@ public class BatteryStatsImpl extends BatteryStats { .noteBluetoothScanStoppedLocked(elapsedRealtimeMs, isUnoptimized); } - private int getAttributionUid(int uid, WorkChain workChain) { - if (workChain != null) { - return mapUid(workChain.getAttributionUid()); - } - - return mapUid(uid); - } - @GuardedBy("this") public void noteBluetoothScanStoppedFromSourceLocked(WorkSource ws, boolean isUnoptimized) { noteBluetoothScanStoppedFromSourceLocked(ws, isUnoptimized, diff --git a/core/java/com/android/internal/os/KernelAllocationStats.java b/core/java/com/android/internal/os/KernelAllocationStats.java index 1c3f8b0bf095..58d51e327ade 100644 --- a/core/java/com/android/internal/os/KernelAllocationStats.java +++ b/core/java/com/android/internal/os/KernelAllocationStats.java @@ -24,21 +24,29 @@ public final class KernelAllocationStats { /** Process dma-buf stats. */ public static final class ProcessDmabuf { + public final int uid; + public final String processName; + public final int oomScore; + /** Size of buffers retained by the process. */ public final int retainedSizeKb; /** Number of buffers retained by the process. */ public final int retainedBuffersCount; - /** Size of buffers mapped to the address space. */ - public final int mappedSizeKb; - /** Count of buffers mapped to the address space. */ - public final int mappedBuffersCount; + /** Size of buffers shared with Surface Flinger. */ + public final int surfaceFlingerSizeKb; + /** Count of buffers shared with Surface Flinger. */ + public final int surfaceFlingerCount; - ProcessDmabuf(int retainedSizeKb, int retainedBuffersCount, - int mappedSizeKb, int mappedBuffersCount) { + ProcessDmabuf(int uid, String processName, int oomScore, int retainedSizeKb, + int retainedBuffersCount, int surfaceFlingerSizeKb, + int surfaceFlingerCount) { + this.uid = uid; + this.processName = processName; + this.oomScore = oomScore; this.retainedSizeKb = retainedSizeKb; this.retainedBuffersCount = retainedBuffersCount; - this.mappedSizeKb = mappedSizeKb; - this.mappedBuffersCount = mappedBuffersCount; + this.surfaceFlingerSizeKb = surfaceFlingerSizeKb; + this.surfaceFlingerCount = surfaceFlingerCount; } } @@ -47,7 +55,7 @@ public final class KernelAllocationStats { * stats could not be read. */ @Nullable - public static native ProcessDmabuf getDmabufAllocations(int pid); + public static native ProcessDmabuf[] getDmabufAllocations(); /** Pid to gpu memory size. */ public static final class ProcessGpuMem { diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java index cc7e9d95b4f8..db41d333e1d4 100644 --- a/core/java/com/android/server/SystemConfig.java +++ b/core/java/com/android/server/SystemConfig.java @@ -31,9 +31,11 @@ import android.os.FileUtils; import android.os.Process; import android.os.SystemProperties; import android.os.Trace; +import android.os.VintfRuntimeInfo; import android.os.incremental.IncrementalManager; import android.os.storage.StorageManager; import android.permission.PermissionManager.SplitPermissionInfo; +import android.sysprop.ApexProperties; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; @@ -56,7 +58,10 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -1187,7 +1192,8 @@ public class SystemConfig { boolean systemExt = permFile.toPath().startsWith( Environment.getSystemExtDirectory().toPath() + "/"); boolean apex = permFile.toPath().startsWith( - Environment.getApexDirectory().toPath() + "/"); + Environment.getApexDirectory().toPath() + "/") + && ApexProperties.updatable().orElse(false); if (vendor) { readPrivAppPermissions(parser, mVendorPrivAppPermissions, mVendorPrivAppDenyPermissions); @@ -1445,6 +1451,14 @@ public class SystemConfig { addFeature(PackageManager.FEATURE_IPSEC_TUNNELS, 0); } + if (isFilesystemSupported("erofs")) { + if (isKernelVersionAtLeast(5, 10)) { + addFeature(PackageManager.FEATURE_EROFS, 0); + } else if (isKernelVersionAtLeast(4, 19)) { + addFeature(PackageManager.FEATURE_EROFS_LEGACY, 0); + } + } + for (String featureName : mUnavailableFeatures) { removeFeature(featureName); } @@ -1814,4 +1828,29 @@ public class SystemConfig { private static boolean isSystemProcess() { return Process.myUid() == Process.SYSTEM_UID; } + + private static boolean isFilesystemSupported(String fs) { + try { + final byte[] fsTableData = Files.readAllBytes(Paths.get("/proc/filesystems")); + final String fsTable = new String(fsTableData, StandardCharsets.UTF_8); + return fsTable.contains("\t" + fs + "\n"); + } catch (Exception e) { + return false; + } + } + + private static boolean isKernelVersionAtLeast(int major, int minor) { + final String kernelVersion = VintfRuntimeInfo.getKernelVersion(); + final String[] parts = kernelVersion.split("\\."); + if (parts.length < 2) { + return false; + } + try { + final int majorVersion = Integer.parseInt(parts[0]); + final int minorVersion = Integer.parseInt(parts[1]); + return majorVersion > major || (majorVersion == major && minorVersion >= minor); + } catch (NumberFormatException e) { + return false; + } + } } diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 3a76745cc4d9..a1be88440c97 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -272,6 +272,7 @@ cc_library_shared { "libinput", "libcamera_client", "libcamera_metadata", + "libprocinfo", "libsqlite", "libEGL", "libGLESv1_CM", diff --git a/core/jni/android_window_WindowInfosListener.cpp b/core/jni/android_window_WindowInfosListener.cpp index 08c9f20b0815..aae2549df429 100644 --- a/core/jni/android_window_WindowInfosListener.cpp +++ b/core/jni/android_window_WindowInfosListener.cpp @@ -44,6 +44,11 @@ static struct { jmethodID ctor; } gDisplayInfoClassInfo; +static struct { + jclass clazz; + jmethodID ctor; +} gPairClassInfo; + static jclass gInputWindowHandleClass; jobject fromDisplayInfo(JNIEnv* env, gui::DisplayInfo displayInfo) { @@ -57,6 +62,30 @@ jobject fromDisplayInfo(JNIEnv* env, gui::DisplayInfo displayInfo) { displayInfo.logicalHeight, matrixObj.get()); } +static jobjectArray fromWindowInfos(JNIEnv* env, const std::vector<WindowInfo>& windowInfos) { + jobjectArray jWindowHandlesArray = + env->NewObjectArray(windowInfos.size(), gInputWindowHandleClass, nullptr); + for (int i = 0; i < windowInfos.size(); i++) { + ScopedLocalRef<jobject> + jWindowHandle(env, + android_view_InputWindowHandle_fromWindowInfo(env, windowInfos[i])); + env->SetObjectArrayElement(jWindowHandlesArray, i, jWindowHandle.get()); + } + + return jWindowHandlesArray; +} + +static jobjectArray fromDisplayInfos(JNIEnv* env, const std::vector<DisplayInfo>& displayInfos) { + jobjectArray jDisplayInfoArray = + env->NewObjectArray(displayInfos.size(), gDisplayInfoClassInfo.clazz, nullptr); + for (int i = 0; i < displayInfos.size(); i++) { + ScopedLocalRef<jobject> jDisplayInfo(env, fromDisplayInfo(env, displayInfos[i])); + env->SetObjectArrayElement(jDisplayInfoArray, i, jDisplayInfo.get()); + } + + return jDisplayInfoArray; +} + struct WindowInfosListener : public gui::WindowInfosListener { WindowInfosListener(JNIEnv* env, jobject listener) : mListener(env->NewWeakGlobalRef(listener)) {} @@ -72,26 +101,8 @@ struct WindowInfosListener : public gui::WindowInfosListener { return; } - ScopedLocalRef<jobjectArray> - jWindowHandlesArray(env, - env->NewObjectArray(windowInfos.size(), gInputWindowHandleClass, - nullptr)); - for (int i = 0; i < windowInfos.size(); i++) { - ScopedLocalRef<jobject> - jWindowHandle(env, - android_view_InputWindowHandle_fromWindowInfo(env, - windowInfos[i])); - env->SetObjectArrayElement(jWindowHandlesArray.get(), i, jWindowHandle.get()); - } - - ScopedLocalRef<jobjectArray> - jDisplayInfoArray(env, - env->NewObjectArray(displayInfos.size(), - gDisplayInfoClassInfo.clazz, nullptr)); - for (int i = 0; i < displayInfos.size(); i++) { - ScopedLocalRef<jobject> jDisplayInfo(env, fromDisplayInfo(env, displayInfos[i])); - env->SetObjectArrayElement(jDisplayInfoArray.get(), i, jDisplayInfo.get()); - } + ScopedLocalRef<jobjectArray> jWindowHandlesArray(env, fromWindowInfos(env, windowInfos)); + ScopedLocalRef<jobjectArray> jDisplayInfoArray(env, fromDisplayInfos(env, displayInfos)); env->CallVoidMethod(listener, gListenerClassInfo.onWindowInfosChanged, jWindowHandlesArray.get(), jDisplayInfoArray.get()); @@ -124,9 +135,16 @@ void destroyNativeService(void* ptr) { listener->decStrong((void*)nativeCreate); } -void nativeRegister(JNIEnv* env, jclass clazz, jlong ptr) { +jobject nativeRegister(JNIEnv* env, jclass clazz, jlong ptr) { sp<WindowInfosListener> listener = reinterpret_cast<WindowInfosListener*>(ptr); - SurfaceComposerClient::getDefault()->addWindowInfosListener(listener); + std::pair<std::vector<gui::WindowInfo>, std::vector<gui::DisplayInfo>> initialInfo; + SurfaceComposerClient::getDefault()->addWindowInfosListener(listener, &initialInfo); + + ScopedLocalRef<jobjectArray> jWindowHandlesArray(env, fromWindowInfos(env, initialInfo.first)); + ScopedLocalRef<jobjectArray> jDisplayInfoArray(env, fromDisplayInfos(env, initialInfo.second)); + + return env->NewObject(gPairClassInfo.clazz, gPairClassInfo.ctor, jWindowHandlesArray.get(), + jDisplayInfoArray.get()); } void nativeUnregister(JNIEnv* env, jclass clazz, jlong ptr) { @@ -141,7 +159,7 @@ static jlong nativeGetFinalizer(JNIEnv* /* env */, jclass /* clazz */) { const JNINativeMethod gMethods[] = { /* name, signature, funcPtr */ {"nativeCreate", "(Landroid/window/WindowInfosListener;)J", (void*)nativeCreate}, - {"nativeRegister", "(J)V", (void*)nativeRegister}, + {"nativeRegister", "(J)Landroid/util/Pair;", (void*)nativeRegister}, {"nativeUnregister", "(J)V", (void*)nativeUnregister}, {"nativeGetFinalizer", "()J", (void*)nativeGetFinalizer}}; @@ -166,6 +184,12 @@ int register_android_window_WindowInfosListener(JNIEnv* env) { gDisplayInfoClassInfo.clazz = MakeGlobalRefOrDie(env, clazz); gDisplayInfoClassInfo.ctor = env->GetMethodID(gDisplayInfoClassInfo.clazz, "<init>", "(IIILandroid/graphics/Matrix;)V"); + + clazz = env->FindClass("android/util/Pair"); + gPairClassInfo.clazz = MakeGlobalRefOrDie(env, clazz); + gPairClassInfo.ctor = env->GetMethodID(gPairClassInfo.clazz, "<init>", + "(Ljava/lang/Object;Ljava/lang/Object;)V"); + return 0; } diff --git a/core/jni/com_android_internal_os_KernelAllocationStats.cpp b/core/jni/com_android_internal_os_KernelAllocationStats.cpp index e0a24430e739..5b104977f1d8 100644 --- a/core/jni/com_android_internal_os_KernelAllocationStats.cpp +++ b/core/jni/com_android_internal_os_KernelAllocationStats.cpp @@ -13,12 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include <android-base/logging.h> +#include <android-base/stringprintf.h> #include <dmabufinfo/dmabufinfo.h> #include <jni.h> #include <meminfo/sysmeminfo.h> +#include <procinfo/process.h> #include "core_jni_helpers.h" +using DmaBuffer = ::android::dmabufinfo::DmaBuffer; +using android::base::ReadFileToString; +using android::base::StringPrintf; + namespace { static jclass gProcessDmabufClazz; static jmethodID gProcessDmabufCtor; @@ -28,30 +35,127 @@ static jmethodID gProcessGpuMemCtor; namespace android { -static jobject KernelAllocationStats_getDmabufAllocations(JNIEnv *env, jobject, jint pid) { - std::vector<dmabufinfo::DmaBuffer> buffers; - if (!dmabufinfo::ReadDmaBufMapRefs(pid, &buffers)) { +struct PidDmaInfo { + uid_t uid; + std::string cmdline; + int oomScoreAdj; +}; + +static jobjectArray KernelAllocationStats_getDmabufAllocations(JNIEnv *env, jobject) { + std::vector<DmaBuffer> buffers; + + if (!dmabufinfo::ReadDmaBufs(&buffers)) { return nullptr; } - jint mappedSize = 0; - jint mappedCount = buffers.size(); - for (const auto &buffer : buffers) { - mappedSize += buffer.size(); + + // Create a reverse map from pid to dmabufs + // Store dmabuf inodes & sizes for later processing. + std::unordered_map<pid_t, std::set<ino_t>> pidToInodes; + std::unordered_map<ino_t, long> inodeToSize; + for (auto &buf : buffers) { + for (auto pid : buf.pids()) { + pidToInodes[pid].insert(buf.inode()); + } + inodeToSize[buf.inode()] = buf.size(); + } + + pid_t surfaceFlingerPid = -1; + // The set of all inodes that are being retained by SurfaceFlinger. Buffers + // shared between another process and SF will appear in this set. + std::set<ino_t> surfaceFlingerBufferInodes; + // The set of all inodes that are being retained by any process other + // than SurfaceFlinger. Buffers shared between another process and SF will + // appear in this set. + std::set<ino_t> otherProcessBufferInodes; + + // Find SurfaceFlinger pid & get cmdlines, oomScoreAdj, etc for each pid + // holding any DMA buffers. + std::unordered_map<pid_t, PidDmaInfo> pidDmaInfos; + for (const auto &pidToInodeEntry : pidToInodes) { + pid_t pid = pidToInodeEntry.first; + + android::procinfo::ProcessInfo processInfo; + if (!android::procinfo::GetProcessInfo(pid, &processInfo)) { + continue; + } + + std::string cmdline; + if (!ReadFileToString(StringPrintf("/proc/%d/cmdline", pid), &cmdline)) { + continue; + } + + // cmdline strings are null-delimited, so we split on \0 here + if (cmdline.substr(0, cmdline.find('\0')) == "/system/bin/surfaceflinger") { + if (surfaceFlingerPid == -1) { + surfaceFlingerPid = pid; + surfaceFlingerBufferInodes = pidToInodes[pid]; + } else { + LOG(ERROR) << "getDmabufAllocations found multiple SF processes; pid1: " << pid + << ", pid2:" << surfaceFlingerPid; + surfaceFlingerPid = -2; // Used as a sentinel value below + } + } else { + otherProcessBufferInodes.insert(pidToInodes[pid].begin(), pidToInodes[pid].end()); + } + + std::string oomScoreAdjStr; + if (!ReadFileToString(StringPrintf("/proc/%d/oom_score_adj", pid), &oomScoreAdjStr)) { + continue; + } + + pidDmaInfos[pid] = PidDmaInfo{.uid = processInfo.uid, + .cmdline = cmdline, + .oomScoreAdj = atoi(oomScoreAdjStr.c_str())}; + } + + if (surfaceFlingerPid < 0) { + LOG(ERROR) << "getDmabufAllocations could not identify SurfaceFlinger " + << "process via /proc/pid/cmdline"; } - mappedSize /= 1024; - - jint retainedSize = -1; - jint retainedCount = -1; - if (dmabufinfo::ReadDmaBufFdRefs(pid, &buffers)) { - retainedCount = buffers.size(); - retainedSize = 0; - for (const auto &buffer : buffers) { - retainedSize += buffer.size(); + + jobjectArray ret = env->NewObjectArray(pidDmaInfos.size(), gProcessDmabufClazz, NULL); + int retArrayIndex = 0; + for (const auto &pidDmaInfosEntry : pidDmaInfos) { + pid_t pid = pidDmaInfosEntry.first; + + // For all processes apart from SurfaceFlinger, this set will store the + // dmabuf inodes that are shared with SF. For SF, it will store the inodes + // that are shared with any other process. + std::set<ino_t> sharedBuffers; + if (pid == surfaceFlingerPid) { + set_intersection(surfaceFlingerBufferInodes.begin(), surfaceFlingerBufferInodes.end(), + otherProcessBufferInodes.begin(), otherProcessBufferInodes.end(), + std::inserter(sharedBuffers, sharedBuffers.end())); + } else if (surfaceFlingerPid > 0) { + set_intersection(pidToInodes[pid].begin(), pidToInodes[pid].end(), + surfaceFlingerBufferInodes.begin(), surfaceFlingerBufferInodes.end(), + std::inserter(sharedBuffers, sharedBuffers.begin())); + } // If surfaceFlingerPid < 0; it means we failed to identify it, and + // the SF-related fields below should be left empty. + + long totalSize = 0; + long sharedBuffersSize = 0; + for (const auto &inode : pidToInodes[pid]) { + totalSize += inodeToSize[inode]; + if (sharedBuffers.count(inode)) { + sharedBuffersSize += inodeToSize[inode]; + } } - retainedSize /= 1024; + + jobject obj = env->NewObject(gProcessDmabufClazz, gProcessDmabufCtor, + /* uid */ pidDmaInfos[pid].uid, + /* process name */ + env->NewStringUTF(pidDmaInfos[pid].cmdline.c_str()), + /* oomscore */ pidDmaInfos[pid].oomScoreAdj, + /* retainedSize */ totalSize / 1024, + /* retainedCount */ pidToInodes[pid].size(), + /* sharedWithSurfaceFlinger size */ sharedBuffersSize / 1024, + /* sharedWithSurfaceFlinger count */ sharedBuffers.size()); + + env->SetObjectArrayElement(ret, retArrayIndex++, obj); } - return env->NewObject(gProcessDmabufClazz, gProcessDmabufCtor, retainedSize, retainedCount, - mappedSize, mappedCount); + + return ret; } static jobject KernelAllocationStats_getGpuAllocations(JNIEnv *env) { @@ -74,7 +178,7 @@ static jobject KernelAllocationStats_getGpuAllocations(JNIEnv *env) { } static const JNINativeMethod methods[] = { - {"getDmabufAllocations", "(I)Lcom/android/internal/os/KernelAllocationStats$ProcessDmabuf;", + {"getDmabufAllocations", "()[Lcom/android/internal/os/KernelAllocationStats$ProcessDmabuf;", (void *)KernelAllocationStats_getDmabufAllocations}, {"getGpuAllocations", "()[Lcom/android/internal/os/KernelAllocationStats$ProcessGpuMem;", (void *)KernelAllocationStats_getGpuAllocations}, @@ -86,7 +190,8 @@ int register_com_android_internal_os_KernelAllocationStats(JNIEnv *env) { jclass clazz = FindClassOrDie(env, "com/android/internal/os/KernelAllocationStats$ProcessDmabuf"); gProcessDmabufClazz = MakeGlobalRefOrDie(env, clazz); - gProcessDmabufCtor = GetMethodIDOrDie(env, gProcessDmabufClazz, "<init>", "(IIII)V"); + gProcessDmabufCtor = + GetMethodIDOrDie(env, gProcessDmabufClazz, "<init>", "(ILjava/lang/String;IIIII)V"); clazz = FindClassOrDie(env, "com/android/internal/os/KernelAllocationStats$ProcessGpuMem"); gProcessGpuMemClazz = MakeGlobalRefOrDie(env, clazz); @@ -94,4 +199,4 @@ int register_com_android_internal_os_KernelAllocationStats(JNIEnv *env) { return res; } -} // namespace android +} // namespace android
\ No newline at end of file diff --git a/core/proto/android/os/appbatterystats.proto b/core/proto/android/os/appbatterystats.proto new file mode 100644 index 000000000000..8769ebb74979 --- /dev/null +++ b/core/proto/android/os/appbatterystats.proto @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; +package android.os; + +option java_multiple_files = true; + +message AppBatteryStatsProto { + message UidStats { + optional int32 uid = 1; + + message ProcessStateStats { + enum ProcessState { + UNSPECIFIED = 0; + FOREGROUND = 1; + BACKGROUND = 2; + FOREGROUND_SERVICE = 3; + CACHED = 4; + } + + optional ProcessState process_state = 1; + + // Time spent in this state in the past 24 hours + optional int64 duration_ms = 2; + // Estimated power consumed in this state in the past 24 hours + optional double power_mah = 3; + } + + repeated ProcessStateStats process_state_stats = 2; + } + + repeated UidStats uid_stats = 1; +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index d652b2ff4f4a..0c194db02128 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -528,6 +528,7 @@ <protected-broadcast android:name="android.intent.action.MANAGED_PROFILE_ADDED" /> <protected-broadcast android:name="android.intent.action.MANAGED_PROFILE_UNLOCKED" /> <protected-broadcast android:name="android.intent.action.MANAGED_PROFILE_REMOVED" /> + <protected-broadcast android:name="android.app.action.MANAGED_PROFILE_PROVISIONED" /> <protected-broadcast android:name="android.bluetooth.adapter.action.BLE_STATE_CHANGED" /> <protected-broadcast android:name="com.android.bluetooth.map.USER_CONFIRM_TIMEOUT" /> @@ -720,10 +721,12 @@ <!-- Added in T --> <protected-broadcast android:name="android.safetycenter.action.REFRESH_SAFETY_SOURCES" /> + <protected-broadcast android:name="android.safetycenter.action.SAFETY_CENTER_ENABLED_CHANGED" /> <protected-broadcast android:name="android.app.action.DEVICE_POLICY_RESOURCE_UPDATED" /> <protected-broadcast android:name="android.intent.action.SHOW_FOREGROUND_SERVICE_MANAGER" /> <protected-broadcast android:name="android.service.autofill.action.DELAYED_FILL" /> <protected-broadcast android:name="android.app.action.PROVISIONING_COMPLETED" /> + <protected-broadcast android:name="android.app.action.LOST_MODE_LOCATION_UPDATE" /> <!-- ====================================================================== --> <!-- RUNTIME PERMISSIONS --> @@ -6051,10 +6054,10 @@ <permission android:name="android.permission.MANAGE_APPOPS" android:protectionLevel="signature" /> - <!-- @hide Permission that allows background clipboard access. - <p>Not for use by third-party applications. --> + <!-- @SystemApi Permission that allows background clipboard access. + @hide Not for use by third-party applications. --> <permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND" - android:protectionLevel="signature" /> + android:protectionLevel="signature|role" /> <!-- @hide Permission that suppresses the notification when the clipboard is accessed. <p>Not for use by third-party applications. --> <permission android:name="android.permission.SUPPRESS_CLIPBOARD_ACCESS_NOTIFICATION" diff --git a/core/tests/coretests/src/android/net/SntpClientTest.java b/core/tests/coretests/src/android/net/SntpClientTest.java index ba7df1e218d9..267fc2b636d6 100644 --- a/core/tests/coretests/src/android/net/SntpClientTest.java +++ b/core/tests/coretests/src/android/net/SntpClientTest.java @@ -295,7 +295,8 @@ public class SntpClientTest { @Test public void testDnsResolutionFailure() throws Exception { - assertFalse(mClient.requestTime("ntp.server.doesnotexist.example", 5000, mNetwork)); + assertFalse(mClient.requestTime("ntp.server.doesnotexist.example", + SntpClient.STANDARD_NTP_PORT, 5000, mNetwork)); } @Test diff --git a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java index 1ae9649dfe06..e303934c4aad 100644 --- a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java +++ b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java @@ -60,7 +60,7 @@ import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) public class HandwritingInitiatorTest { private static final int TOUCH_SLOP = 8; - private static final long TAP_TIMEOUT = ViewConfiguration.getTapTimeout(); + private static final long TIMEOUT = ViewConfiguration.getLongPressTimeout(); private static final Rect sHwArea = new Rect(100, 200, 500, 500); private HandwritingInitiator mHandwritingInitiator; @@ -177,7 +177,7 @@ public class HandwritingInitiatorTest { } @Test - public void onTouchEvent_notStartHandwriting_when_stylusMove_afterTapTimeOut() { + public void onTouchEvent_notStartHandwriting_when_stylusMove_afterTimeOut() { mHandwritingInitiator.onInputConnectionCreated(mTestView); final int x1 = 10; final int y1 = 10; @@ -187,7 +187,7 @@ public class HandwritingInitiatorTest { final int x2 = x1 + TOUCH_SLOP * 2; final int y2 = y1; - final long time2 = time1 + TAP_TIMEOUT + 10L; + final long time2 = time1 + TIMEOUT + 10L; MotionEvent stylusEvent2 = createStylusEvent(ACTION_MOVE, x2, y2, time2); mHandwritingInitiator.onTouchEvent(stylusEvent2); diff --git a/core/tests/coretests/src/android/window/BackNavigationTest.java b/core/tests/coretests/src/android/window/BackNavigationTest.java index 8fa48ef5494d..94a149b09d54 100644 --- a/core/tests/coretests/src/android/window/BackNavigationTest.java +++ b/core/tests/coretests/src/android/window/BackNavigationTest.java @@ -111,12 +111,12 @@ public class BackNavigationTest { CountDownLatch backRegisteredLatch = new CountDownLatch(1); mScenario.onActivity(activity -> { activity.getOnBackInvokedDispatcher().registerOnBackInvokedCallback( - new OnBackInvokedCallback() { + 0, new OnBackInvokedCallback() { @Override public void onBackInvoked() { backInvokedLatch.countDown(); } - }, 0 + } ); backRegisteredLatch.countDown(); }); diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java index f8c994416f46..212f4ed92b8c 100644 --- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java +++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java @@ -77,9 +77,9 @@ public class WindowOnBackInvokedDispatcherTest { ArgumentCaptor.forClass(IOnBackInvokedCallback.class); mDispatcher.registerOnBackInvokedCallback( - mCallback1, OnBackInvokedDispatcher.PRIORITY_DEFAULT); + OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCallback1); mDispatcher.registerOnBackInvokedCallback( - mCallback2, OnBackInvokedDispatcher.PRIORITY_DEFAULT); + OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCallback2); verify(mWindowSession, times(2)).setOnBackInvokedCallback( Mockito.eq(mWindow), @@ -102,9 +102,9 @@ public class WindowOnBackInvokedDispatcherTest { ArgumentCaptor.forClass(IOnBackInvokedCallback.class); mDispatcher.registerOnBackInvokedCallback( - mCallback1, OnBackInvokedDispatcher.PRIORITY_OVERLAY); + OnBackInvokedDispatcher.PRIORITY_OVERLAY, mCallback1); mDispatcher.registerOnBackInvokedCallback( - mCallback2, OnBackInvokedDispatcher.PRIORITY_DEFAULT); + OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCallback2); verify(mWindowSession).setOnBackInvokedCallback( Mockito.eq(mWindow), captor.capture(), @@ -118,9 +118,9 @@ public class WindowOnBackInvokedDispatcherTest { @Test public void propagatesTopCallback_withRemoval() throws RemoteException { mDispatcher.registerOnBackInvokedCallback( - mCallback1, OnBackInvokedDispatcher.PRIORITY_DEFAULT); + OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCallback1); mDispatcher.registerOnBackInvokedCallback( - mCallback2, OnBackInvokedDispatcher.PRIORITY_DEFAULT); + OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCallback2); reset(mWindowSession); mDispatcher.unregisterOnBackInvokedCallback(mCallback1); @@ -139,16 +139,17 @@ public class WindowOnBackInvokedDispatcherTest { ArgumentCaptor<IOnBackInvokedCallback> captor = ArgumentCaptor.forClass(IOnBackInvokedCallback.class); - mDispatcher.registerOnBackInvokedCallback(mCallback1, - OnBackInvokedDispatcher.PRIORITY_OVERLAY); + mDispatcher.registerOnBackInvokedCallback(OnBackInvokedDispatcher.PRIORITY_OVERLAY, + mCallback1 + ); mDispatcher.registerOnBackInvokedCallback( - mCallback2, OnBackInvokedDispatcher.PRIORITY_DEFAULT); + OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCallback2); mDispatcher.registerOnBackInvokedCallback( - mCallback1, OnBackInvokedDispatcher.PRIORITY_DEFAULT); + OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCallback1); reset(mWindowSession); mDispatcher.registerOnBackInvokedCallback( - mCallback2, OnBackInvokedDispatcher.PRIORITY_OVERLAY); + OnBackInvokedDispatcher.PRIORITY_OVERLAY, mCallback2); verify(mWindowSession).setOnBackInvokedCallback( Mockito.eq(mWindow), captor.capture(), diff --git a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java index ec45a016507a..625f52a87ecf 100644 --- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java @@ -232,7 +232,7 @@ public final class LooperStatsTest { assertThat(entry3.handlerClassName).isEqualTo( "com.android.internal.os.LooperStatsTest$TestHandlerSecond"); assertThat(entry3.messageName).startsWith( - "com.android.internal.os.LooperStatsTest$$ExternalSyntheticLambda5"); + "com.android.internal.os.LooperStatsTest$$ExternalSyntheticLambda"); assertThat(entry3.messageCount).isEqualTo(1); assertThat(entry3.recordedMessageCount).isEqualTo(1); assertThat(entry3.exceptionCount).isEqualTo(0); diff --git a/core/tests/mockingcoretests/src/android/util/TimingsTraceLogTest.java b/core/tests/mockingcoretests/src/android/util/TimingsTraceLogTest.java index 5dc44d21c6b7..8de919681006 100644 --- a/core/tests/mockingcoretests/src/android/util/TimingsTraceLogTest.java +++ b/core/tests/mockingcoretests/src/android/util/TimingsTraceLogTest.java @@ -123,7 +123,7 @@ public class TimingsTraceLogTest { public void testLogDuration() throws Exception { TimingsTraceLog log = new TimingsTraceLog(TAG, TRACE_TAG_APP, 10); log.logDuration("logro", 42); - verify((MockedVoidMethod) () -> Slog.d(eq(TAG), contains("logro took to complete: 42ms"))); + verify((MockedVoidMethod) () -> Slog.v(eq(TAG), contains("logro took to complete: 42ms"))); } @Test @@ -134,7 +134,7 @@ public class TimingsTraceLogTest { verify((MockedVoidMethod) () -> Trace.traceBegin(TRACE_TAG_APP, "test")); verify((MockedVoidMethod) () -> Trace.traceEnd(TRACE_TAG_APP)); - verify((MockedVoidMethod) () -> Slog.d(eq(TAG), matches("test took to complete: \\dms"))); + verify((MockedVoidMethod) () -> Slog.v(eq(TAG), matches("test took to complete: \\dms"))); } @Test @@ -149,8 +149,8 @@ public class TimingsTraceLogTest { verify((MockedVoidMethod) () -> Trace.traceBegin(TRACE_TAG_APP, "L2")); verify((MockedVoidMethod) () -> Trace.traceEnd(TRACE_TAG_APP), times(2)); // L1 and L2 - verify((MockedVoidMethod) () -> Slog.d(eq(TAG), matches("L2 took to complete: \\d+ms"))); - verify((MockedVoidMethod) () -> Slog.d(eq(TAG), matches("L1 took to complete: \\d+ms"))); + verify((MockedVoidMethod) () -> Slog.v(eq(TAG), matches("L2 took to complete: \\d+ms"))); + verify((MockedVoidMethod) () -> Slog.v(eq(TAG), matches("L1 took to complete: \\d+ms"))); } @Test @@ -170,9 +170,9 @@ public class TimingsTraceLogTest { verify((MockedVoidMethod) () -> Trace.traceBegin(TRACE_TAG_APP, "L3")); verify((MockedVoidMethod) () -> Trace.traceEnd(TRACE_TAG_APP), times(3)); - verify((MockedVoidMethod) () -> Slog.d(eq(TAG), matches("L2 took to complete: \\d+ms"))); - verify((MockedVoidMethod) () -> Slog.d(eq(TAG), matches("L1 took to complete: \\d+ms"))); - verify((MockedVoidMethod) () -> Slog.d(eq(TAG), matches("L3 took to complete: \\d+ms")), + verify((MockedVoidMethod) () -> Slog.v(eq(TAG), matches("L2 took to complete: \\d+ms"))); + verify((MockedVoidMethod) () -> Slog.v(eq(TAG), matches("L1 took to complete: \\d+ms"))); + verify((MockedVoidMethod) () -> Slog.v(eq(TAG), matches("L3 took to complete: \\d+ms")), never()); verify((MockedVoidMethod) () -> Slog.w(TAG, "not tracing duration of 'L3' " diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml index d1873a0f40cf..2d1db716b700 100644 --- a/data/etc/com.android.systemui.xml +++ b/data/etc/com.android.systemui.xml @@ -76,5 +76,8 @@ <permission name="android.permission.FORCE_STOP_PACKAGES" /> <permission name="android.permission.ACCESS_FPS_COUNTER" /> <permission name="android.permission.CHANGE_CONFIGURATION" /> + <permission name="android.permission.LOG_COMPAT_CHANGE" /> + <permission name="android.permission.READ_COMPAT_CHANGE_CONFIG" /> + <permission name="android.permission.READ_DEVICE_CONFIG" /> </privapp-permissions> </permissions> diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index e898f570a6b8..4449c48d28bb 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -2257,6 +2257,12 @@ "group": "WM_DEBUG_WINDOW_TRANSITIONS", "at": "com\/android\/server\/wm\/TransitionController.java" }, + "264036181": { + "message": "Unable to retrieve task to start recording for display %d", + "level": "VERBOSE", + "group": "WM_DEBUG_CONTENT_RECORDING", + "at": "com\/android\/server\/wm\/ContentRecorder.java" + }, "269576220": { "message": "Resuming rotation after drag", "level": "DEBUG", @@ -2761,6 +2767,12 @@ "group": "WM_DEBUG_WALLPAPER", "at": "com\/android\/server\/wm\/WallpaperWindowToken.java" }, + "736003885": { + "message": "Unable to retrieve the task token to start recording for display %d", + "level": "VERBOSE", + "group": "WM_DEBUG_CONTENT_RECORDING", + "at": "com\/android\/server\/wm\/ContentRecorder.java" + }, "736692676": { "message": "Config is relaunching %s", "level": "VERBOSE", @@ -2791,6 +2803,12 @@ "group": "WM_DEBUG_RECENTS_ANIMATIONS", "at": "com\/android\/server\/wm\/RecentsAnimation.java" }, + "778774915": { + "message": "Unable to record task since feature is disabled %d", + "level": "VERBOSE", + "group": "WM_DEBUG_CONTENT_RECORDING", + "at": "com\/android\/server\/wm\/ContentRecorder.java" + }, "781471998": { "message": "moveWindowTokenToDisplay: Cannot move to the original display for token: %s", "level": "WARN", diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 61f7facf0916..a2f5301e353f 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -329,7 +329,7 @@ public class Typeface { FontFamily.Builder familyBuilder = null; for (final FontFileResourceEntry fontFile : filesEntry.getEntries()) { final Font.Builder fontBuilder = new Font.Builder(mgr, fontFile.getFileName(), - false /* isAsset */, 0 /* cookie */) + false /* isAsset */, AssetManager.COOKIE_UNKNOWN) .setTtcIndex(fontFile.getTtcIndex()) .setFontVariationSettings(fontFile.getVariationSettings()); if (fontFile.getWeight() != Typeface.RESOLVE_BY_FONT_TABLE) { diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java index cd7936d50dff..abd0be9c2872 100644 --- a/graphics/java/android/graphics/fonts/Font.java +++ b/graphics/java/android/graphics/fonts/Font.java @@ -179,7 +179,7 @@ public final class Font { */ public Builder(@NonNull AssetManager am, @NonNull String path) { try { - mBuffer = createBuffer(am, path, true /* is asset */, 0 /* cookie */); + mBuffer = createBuffer(am, path, true /* is asset */, AssetManager.COOKIE_UNKNOWN); } catch (IOException e) { mException = e; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index 58f79f3600de..13d12b3589c0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -1049,10 +1049,17 @@ public class BubbleStackView extends FrameLayout private final Runnable mAnimateTemporarilyInvisibleImmediate = () -> { if (mTemporarilyInvisible && mFlyout.getVisibility() != View.VISIBLE) { + // To calculate a distance, bubble stack needs to be moved to become hidden, + // we need to take into account that the bubble stack is positioned on the edge + // of the available screen rect, which can be offset by system bars and cutouts. if (mStackAnimationController.isStackOnLeftSide()) { - animate().translationX(-mBubbleSize).start(); + int availableRectOffsetX = + mPositioner.getAvailableRect().left - mPositioner.getScreenRect().left; + animate().translationX(-(mBubbleSize + availableRectOffsetX)).start(); } else { - animate().translationX(mBubbleSize).start(); + int availableRectOffsetX = + mPositioner.getAvailableRect().right - mPositioner.getScreenRect().right; + animate().translationX(mBubbleSize - availableRectOffsetX).start(); } } else { animate().translationX(0).start(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index be713a59a816..e5a755c35add 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -166,8 +166,7 @@ public class PipTransition extends PipTransitionController { mExitTransition = null; mHasFadeOut = false; if (mFinishCallback != null) { - mFinishCallback.onTransitionFinished(null, null); - mFinishCallback = null; + callFinishCallback(null /* wct */); mFinishTransaction = null; throw new RuntimeException("Previous callback not called, aborting exit PIP."); } @@ -232,6 +231,11 @@ public class PipTransition extends PipTransitionController { return false; } + /** Helper to identify whether this handler is currently the one playing an animation */ + private boolean isAnimatingLocally() { + return mFinishTransaction != null; + } + @Nullable @Override public WindowContainerTransaction handleRequest(@NonNull IBinder transition, @@ -282,9 +286,11 @@ public class PipTransition extends PipTransitionController { if (enteringPip) { mPipTransitionState.setTransitionState(ENTERED_PIP); } - // If there is an expected exit transition, then the exit will be "merged" into this - // transition so don't fire the finish-callback in that case. - if (mExitTransition == null && mFinishCallback != null) { + // If we have an exit transition, but aren't playing a transition locally, it + // means we're expecting the exit transition will be "merged" into another transition + // (likely a remote like launcher), so don't fire the finish-callback here -- wait until + // the exit transition is merged. + if ((mExitTransition == null || isAnimatingLocally()) && mFinishCallback != null) { WindowContainerTransaction wct = new WindowContainerTransaction(); prepareFinishResizeTransaction(taskInfo, destinationBounds, direction, wct); @@ -305,12 +311,19 @@ public class PipTransition extends PipTransitionController { mSurfaceTransactionHelper.crop(mFinishTransaction, leash, finishBounds); } mFinishTransaction = null; - mFinishCallback.onTransitionFinished(wct, null /* callback */); - mFinishCallback = null; + callFinishCallback(wct); } finishResizeForMenu(destinationBounds); } + private void callFinishCallback(WindowContainerTransaction wct) { + // Need to unset mFinishCallback first because onTransitionFinished can re-enter this + // handler if there is a pending PiP animation. + final Transitions.TransitionFinishCallback finishCallback = mFinishCallback; + mFinishCallback = null; + finishCallback.onTransitionFinished(wct, null /* callback */); + } + @Override public void forceFinishTransition() { if (mFinishCallback == null) return; @@ -572,8 +585,7 @@ public class PipTransition extends PipTransitionController { mHasFadeOut = false; if (mFinishCallback != null) { - mFinishCallback.onTransitionFinished(null /* wct */, null /* callback */); - mFinishCallback = null; + callFinishCallback(null /* wct */); mFinishTransaction = null; throw new RuntimeException("Previous callback not called, aborting entering PIP."); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 177b4a8b5982..2da5becae8f5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -18,6 +18,7 @@ package com.android.wm.shell.splitscreen; import static android.app.ActivityManager.START_SUCCESS; import static android.app.ActivityManager.START_TASK_TO_FRONT; +import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.RemoteAnimationTarget.MODE_OPENING; @@ -325,8 +326,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } } - public void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position, - @Nullable Bundle options) { + public void startIntent(PendingIntent intent, @Nullable Intent fillInIntent, + @SplitPosition int position, @Nullable Bundle options) { if (!ENABLE_SHELL_TRANSITIONS) { startIntentLegacy(intent, fillInIntent, position, options); return; @@ -335,6 +336,15 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, try { options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */); + + // Flag this as a no-user-action launch to prevent sending user leaving event to the + // current top activity since it's going to be put into another side of the split. This + // prevents the current top activity from going into pip mode due to user leaving event. + if (fillInIntent == null) { + fillInIntent = new Intent(); + } + fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION); + intent.send(mContext, 0, fillInIntent, null /* onFinished */, null /* handler */, null /* requiredPermission */, options); } catch (PendingIntent.CanceledException e) { @@ -342,7 +352,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } } - private void startIntentLegacy(PendingIntent intent, Intent fillInIntent, + private void startIntentLegacy(PendingIntent intent, @Nullable Intent fillInIntent, @SplitPosition int position, @Nullable Bundle options) { final WindowContainerTransaction evictWct = new WindowContainerTransaction(); mStageCoordinator.prepareEvictChildTasks(position, evictWct); @@ -393,6 +403,15 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, final WindowContainerTransaction wct = new WindowContainerTransaction(); options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, wct); + + // Flag this as a no-user-action launch to prevent sending user leaving event to the current + // top activity since it's going to be put into another side of the split. This prevents the + // current top activity from going into pip mode due to user leaving event. + if (fillInIntent == null) { + fillInIntent = new Intent(); + } + fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION); + wct.sendPendingIntent(intent, fillInIntent, options); mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct); } diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp index dd272cd5ff7d..c24cabb287de 100644 --- a/libs/hwui/HardwareBitmapUploader.cpp +++ b/libs/hwui/HardwareBitmapUploader.cpp @@ -310,6 +310,11 @@ bool HardwareBitmapUploader::has1010102Support() { return has101012Support; } +bool HardwareBitmapUploader::hasAlpha8Support() { + static bool hasAlpha8Support = checkSupport(AHARDWAREBUFFER_FORMAT_R8_UNORM); + return hasAlpha8Support; +} + static FormatInfo determineFormat(const SkBitmap& skBitmap, bool usingGL) { FormatInfo formatInfo; switch (skBitmap.info().colorType()) { @@ -363,6 +368,13 @@ static FormatInfo determineFormat(const SkBitmap& skBitmap, bool usingGL) { } formatInfo.format = GL_RGBA; break; + case kAlpha_8_SkColorType: + formatInfo.isSupported = HardwareBitmapUploader::hasAlpha8Support(); + formatInfo.bufferFormat = AHARDWAREBUFFER_FORMAT_R8_UNORM; + formatInfo.format = GL_R8; + formatInfo.type = GL_UNSIGNED_BYTE; + formatInfo.vkFormat = VK_FORMAT_R8_UNORM; + break; default: ALOGW("unable to create hardware bitmap of colortype: %d", skBitmap.info().colorType()); formatInfo.valid = false; diff --git a/libs/hwui/HardwareBitmapUploader.h b/libs/hwui/HardwareBitmapUploader.h index 34f43cd8a198..81057a24c29c 100644 --- a/libs/hwui/HardwareBitmapUploader.h +++ b/libs/hwui/HardwareBitmapUploader.h @@ -30,11 +30,13 @@ public: #ifdef __ANDROID__ static bool hasFP16Support(); static bool has1010102Support(); + static bool hasAlpha8Support(); #else static bool hasFP16Support() { return true; } static bool has1010102Support() { return true; } + static bool hasAlpha8Support() { return true; } #endif }; diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index 1a89cfd5d0ad..67f47580a70f 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -104,6 +104,10 @@ sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(size_t size, const SkImageInfo& info, sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(const SkBitmap& bitmap) { #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration + if (bitmap.colorType() == kAlpha_8_SkColorType && + !uirenderer::HardwareBitmapUploader::hasAlpha8Support()) { + return nullptr; + } return uirenderer::HardwareBitmapUploader::allocateHardwareBitmap(bitmap); #else return Bitmap::allocateHeapBitmap(bitmap.info()); diff --git a/location/java/android/location/GnssExcessPathInfo.java b/location/java/android/location/GnssExcessPathInfo.java new file mode 100644 index 000000000000..72b2374bad8f --- /dev/null +++ b/location/java/android/location/GnssExcessPathInfo.java @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.location; + +import static android.hardware.gnss.measurement_corrections.SingleSatCorrection.ExcessPathInfo.EXCESS_PATH_INFO_HAS_ATTENUATION; +import static android.hardware.gnss.measurement_corrections.SingleSatCorrection.ExcessPathInfo.EXCESS_PATH_INFO_HAS_EXCESS_PATH_LENGTH; +import static android.hardware.gnss.measurement_corrections.SingleSatCorrection.ExcessPathInfo.EXCESS_PATH_INFO_HAS_EXCESS_PATH_LENGTH_UNC; +import static android.hardware.gnss.measurement_corrections.SingleSatCorrection.ExcessPathInfo.EXCESS_PATH_INFO_HAS_REFLECTING_PLANE; + +import android.annotation.FloatRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.Preconditions; + +import java.util.Objects; + +/** + * Contains the info of an excess path signal caused by reflection + * + * @hide + */ +@SystemApi +public final class GnssExcessPathInfo implements Parcelable { + + private static final int HAS_EXCESS_PATH_LENGTH_MASK = EXCESS_PATH_INFO_HAS_EXCESS_PATH_LENGTH; + private static final int HAS_EXCESS_PATH_LENGTH_UNC_MASK = + EXCESS_PATH_INFO_HAS_EXCESS_PATH_LENGTH_UNC; + private static final int HAS_REFLECTING_PLANE_MASK = EXCESS_PATH_INFO_HAS_REFLECTING_PLANE; + private static final int HAS_ATTENUATION_MASK = EXCESS_PATH_INFO_HAS_ATTENUATION; + + /* A bitmask of fields present in this object (see HAS_* constants defined above) */ + private final int mFlags; + private final float mExcessPathLengthMeters; + private final float mExcessPathLengthUncertaintyMeters; + @Nullable + private final GnssReflectingPlane mReflectingPlane; + private final float mAttenuationDb; + + private GnssExcessPathInfo( + int flags, + float excessPathLengthMeters, + float excessPathLengthUncertaintyMeters, + @Nullable GnssReflectingPlane reflectingPlane, + float attenuationDb) { + mFlags = flags; + mExcessPathLengthMeters = excessPathLengthMeters; + mExcessPathLengthUncertaintyMeters = excessPathLengthUncertaintyMeters; + mReflectingPlane = reflectingPlane; + mAttenuationDb = attenuationDb; + } + + /** + * Gets a bitmask of fields present in this object. + * + * <p>This API exists for JNI since it is easier for JNI to get one integer flag than looking up + * several has* methods. + * @hide + */ + public int getFlags() { + return mFlags; + } + + /** Returns {@code true} if {@link #getExcessPathLengthMeters()} is valid. */ + public boolean hasExcessPathLength() { + return (mFlags & HAS_EXCESS_PATH_LENGTH_MASK) != 0; + } + + /** + * Returns the excess path length to be subtracted from pseudorange before using it in + * calculating location. + * + * <p>{@link #hasExcessPathLength()} must be true when calling this method. Otherwise, an + * {@link UnsupportedOperationException} will be thrown. + */ + @FloatRange(from = 0.0f) + public float getExcessPathLengthMeters() { + if (!hasExcessPathLength()) { + throw new UnsupportedOperationException( + "getExcessPathLengthMeters() is not supported when hasExcessPathLength() is " + + "false"); + } + return mExcessPathLengthMeters; + } + + /** Returns {@code true} if {@link #getExcessPathLengthUncertaintyMeters()} is valid. */ + public boolean hasExcessPathLengthUncertainty() { + return (mFlags & HAS_EXCESS_PATH_LENGTH_UNC_MASK) != 0; + } + + /** + * Returns the error estimate (1-sigma) for the excess path length estimate. + * + * <p>{@link #hasExcessPathLengthUncertainty()} must be true when calling this method. + * Otherwise, an {@link UnsupportedOperationException} will be thrown. + */ + @FloatRange(from = 0.0f) + public float getExcessPathLengthUncertaintyMeters() { + if (!hasExcessPathLengthUncertainty()) { + throw new UnsupportedOperationException( + "getExcessPathLengthUncertaintyMeters() is not supported when " + + "hasExcessPathLengthUncertainty() is false"); + } + return mExcessPathLengthUncertaintyMeters; + } + + /** + * Returns {@code true} if {@link #getReflectingPlane()} is valid. + * + * <p>Returns false if the satellite signal goes through multiple reflections or if reflection + * plane serving is not supported. + */ + public boolean hasReflectingPlane() { + return (mFlags & HAS_REFLECTING_PLANE_MASK) != 0; + } + + /** + * Returns the reflecting plane characteristics at which the signal has bounced. + * + * <p>{@link #hasReflectingPlane()} must be true when calling this method. Otherwise, an + * {@link UnsupportedOperationException} will be thrown. + */ + @NonNull + public GnssReflectingPlane getReflectingPlane() { + if (!hasReflectingPlane()) { + throw new UnsupportedOperationException( + "getReflectingPlane() is not supported when hasReflectingPlane() is false"); + } + return mReflectingPlane; + } + + /** Returns {@code true} if {@link #getAttenuationDb()} is valid. */ + public boolean hasAttenuation() { + return (mFlags & HAS_ATTENUATION_MASK) != 0; + } + + /** + * Returns the expected reduction of signal strength of this path in non-negative dB. + * + * <p>{@link #hasAttenuation()} must be true when calling this method. Otherwise, an + * {@link UnsupportedOperationException} will be thrown. + */ + @FloatRange(from = 0.0f) + public float getAttenuationDb() { + if (!hasAttenuation()) { + throw new UnsupportedOperationException( + "getAttenuationDb() is not supported when hasAttenuation() is false"); + } + return mAttenuationDb; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel parcel, int parcelFlags) { + parcel.writeInt(mFlags); + if (hasExcessPathLength()) { + parcel.writeFloat(mExcessPathLengthMeters); + } + if (hasExcessPathLengthUncertainty()) { + parcel.writeFloat(mExcessPathLengthUncertaintyMeters); + } + if (hasReflectingPlane()) { + mReflectingPlane.writeToParcel(parcel, parcelFlags); + } + if (hasAttenuation()) { + parcel.writeFloat(mAttenuationDb); + } + } + + public static final @NonNull Creator<GnssExcessPathInfo> CREATOR = + new Creator<GnssExcessPathInfo>() { + @Override + @NonNull + public GnssExcessPathInfo createFromParcel(@NonNull Parcel parcel) { + int flags = parcel.readInt(); + float excessPathLengthMeters = + (flags & HAS_EXCESS_PATH_LENGTH_MASK) != 0 + ? parcel.readFloat() : 0; + float excessPathLengthUncertaintyMeters = + (flags & HAS_EXCESS_PATH_LENGTH_UNC_MASK) != 0 + ? parcel.readFloat() : 0; + GnssReflectingPlane reflectingPlane = + (flags & HAS_REFLECTING_PLANE_MASK) != 0 + ? GnssReflectingPlane.CREATOR.createFromParcel(parcel) : null; + float attenuationDb = + (flags & HAS_ATTENUATION_MASK) != 0 + ? parcel.readFloat() : 0; + return new GnssExcessPathInfo(flags, excessPathLengthMeters, + excessPathLengthUncertaintyMeters, reflectingPlane, attenuationDb); + } + + @Override + public GnssExcessPathInfo[] newArray(int i) { + return new GnssExcessPathInfo[i]; + } + }; + + @Override + public boolean equals(Object obj) { + if (obj instanceof GnssExcessPathInfo) { + GnssExcessPathInfo that = (GnssExcessPathInfo) obj; + return this.mFlags == that.mFlags + && (!hasExcessPathLength() || Float.compare(this.mExcessPathLengthMeters, + that.mExcessPathLengthMeters) == 0) + && (!hasExcessPathLengthUncertainty() || Float.compare( + this.mExcessPathLengthUncertaintyMeters, + that.mExcessPathLengthUncertaintyMeters) == 0) + && (!hasReflectingPlane() || Objects.equals(this.mReflectingPlane, + that.mReflectingPlane)) + && (!hasAttenuation() || Float.compare(this.mAttenuationDb, + that.mAttenuationDb) == 0); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(mFlags, + mExcessPathLengthMeters, + mExcessPathLengthUncertaintyMeters, + mReflectingPlane, + mAttenuationDb); + } + + @NonNull + @Override + public String toString() { + StringBuilder builder = new StringBuilder("GnssExcessPathInfo["); + if (hasExcessPathLength()) { + builder.append(" ExcessPathLengthMeters=").append(mExcessPathLengthMeters); + } + if (hasExcessPathLengthUncertainty()) { + builder.append(" ExcessPathLengthUncertaintyMeters=").append( + mExcessPathLengthUncertaintyMeters); + } + if (hasReflectingPlane()) { + builder.append(" ReflectingPlane=").append(mReflectingPlane); + } + if (hasAttenuation()) { + builder.append(" AttenuationDb=").append(mAttenuationDb); + } + builder.append(']'); + return builder.toString(); + } + + /** Builder for {@link GnssExcessPathInfo}. */ + public static final class Builder { + private int mFlags; + private float mExcessPathLengthMeters; + private float mExcessPathLengthUncertaintyMeters; + @Nullable + private GnssReflectingPlane mReflectingPlane; + private float mAttenuationDb; + + /** Constructor for {@link Builder}. */ + public Builder() {} + + /** + * Sets the excess path length to be subtracted from pseudorange before using it in + * calculating location. + */ + @NonNull + public Builder setExcessPathLengthMeters( + @FloatRange(from = 0.0f) float excessPathLengthMeters) { + Preconditions.checkArgumentInRange(excessPathLengthMeters, 0, Float.MAX_VALUE, + "excessPathLengthMeters"); + mExcessPathLengthMeters = excessPathLengthMeters; + mFlags |= HAS_EXCESS_PATH_LENGTH_MASK; + return this; + } + + /** + * Clears the excess path length. + * + * <p>This is to negate {@link #setExcessPathLengthMeters} call. + */ + @NonNull + public Builder clearExcessPathLengthMeters() { + mExcessPathLengthMeters = 0; + mFlags &= ~HAS_EXCESS_PATH_LENGTH_MASK; + return this; + } + + /** Sets the error estimate (1-sigma) for the excess path length estimate */ + @NonNull + public Builder setExcessPathLengthUncertaintyMeters( + @FloatRange(from = 0.0f) float excessPathLengthUncertaintyMeters) { + Preconditions.checkArgumentInRange(excessPathLengthUncertaintyMeters, 0, + Float.MAX_VALUE, "excessPathLengthUncertaintyMeters"); + mExcessPathLengthUncertaintyMeters = excessPathLengthUncertaintyMeters; + mFlags |= HAS_EXCESS_PATH_LENGTH_UNC_MASK; + return this; + } + + /** + * Clears the error estimate (1-sigma) for the excess path length estimate + * + * <p>This is to negate {@link #setExcessPathLengthUncertaintyMeters} call. + */ + @NonNull + public Builder clearExcessPathLengthUncertaintyMeters() { + mExcessPathLengthUncertaintyMeters = 0; + mFlags &= ~HAS_EXCESS_PATH_LENGTH_UNC_MASK; + return this; + } + + /** Sets the reflecting plane information */ + @NonNull + public Builder setReflectingPlane(@Nullable GnssReflectingPlane reflectingPlane) { + mReflectingPlane = reflectingPlane; + if (reflectingPlane != null) { + mFlags |= HAS_REFLECTING_PLANE_MASK; + } else { + mFlags &= ~HAS_REFLECTING_PLANE_MASK; + } + return this; + } + + /** + * Sets the attenuation value in dB. + */ + @NonNull + public Builder setAttenuationDb(@FloatRange(from = 0.0f) float attenuationDb) { + Preconditions.checkArgumentInRange(attenuationDb, 0, Float.MAX_VALUE, + "attenuationDb"); + mAttenuationDb = attenuationDb; + mFlags |= HAS_ATTENUATION_MASK; + return this; + } + + /** + * Clears the attenuation value in dB. + * + * <p>This is to negate {@link #setAttenuationDb(float)} call. + */ + @NonNull + public Builder clearAttenuationDb() { + mAttenuationDb = 0; + mFlags &= ~HAS_ATTENUATION_MASK; + return this; + } + + /** Builds a {@link GnssExcessPathInfo} instance as specified by this builder. */ + @NonNull + public GnssExcessPathInfo build() { + return new GnssExcessPathInfo( + mFlags, + mExcessPathLengthMeters, + mExcessPathLengthUncertaintyMeters, + mReflectingPlane, + mAttenuationDb); + } + } +} diff --git a/location/java/android/location/GnssReflectingPlane.java b/location/java/android/location/GnssReflectingPlane.java index 1acdd1ecce0b..115cbec5e4de 100644 --- a/location/java/android/location/GnssReflectingPlane.java +++ b/location/java/android/location/GnssReflectingPlane.java @@ -22,9 +22,14 @@ import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; +import java.util.Objects; + /** * Holds the characteristics of the reflecting plane that a satellite signal has bounced from. * + * <p>Starting with Android T, this class supports {@link #equals} and {@link #hashCode}, which + * are not supported before that. + * * @hide */ @SystemApi @@ -107,24 +112,41 @@ public final class GnssReflectingPlane implements Parcelable { } }; + @Override + public void writeToParcel(@NonNull Parcel parcel, int flags) { + parcel.writeDouble(mLatitudeDegrees); + parcel.writeDouble(mLongitudeDegrees); + parcel.writeDouble(mAltitudeMeters); + parcel.writeDouble(mAzimuthDegrees); + } + @NonNull @Override public String toString() { - final String format = " %-29s = %s\n"; - StringBuilder builder = new StringBuilder("ReflectingPlane:\n"); - builder.append(String.format(format, "LatitudeDegrees = ", mLatitudeDegrees)); - builder.append(String.format(format, "LongitudeDegrees = ", mLongitudeDegrees)); - builder.append(String.format(format, "AltitudeMeters = ", mAltitudeMeters)); - builder.append(String.format(format, "AzimuthDegrees = ", mAzimuthDegrees)); + StringBuilder builder = new StringBuilder("ReflectingPlane["); + builder.append(" LatitudeDegrees=").append(mLatitudeDegrees); + builder.append(" LongitudeDegrees=").append(mLongitudeDegrees); + builder.append(" AltitudeMeters=").append(mAltitudeMeters); + builder.append(" AzimuthDegrees=").append(mAzimuthDegrees); + builder.append(']'); return builder.toString(); } @Override - public void writeToParcel(@NonNull Parcel parcel, int flags) { - parcel.writeDouble(mLatitudeDegrees); - parcel.writeDouble(mLongitudeDegrees); - parcel.writeDouble(mAltitudeMeters); - parcel.writeDouble(mAzimuthDegrees); + public boolean equals(Object obj) { + if (obj instanceof GnssReflectingPlane) { + GnssReflectingPlane that = (GnssReflectingPlane) obj; + return Double.compare(this.mLatitudeDegrees, that.mLatitudeDegrees) == 0 + && Double.compare(this.mLongitudeDegrees, that.mLongitudeDegrees) == 0 + && Double.compare(this.mAltitudeMeters, that.mAltitudeMeters) == 0 + && Double.compare(this.mAzimuthDegrees, that.mAzimuthDegrees) == 0; + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(mLatitudeDegrees, mLatitudeDegrees, mAltitudeMeters, mAzimuthDegrees); } /** Builder for {@link GnssReflectingPlane} */ diff --git a/location/java/android/location/GnssSingleSatCorrection.java b/location/java/android/location/GnssSingleSatCorrection.java index 262630b79cb0..a7fce0aaaf6c 100644 --- a/location/java/android/location/GnssSingleSatCorrection.java +++ b/location/java/android/location/GnssSingleSatCorrection.java @@ -16,6 +16,11 @@ package android.location; +import static android.hardware.gnss.measurement_corrections.SingleSatCorrection.SINGLE_SAT_CORRECTION_HAS_COMBINED_ATTENUATION; +import static android.hardware.gnss.measurement_corrections.SingleSatCorrection.SINGLE_SAT_CORRECTION_HAS_COMBINED_EXCESS_PATH_LENGTH; +import static android.hardware.gnss.measurement_corrections.SingleSatCorrection.SINGLE_SAT_CORRECTION_HAS_COMBINED_EXCESS_PATH_LENGTH_UNC; +import static android.hardware.gnss.measurement_corrections.SingleSatCorrection.SINGLE_SAT_CORRECTION_HAS_SAT_IS_LOS_PROBABILITY; + import android.annotation.FloatRange; import android.annotation.IntRange; import android.annotation.NonNull; @@ -26,6 +31,8 @@ import android.os.Parcelable; import com.android.internal.util.Preconditions; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; /** @@ -36,106 +43,47 @@ import java.util.Objects; @SystemApi public final class GnssSingleSatCorrection implements Parcelable { - /** - * Bit mask for {@link #mSingleSatCorrectionFlags} indicating the presence of {@link - * #mProbSatIsLos}. - * - * @hide - */ - public static final int HAS_PROB_SAT_IS_LOS_MASK = 1 << 0; - - /** - * Bit mask for {@link #mSingleSatCorrectionFlags} indicating the presence of {@link - * #mExcessPathLengthMeters}. - * - * @hide - */ - public static final int HAS_EXCESS_PATH_LENGTH_MASK = 1 << 1; - - /** - * Bit mask for {@link #mSingleSatCorrectionFlags} indicating the presence of {@link - * #mExcessPathLengthUncertaintyMeters}. - * - * @hide - */ - public static final int HAS_EXCESS_PATH_LENGTH_UNC_MASK = 1 << 2; - - /** - * Bit mask for {@link #mSingleSatCorrectionFlags} indicating the presence of {@link - * #mReflectingPlane}. - * - * @hide - */ - public static final int HAS_REFLECTING_PLANE_MASK = 1 << 3; + private static final int HAS_PROB_SAT_IS_LOS_MASK = + SINGLE_SAT_CORRECTION_HAS_SAT_IS_LOS_PROBABILITY; + private static final int HAS_COMBINED_EXCESS_PATH_LENGTH_MASK = + SINGLE_SAT_CORRECTION_HAS_COMBINED_EXCESS_PATH_LENGTH; + private static final int HAS_COMBINED_EXCESS_PATH_LENGTH_UNC_MASK = + SINGLE_SAT_CORRECTION_HAS_COMBINED_EXCESS_PATH_LENGTH_UNC; + private static final int HAS_COMBINED_ATTENUATION_MASK = + SINGLE_SAT_CORRECTION_HAS_COMBINED_ATTENUATION; - /** A bitmask of fields present in this object (see HAS_* constants defined above) */ + /* A bitmask of fields present in this object (see HAS_* constants defined above). */ private final int mSingleSatCorrectionFlags; - /** Defines the constellation of the given satellite as defined in {@link GnssStatus}. */ - @GnssStatus.ConstellationType private final int mConstellationType; - - /** - * Satellite vehicle ID number - * - * <p>Interpretation depends on {@link GnssStatus#getSvid(int)}. - */ - @IntRange(from = 0) private final int mSatId; - - /** - * Carrier frequency of the signal to be corrected, for example it can be the GPS center - * frequency for L1 = 1,575,420,000 Hz, varying GLO channels, etc. - * - * <p>For an L1, L5 receiver tracking a satellite on L1 and L5 at the same time, two correction - * objects will be reported for this same satellite, in one of the correction objects, all the - * values related to L1 will be filled, and in the other all of the values related to L5 will be - * filled. - */ - @FloatRange(from = 0.0f, fromInclusive = false) private final float mCarrierFrequencyHz; - - /** - * The probability that the satellite is estimated to be in Line-of-Sight condition at the given - * location. - */ - @FloatRange(from = 0.0f, to = 1.0f) private final float mProbSatIsLos; + private final float mCombinedExcessPathLengthMeters; + private final float mCombinedExcessPathLengthUncertaintyMeters; + private final float mCombinedAttenuationDb; - /** - * Excess path length to be subtracted from pseudorange before using it in calculating location. - */ - @FloatRange(from = 0.0f) - private final float mExcessPathLengthMeters; - - /** Error estimate (1-sigma) for the Excess path length estimate */ - @FloatRange(from = 0.0f) - private final float mExcessPathLengthUncertaintyMeters; - - /** - * Defines the reflecting plane location and azimuth information - * - * <p>The flag HAS_REFLECTING_PLANE will be used to set this value to invalid if the satellite - * signal goes through multiple reflections or if reflection plane serving is not supported. - */ - @Nullable - private final GnssReflectingPlane mReflectingPlane; + @NonNull + private final List<GnssExcessPathInfo> mGnssExcessPathInfoList; private GnssSingleSatCorrection(int singleSatCorrectionFlags, int constellationType, int satId, float carrierFrequencyHz, float probSatIsLos, float excessPathLengthMeters, - float excessPathLengthUncertaintyMeters, GnssReflectingPlane reflectingPlane) { + float excessPathLengthUncertaintyMeters, + float combinedAttenuationDb, + @NonNull List<GnssExcessPathInfo> gnssExcessPathInfoList) { mSingleSatCorrectionFlags = singleSatCorrectionFlags; mConstellationType = constellationType; mSatId = satId; mCarrierFrequencyHz = carrierFrequencyHz; mProbSatIsLos = probSatIsLos; - mExcessPathLengthMeters = excessPathLengthMeters; - mExcessPathLengthUncertaintyMeters = excessPathLengthUncertaintyMeters; - mReflectingPlane = reflectingPlane; + mCombinedExcessPathLengthMeters = excessPathLengthMeters; + mCombinedExcessPathLengthUncertaintyMeters = excessPathLengthUncertaintyMeters; + mCombinedAttenuationDb = combinedAttenuationDb; + mGnssExcessPathInfoList = gnssExcessPathInfoList; } /** - * Gets a bitmask of fields present in this object + * Gets a bitmask of fields present in this object. * * @hide */ @@ -193,29 +141,46 @@ public final class GnssSingleSatCorrection implements Parcelable { } /** - * Returns the Excess path length to be subtracted from pseudorange before using it in + * Returns the combined excess path length to be subtracted from pseudorange before using it in * calculating location. */ @FloatRange(from = 0.0f) public float getExcessPathLengthMeters() { - return mExcessPathLengthMeters; + return mCombinedExcessPathLengthMeters; } - /** Returns the error estimate (1-sigma) for the Excess path length estimate */ + /** Returns the error estimate (1-sigma) for the combined excess path length estimate. */ @FloatRange(from = 0.0f) public float getExcessPathLengthUncertaintyMeters() { - return mExcessPathLengthUncertaintyMeters; + return mCombinedExcessPathLengthUncertaintyMeters; } /** - * Returns the reflecting plane characteristics at which the signal has bounced + * Returns the combined expected reduction of signal strength for this satellite in + * non-negative dB. + */ + @FloatRange(from = 0.0f) + public float getCombinedAttenuationDb() { + return mCombinedAttenuationDb; + } + + /** + * Returns the reflecting plane characteristics at which the signal has bounced. * - * <p>The flag HAS_REFLECTING_PLANE will be used to set this value to invalid if the satellite - * signal goes through multiple reflections or if reflection plane serving is not supported + * @deprecated Combined excess path does not have a reflecting plane. */ @Nullable + @Deprecated public GnssReflectingPlane getReflectingPlane() { - return mReflectingPlane; + return null; + } + + /** + * Returns the list of {@link GnssExcessPathInfo} associated with this satellite signal. + */ + @NonNull + public List<GnssExcessPathInfo> getGnssExcessPathInfoList() { + return mGnssExcessPathInfoList; } /** Returns {@code true} if {@link #getProbabilityLineOfSight()} is valid. */ @@ -225,17 +190,27 @@ public final class GnssSingleSatCorrection implements Parcelable { /** Returns {@code true} if {@link #getExcessPathLengthMeters()} is valid. */ public boolean hasExcessPathLength() { - return (mSingleSatCorrectionFlags & HAS_EXCESS_PATH_LENGTH_MASK) != 0; + return (mSingleSatCorrectionFlags & HAS_COMBINED_EXCESS_PATH_LENGTH_MASK) != 0; } /** Returns {@code true} if {@link #getExcessPathLengthUncertaintyMeters()} is valid. */ public boolean hasExcessPathLengthUncertainty() { - return (mSingleSatCorrectionFlags & HAS_EXCESS_PATH_LENGTH_UNC_MASK) != 0; + return (mSingleSatCorrectionFlags & HAS_COMBINED_EXCESS_PATH_LENGTH_UNC_MASK) != 0; } - /** Returns {@code true} if {@link #getReflectingPlane()} is valid. */ + /** + * Returns {@code true} if {@link #getReflectingPlane()} is valid. + * + * @deprecated Combined excess path does not have a reflecting plane. + */ + @Deprecated public boolean hasReflectingPlane() { - return (mSingleSatCorrectionFlags & HAS_REFLECTING_PLANE_MASK) != 0; + return false; + } + + /** Returns {@code true} if {@link #getCombinedAttenuationDb()} is valid. */ + public boolean hasCombinedAttenuation() { + return (mSingleSatCorrectionFlags & HAS_COMBINED_ATTENUATION_MASK) != 0; } @Override @@ -253,14 +228,15 @@ public final class GnssSingleSatCorrection implements Parcelable { parcel.writeFloat(mProbSatIsLos); } if (hasExcessPathLength()) { - parcel.writeFloat(mExcessPathLengthMeters); + parcel.writeFloat(mCombinedExcessPathLengthMeters); } if (hasExcessPathLengthUncertainty()) { - parcel.writeFloat(mExcessPathLengthUncertaintyMeters); + parcel.writeFloat(mCombinedExcessPathLengthUncertaintyMeters); } - if (hasReflectingPlane()) { - mReflectingPlane.writeToParcel(parcel, flags); + if (hasCombinedAttenuation()) { + parcel.writeFloat(mCombinedAttenuationDb); } + parcel.writeTypedList(mGnssExcessPathInfoList); } public static final Creator<GnssSingleSatCorrection> CREATOR = @@ -274,18 +250,21 @@ public final class GnssSingleSatCorrection implements Parcelable { float carrierFrequencyHz = parcel.readFloat(); float probSatIsLos = (singleSatCorrectionFlags & HAS_PROB_SAT_IS_LOS_MASK) != 0 ? parcel.readFloat() : 0; - float excessPathLengthMeters = - (singleSatCorrectionFlags & HAS_EXCESS_PATH_LENGTH_MASK) != 0 + float combinedExcessPathLengthMeters = + (singleSatCorrectionFlags & HAS_COMBINED_EXCESS_PATH_LENGTH_MASK) != 0 ? parcel.readFloat() : 0; - float excessPathLengthUncertaintyMeters = - (singleSatCorrectionFlags & HAS_EXCESS_PATH_LENGTH_UNC_MASK) != 0 + float combinedExcessPathLengthUncertaintyMeters = + (singleSatCorrectionFlags & HAS_COMBINED_EXCESS_PATH_LENGTH_UNC_MASK) + != 0 ? parcel.readFloat() : 0; + float combinedAttenuationDb = + (singleSatCorrectionFlags & HAS_COMBINED_ATTENUATION_MASK) != 0 ? parcel.readFloat() : 0; - GnssReflectingPlane reflectingPlane = - (singleSatCorrectionFlags & HAS_REFLECTING_PLANE_MASK) != 0 - ? GnssReflectingPlane.CREATOR.createFromParcel(parcel) : null; + List<GnssExcessPathInfo> gnssExcessPathInfoList = parcel.createTypedArrayList( + GnssExcessPathInfo.CREATOR); return new GnssSingleSatCorrection(singleSatCorrectionFlags, constellationType, - satId, carrierFrequencyHz, probSatIsLos, excessPathLengthMeters, - excessPathLengthUncertaintyMeters, reflectingPlane); + satId, carrierFrequencyHz, probSatIsLos, combinedExcessPathLengthMeters, + combinedExcessPathLengthUncertaintyMeters, combinedAttenuationDb, + gnssExcessPathInfoList); } @Override @@ -296,56 +275,24 @@ public final class GnssSingleSatCorrection implements Parcelable { @Override public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof GnssSingleSatCorrection)) { - return false; - } - - GnssSingleSatCorrection other = (GnssSingleSatCorrection) obj; - if (mConstellationType != other.mConstellationType) { - return false; - } - if (mSatId != other.mSatId) { - return false; - } - if (Float.compare(mCarrierFrequencyHz, other.mCarrierFrequencyHz) != 0) { - return false; - } - - if (hasValidSatelliteLineOfSight() != other.hasValidSatelliteLineOfSight()) { - return false; - } - if (hasValidSatelliteLineOfSight() - && Float.compare(mProbSatIsLos, other.mProbSatIsLos) != 0) { - return false; - } - - if (hasExcessPathLength() != other.hasExcessPathLength()) { - return false; - } - if (hasExcessPathLength() - && Float.compare(mExcessPathLengthMeters, other.mExcessPathLengthMeters) != 0) { - return false; - } - - if (hasExcessPathLengthUncertainty() != other.hasExcessPathLengthUncertainty()) { - return false; - } - if (hasExcessPathLengthUncertainty() && Float.compare(mExcessPathLengthUncertaintyMeters, - other.mExcessPathLengthUncertaintyMeters) != 0) { - return false; - } - - if (hasReflectingPlane() != other.hasReflectingPlane()) { - return false; - } - if (hasReflectingPlane() - && !mReflectingPlane.equals(other.mReflectingPlane)) { - return false; - } - return true; + if (obj instanceof GnssSingleSatCorrection) { + GnssSingleSatCorrection that = (GnssSingleSatCorrection) obj; + return this.mSingleSatCorrectionFlags == that.mSingleSatCorrectionFlags + && this.mConstellationType == that.mConstellationType + && this.mSatId == that.mSatId + && Float.compare(mCarrierFrequencyHz, that.mCarrierFrequencyHz) == 0 + && (!hasValidSatelliteLineOfSight() || Float.compare(mProbSatIsLos, + that.mProbSatIsLos) == 0) + && (!hasExcessPathLength() || Float.compare(mCombinedExcessPathLengthMeters, + that.mCombinedExcessPathLengthMeters) == 0) + && (!hasExcessPathLengthUncertainty() || Float.compare( + mCombinedExcessPathLengthUncertaintyMeters, + that.mCombinedExcessPathLengthUncertaintyMeters) == 0) + && (!hasCombinedAttenuation() || Float.compare(mCombinedAttenuationDb, + that.mCombinedAttenuationDb) == 0) + && mGnssExcessPathInfoList.equals(that.mGnssExcessPathInfoList); + } + return false; } @Override @@ -355,9 +302,10 @@ public final class GnssSingleSatCorrection implements Parcelable { mSatId, mCarrierFrequencyHz, mProbSatIsLos, - mExcessPathLengthMeters, - mExcessPathLengthUncertaintyMeters, - mReflectingPlane); + mCombinedExcessPathLengthMeters, + mCombinedExcessPathLengthUncertaintyMeters, + mCombinedAttenuationDb, + mGnssExcessPathInfoList); } @NonNull @@ -371,14 +319,19 @@ public final class GnssSingleSatCorrection implements Parcelable { builder.append(" ProbSatIsLos=").append(mProbSatIsLos); } if (hasExcessPathLength()) { - builder.append(" ExcessPathLengthMeters=").append(mExcessPathLengthMeters); + builder.append(" CombinedExcessPathLengthMeters=").append( + mCombinedExcessPathLengthMeters); } if (hasExcessPathLengthUncertainty()) { - builder.append(" ExcessPathLengthUncertaintyMeters=").append( - mExcessPathLengthUncertaintyMeters); + builder.append(" CombinedExcessPathLengthUncertaintyMeters=").append( + mCombinedExcessPathLengthUncertaintyMeters); } - if (hasReflectingPlane()) { - builder.append(" ReflectingPlane=").append(mReflectingPlane); + if (hasCombinedAttenuation()) { + builder.append(" CombinedAttenuationDb=").append( + mCombinedAttenuationDb); + } + if (!mGnssExcessPathInfoList.isEmpty()) { + builder.append(' ').append(mGnssExcessPathInfoList.toString()); } builder.append(']'); return builder.toString(); @@ -386,21 +339,16 @@ public final class GnssSingleSatCorrection implements Parcelable { /** Builder for {@link GnssSingleSatCorrection} */ public static final class Builder { - - /** - * For documentation of below fields, see corresponding fields in {@link - * GnssSingleSatCorrection}. - */ private int mSingleSatCorrectionFlags; - private int mConstellationType; private int mSatId; private float mCarrierFrequencyHz; private float mProbSatIsLos; - private float mExcessPathLengthMeters; - private float mExcessPathLengthUncertaintyMeters; - @Nullable - private GnssReflectingPlane mReflectingPlane; + private float mCombinedExcessPathLengthMeters; + private float mCombinedExcessPathLengthUncertaintyMeters; + private float mCombinedAttenuationDb; + @NonNull + private List<GnssExcessPathInfo> mGnssExcessInfoList = new ArrayList<>(); /** Sets the constellation type. */ @NonNull public Builder setConstellationType( @@ -409,18 +357,18 @@ public final class GnssSingleSatCorrection implements Parcelable { return this; } - /** Sets the Satellite ID defined in the ICD of the given constellation. */ + /** Sets the satellite ID defined in the ICD of the given constellation. */ @NonNull public Builder setSatelliteId(@IntRange(from = 0) int satId) { Preconditions.checkArgumentNonnegative(satId, "satId should be non-negative."); mSatId = satId; return this; } - /** Sets the Carrier frequency in Hz. */ + /** Sets the carrier frequency in Hz. */ @NonNull public Builder setCarrierFrequencyHz( @FloatRange(from = 0.0f, fromInclusive = false) float carrierFrequencyHz) { - Preconditions.checkArgument( - carrierFrequencyHz >= 0, "carrierFrequencyHz should be non-negative."); + Preconditions.checkArgumentInRange( + carrierFrequencyHz, 0, Float.MAX_VALUE, "carrierFrequencyHz"); mCarrierFrequencyHz = carrierFrequencyHz; return this; } @@ -450,58 +398,90 @@ public final class GnssSingleSatCorrection implements Parcelable { } /** - * Sets the Excess path length to be subtracted from pseudorange before using it in + * Sets the combined excess path length to be subtracted from pseudorange before using it in * calculating location. */ - @NonNull public Builder setExcessPathLengthMeters( - @FloatRange(from = 0.0f) float excessPathLengthMeters) { - Preconditions.checkArgument(excessPathLengthMeters >= 0, - "excessPathLengthMeters should be non-negative."); - mExcessPathLengthMeters = excessPathLengthMeters; - mSingleSatCorrectionFlags |= HAS_EXCESS_PATH_LENGTH_MASK; + @NonNull + public Builder setExcessPathLengthMeters( + @FloatRange(from = 0.0f) float combinedExcessPathLengthMeters) { + Preconditions.checkArgumentInRange(combinedExcessPathLengthMeters, 0, Float.MAX_VALUE, + "excessPathLengthMeters"); + mCombinedExcessPathLengthMeters = combinedExcessPathLengthMeters; + mSingleSatCorrectionFlags |= HAS_COMBINED_EXCESS_PATH_LENGTH_MASK; return this; } /** - * Clears the Excess path length. + * Clears the combined excess path length. * * <p>This is to negate {@link #setExcessPathLengthMeters} call. */ @NonNull public Builder clearExcessPathLengthMeters() { - mExcessPathLengthMeters = 0; - mSingleSatCorrectionFlags &= ~HAS_EXCESS_PATH_LENGTH_MASK; + mCombinedExcessPathLengthMeters = 0; + mSingleSatCorrectionFlags &= ~HAS_COMBINED_EXCESS_PATH_LENGTH_MASK; return this; } - /** Sets the error estimate (1-sigma) for the Excess path length estimate */ + /** Sets the error estimate (1-sigma) for the combined excess path length estimate. */ @NonNull public Builder setExcessPathLengthUncertaintyMeters( - @FloatRange(from = 0.0f) float excessPathLengthUncertaintyMeters) { - Preconditions.checkArgument(excessPathLengthUncertaintyMeters >= 0, - "excessPathLengthUncertaintyMeters should be non-negative."); - mExcessPathLengthUncertaintyMeters = excessPathLengthUncertaintyMeters; - mSingleSatCorrectionFlags |= HAS_EXCESS_PATH_LENGTH_UNC_MASK; + @FloatRange(from = 0.0f) float combinedExcessPathLengthUncertaintyMeters) { + Preconditions.checkArgumentInRange(combinedExcessPathLengthUncertaintyMeters, 0, + Float.MAX_VALUE, "excessPathLengthUncertaintyMeters"); + mCombinedExcessPathLengthUncertaintyMeters = combinedExcessPathLengthUncertaintyMeters; + mSingleSatCorrectionFlags |= HAS_COMBINED_EXCESS_PATH_LENGTH_UNC_MASK; return this; } /** - * Clears the error estimate (1-sigma) for the Excess path length estimate + * Clears the error estimate (1-sigma) for the combined excess path length estimate. * * <p>This is to negate {@link #setExcessPathLengthUncertaintyMeters} call. */ @NonNull public Builder clearExcessPathLengthUncertaintyMeters() { - mExcessPathLengthUncertaintyMeters = 0; - mSingleSatCorrectionFlags &= ~HAS_EXCESS_PATH_LENGTH_UNC_MASK; + mCombinedExcessPathLengthUncertaintyMeters = 0; + mSingleSatCorrectionFlags &= ~HAS_COMBINED_EXCESS_PATH_LENGTH_UNC_MASK; return this; } - /** Sets the reflecting plane information */ + /** + * Sets the combined attenuation in Db. + */ + @NonNull public Builder setCombinedAttenuationDb( + @FloatRange(from = 0.0f) float combinedAttenuationDb) { + Preconditions.checkArgumentInRange(combinedAttenuationDb, 0, Float.MAX_VALUE, + "combinedAttenuationDb"); + mCombinedAttenuationDb = combinedAttenuationDb; + mSingleSatCorrectionFlags |= HAS_COMBINED_ATTENUATION_MASK; + return this; + } + + /** + * Clears the combined attenuation. + * + * <p>This is to negate {@link #setCombinedAttenuationDb} call. + */ + @NonNull public Builder clearCombinedAttenuationDb() { + mCombinedAttenuationDb = 0; + mSingleSatCorrectionFlags &= ~HAS_COMBINED_ATTENUATION_MASK; + return this; + } + + /** + * Sets the reflecting plane information. + * + * @deprecated Combined excess path does not have a reflecting plane. + */ + @Deprecated @NonNull public Builder setReflectingPlane(@Nullable GnssReflectingPlane reflectingPlane) { - mReflectingPlane = reflectingPlane; - if (reflectingPlane != null) { - mSingleSatCorrectionFlags |= HAS_REFLECTING_PLANE_MASK; - } else { - mSingleSatCorrectionFlags &= ~HAS_REFLECTING_PLANE_MASK; - } + return this; + } + + /** + * Sets the collection of {@link GnssExcessPathInfo}. + */ + @NonNull + public Builder setGnssExcessPathInfoList(@NonNull List<GnssExcessPathInfo> infoList) { + mGnssExcessInfoList = new ArrayList<>(infoList); return this; } @@ -512,9 +492,10 @@ public final class GnssSingleSatCorrection implements Parcelable { mSatId, mCarrierFrequencyHz, mProbSatIsLos, - mExcessPathLengthMeters, - mExcessPathLengthUncertaintyMeters, - mReflectingPlane); + mCombinedExcessPathLengthMeters, + mCombinedExcessPathLengthUncertaintyMeters, + mCombinedAttenuationDb, + mGnssExcessInfoList); } } } diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java index f1605f1ffe5d..033056cb2a69 100644 --- a/location/java/android/location/Location.java +++ b/location/java/android/location/Location.java @@ -132,31 +132,31 @@ public class Location implements Parcelable { } /** - * Construct a new Location object that is copied from an existing one. + * Constructs a new location copied from the given location. */ - public Location(@NonNull Location l) { - set(l); + public Location(@NonNull Location location) { + set(location); } /** * Turns this location into a copy of the given location. */ - public void set(@NonNull Location l) { - mFieldsMask = l.mFieldsMask; - mProvider = l.mProvider; - mTimeMs = l.mTimeMs; - mElapsedRealtimeNs = l.mElapsedRealtimeNs; - mElapsedRealtimeUncertaintyNs = l.mElapsedRealtimeUncertaintyNs; - mLatitudeDegrees = l.mLatitudeDegrees; - mLongitudeDegrees = l.mLongitudeDegrees; - mHorizontalAccuracyMeters = l.mHorizontalAccuracyMeters; - mAltitudeMeters = l.mAltitudeMeters; - mAltitudeAccuracyMeters = l.mAltitudeAccuracyMeters; - mSpeedMetersPerSecond = l.mSpeedMetersPerSecond; - mSpeedAccuracyMetersPerSecond = l.mSpeedAccuracyMetersPerSecond; - mBearingDegrees = l.mBearingDegrees; - mBearingAccuracyDegrees = l.mBearingAccuracyDegrees; - mExtras = (l.mExtras == null) ? null : new Bundle(l.mExtras); + public void set(@NonNull Location location) { + mFieldsMask = location.mFieldsMask; + mProvider = location.mProvider; + mTimeMs = location.mTimeMs; + mElapsedRealtimeNs = location.mElapsedRealtimeNs; + mElapsedRealtimeUncertaintyNs = location.mElapsedRealtimeUncertaintyNs; + mLatitudeDegrees = location.mLatitudeDegrees; + mLongitudeDegrees = location.mLongitudeDegrees; + mHorizontalAccuracyMeters = location.mHorizontalAccuracyMeters; + mAltitudeMeters = location.mAltitudeMeters; + mAltitudeAccuracyMeters = location.mAltitudeAccuracyMeters; + mSpeedMetersPerSecond = location.mSpeedMetersPerSecond; + mSpeedAccuracyMetersPerSecond = location.mSpeedAccuracyMetersPerSecond; + mBearingDegrees = location.mBearingDegrees; + mBearingAccuracyDegrees = location.mBearingAccuracyDegrees; + mExtras = (location.mExtras == null) ? null : new Bundle(location.mExtras); } /** @@ -182,14 +182,13 @@ public class Location implements Parcelable { } /** - * Returns the approximate distance in meters between this - * location and the given location. Distance is defined using - * the WGS84 ellipsoid. + * Returns the approximate distance in meters between this location and the given location. + * Distance is defined using the WGS84 ellipsoid. * * @param dest the destination location * @return the approximate distance in meters */ - public @FloatRange float distanceTo(@NonNull Location dest) { + public @FloatRange(from = 0.0) float distanceTo(@NonNull Location dest) { BearingDistanceCache cache = sBearingDistanceCache.get(); // See if we already have the result if (mLatitudeDegrees != cache.mLat1 || mLongitudeDegrees != cache.mLon1 @@ -201,11 +200,10 @@ public class Location implements Parcelable { } /** - * Returns the approximate initial bearing in degrees East of true - * North when traveling along the shortest path between this - * location and the given location. The shortest path is defined - * using the WGS84 ellipsoid. Locations that are (nearly) - * antipodal may produce meaningless results. + * Returns the approximate initial bearing in degrees east of true north when traveling along + * the shortest path between this location and the given location. The shortest path is defined + * using the WGS84 ellipsoid. Locations that are (nearly) antipodal may produce meaningless + * results. * * @param dest the destination location * @return the initial bearing in degrees @@ -254,7 +252,7 @@ public class Location implements Parcelable { * not be used to order or compare locations. Prefer {@link #getElapsedRealtimeNanos} for that * purpose, as the elapsed realtime clock is guaranteed to be monotonic. * - * <p>On the other hand, this method may be useful for presenting a human readable time to the + * <p>On the other hand, this method may be useful for presenting a human-readable time to the * user, or as a heuristic for comparing location fixes across reboot or across devices. * * <p>All locations generated by the {@link LocationManager} are guaranteed to have this time @@ -263,25 +261,24 @@ public class Location implements Parcelable { * * @return the Unix epoch time of this location */ - public @IntRange long getTime() { + public @IntRange(from = 0) long getTime() { return mTimeMs; } /** * Sets the Unix epoch time of this location fix, in milliseconds since the start of the Unix - * epoch (00:00:00 January 1, 1970 UTC). + * epoch (00:00:00 January 1 1970 UTC). * * @param timeMs the Unix epoch time of this location - * @see #getTime for more information about times / clocks */ - public void setTime(@IntRange long timeMs) { + public void setTime(@IntRange(from = 0) long timeMs) { mTimeMs = timeMs; } /** * Return the time of this fix in nanoseconds of elapsed realtime since system boot. * - * <p>This value can be compared with {@link android.os.SystemClock#elapsedRealtimeNanos}, to + * <p>This value can be compared with {@link android.os.SystemClock#elapsedRealtimeNanos} to * reliably order or compare locations. This is reliable because elapsed realtime is guaranteed * to be monotonic and continues to increment even when the system is in deep sleep (unlike * {@link #getTime}). However, since elapsed realtime is with reference to system boot, it does @@ -292,7 +289,7 @@ public class Location implements Parcelable { * * @return elapsed realtime of this location in nanoseconds */ - public @IntRange long getElapsedRealtimeNanos() { + public @IntRange(from = 0) long getElapsedRealtimeNanos() { return mElapsedRealtimeNs; } @@ -302,7 +299,7 @@ public class Location implements Parcelable { * @return elapsed realtime of this location in milliseconds * @see #getElapsedRealtimeNanos() */ - public @IntRange long getElapsedRealtimeMillis() { + public @IntRange(from = 0) long getElapsedRealtimeMillis() { return NANOSECONDS.toMillis(mElapsedRealtimeNs); } @@ -312,7 +309,7 @@ public class Location implements Parcelable { * * @return age of this location in milliseconds */ - public @IntRange long getElapsedRealtimeAgeMillis() { + public @IntRange(from = 0) long getElapsedRealtimeAgeMillis() { return getElapsedRealtimeAgeMillis(SystemClock.elapsedRealtime()); } @@ -323,7 +320,8 @@ public class Location implements Parcelable { * @param referenceRealtimeMs reference realtime in milliseconds * @return age of this location in milliseconds */ - public @IntRange long getElapsedRealtimeAgeMillis(@IntRange long referenceRealtimeMs) { + public long getElapsedRealtimeAgeMillis( + @IntRange(from = 0) long referenceRealtimeMs) { return referenceRealtimeMs - getElapsedRealtimeMillis(); } @@ -332,7 +330,7 @@ public class Location implements Parcelable { * * @param elapsedRealtimeNs elapsed realtime in nanoseconds */ - public void setElapsedRealtimeNanos(@IntRange long elapsedRealtimeNs) { + public void setElapsedRealtimeNanos(@IntRange(from = 0) long elapsedRealtimeNs) { mElapsedRealtimeNs = elapsedRealtimeNs; } @@ -346,7 +344,7 @@ public class Location implements Parcelable { * * @return uncertainty in nanoseconds of the elapsed realtime of this location */ - public @FloatRange double getElapsedRealtimeUncertaintyNanos() { + public @FloatRange(from = 0.0) double getElapsedRealtimeUncertaintyNanos() { return mElapsedRealtimeUncertaintyNs; } @@ -358,20 +356,20 @@ public class Location implements Parcelable { * this location */ public void setElapsedRealtimeUncertaintyNanos( - @FloatRange double elapsedRealtimeUncertaintyNs) { + @FloatRange(from = 0.0) double elapsedRealtimeUncertaintyNs) { mElapsedRealtimeUncertaintyNs = elapsedRealtimeUncertaintyNs; mFieldsMask |= HAS_ELAPSED_REALTIME_UNCERTAINTY_MASK; } /** - * True if this location has a elapsed realtime uncertainty, false otherwise. + * True if this location has an elapsed realtime uncertainty, false otherwise. */ public boolean hasElapsedRealtimeUncertaintyNanos() { return (mFieldsMask & HAS_ELAPSED_REALTIME_UNCERTAINTY_MASK) != 0; } /** - * Removes the elapsed realtime uncertainy from this location. + * Removes the elapsed realtime uncertainty from this location. */ public void removeElapsedRealtimeUncertaintyNanos() { mFieldsMask &= ~HAS_ELAPSED_REALTIME_UNCERTAINTY_MASK; @@ -383,7 +381,7 @@ public class Location implements Parcelable { * * @return latitude of this location */ - public @FloatRange double getLatitude() { + public @FloatRange(from = -90.0, to = 90.0) double getLatitude() { return mLatitudeDegrees; } @@ -392,7 +390,7 @@ public class Location implements Parcelable { * * @param latitudeDegrees latitude in degrees */ - public void setLatitude(@FloatRange double latitudeDegrees) { + public void setLatitude(@FloatRange(from = -90.0, to = 90.0) double latitudeDegrees) { mLatitudeDegrees = latitudeDegrees; } @@ -402,7 +400,7 @@ public class Location implements Parcelable { * * @return longitude of this location */ - public @FloatRange double getLongitude() { + public @FloatRange(from = -180.0, to = 180.0) double getLongitude() { return mLongitudeDegrees; } @@ -411,7 +409,7 @@ public class Location implements Parcelable { * * @param longitudeDegrees longitude in degrees */ - public void setLongitude(@FloatRange double longitudeDegrees) { + public void setLongitude(@FloatRange(from = -180.0, to = 180.0) double longitudeDegrees) { mLongitudeDegrees = longitudeDegrees; } @@ -423,12 +421,12 @@ public class Location implements Parcelable { * reported location, there is a 68% chance that the true location falls within this circle. * This accuracy value is only valid for horizontal positioning, and not vertical positioning. * - * <p>This is only valid if {@link #hasSpeed()} is true. All locations generated by the + * <p>This is only valid if {@link #hasAccuracy()} is true. All locations generated by the * {@link LocationManager} include horizontal accuracy. * * @return horizontal accuracy of this location */ - public @FloatRange float getAccuracy() { + public @FloatRange(from = 0.0) float getAccuracy() { return mHorizontalAccuracyMeters; } @@ -437,7 +435,7 @@ public class Location implements Parcelable { * * @param horizontalAccuracyMeters horizontal altitude in meters */ - public void setAccuracy(@FloatRange float horizontalAccuracyMeters) { + public void setAccuracy(@FloatRange(from = 0.0) float horizontalAccuracyMeters) { mHorizontalAccuracyMeters = horizontalAccuracyMeters; mFieldsMask |= HAS_HORIZONTAL_ACCURACY_MASK; } @@ -500,7 +498,7 @@ public class Location implements Parcelable { * * @return vertical accuracy of this location */ - public @FloatRange float getVerticalAccuracyMeters() { + public @FloatRange(from = 0.0) float getVerticalAccuracyMeters() { return mAltitudeAccuracyMeters; } @@ -509,7 +507,7 @@ public class Location implements Parcelable { * * @param altitudeAccuracyMeters altitude accuracy in meters */ - public void setVerticalAccuracyMeters(@FloatRange float altitudeAccuracyMeters) { + public void setVerticalAccuracyMeters(@FloatRange(from = 0.0) float altitudeAccuracyMeters) { mAltitudeAccuracyMeters = altitudeAccuracyMeters; mFieldsMask |= HAS_ALTITUDE_ACCURACY_MASK; } @@ -538,17 +536,16 @@ public class Location implements Parcelable { * * @return speed at the time of this location */ - public @FloatRange float getSpeed() { + public @FloatRange(from = 0.0) float getSpeed() { return mSpeedMetersPerSecond; } /** - * Set the speed at the time of this location, in meters per second. Prefer not to set negative - * speeds. + * Set the speed at the time of this location, in meters per second. * * @param speedMetersPerSecond speed in meters per second */ - public void setSpeed(@FloatRange float speedMetersPerSecond) { + public void setSpeed(@FloatRange(from = 0.0) float speedMetersPerSecond) { mSpeedMetersPerSecond = speedMetersPerSecond; mFieldsMask |= HAS_SPEED_MASK; } @@ -576,7 +573,7 @@ public class Location implements Parcelable { * * @return vertical accuracy of this location */ - public @FloatRange float getSpeedAccuracyMetersPerSecond() { + public @FloatRange(from = 0.0) float getSpeedAccuracyMetersPerSecond() { return mSpeedAccuracyMetersPerSecond; } @@ -585,7 +582,8 @@ public class Location implements Parcelable { * * @param speedAccuracyMeterPerSecond speed accuracy in meters per second */ - public void setSpeedAccuracyMetersPerSecond(@FloatRange float speedAccuracyMeterPerSecond) { + public void setSpeedAccuracyMetersPerSecond( + @FloatRange(from = 0.0) float speedAccuracyMeterPerSecond) { mSpeedAccuracyMetersPerSecond = speedAccuracyMeterPerSecond; mFieldsMask |= HAS_SPEED_ACCURACY_MASK; } @@ -613,7 +611,7 @@ public class Location implements Parcelable { * * @return bearing at the time of this location */ - public @FloatRange(from = 0f, to = 360f, toInclusive = false) float getBearing() { + public @FloatRange(from = 0.0, to = 360.0, toInclusive = false) float getBearing() { return mBearingDegrees; } @@ -663,7 +661,7 @@ public class Location implements Parcelable { * * @return bearing accuracy in degrees of this location */ - public @FloatRange float getBearingAccuracyDegrees() { + public @FloatRange(from = 0.0) float getBearingAccuracyDegrees() { return mBearingAccuracyDegrees; } @@ -672,7 +670,7 @@ public class Location implements Parcelable { * * @param bearingAccuracyDegrees bearing accuracy in degrees */ - public void setBearingAccuracyDegrees(@FloatRange float bearingAccuracyDegrees) { + public void setBearingAccuracyDegrees(@FloatRange(from = 0.0) float bearingAccuracyDegrees) { mBearingAccuracyDegrees = bearingAccuracyDegrees; mFieldsMask |= HAS_BEARING_ACCURACY_MASK; } @@ -692,9 +690,11 @@ public class Location implements Parcelable { } /** - * Returns true if the Location came from a mock provider. + * Returns true if this is a mock location. If this location comes from the Android framework, + * this indicates that the location was provided by a test location provider, and thus may not + * be related to the actual location of the device. * - * @return true if this Location came from a mock provider, false otherwise + * @return true if this location came from a mock provider, false otherwise * @deprecated Prefer {@link #isMock()} instead. */ @Deprecated @@ -703,9 +703,9 @@ public class Location implements Parcelable { } /** - * Flag this Location as having come from a mock provider or not. + * Flag this location as having come from a mock provider or not. * - * @param isFromMockProvider true if this Location came from a mock provider, false otherwise + * @param isFromMockProvider true if this location came from a mock provider, false otherwise * @deprecated Prefer {@link #setMock(boolean)} instead. * @hide */ @@ -745,7 +745,7 @@ public class Location implements Parcelable { * will be present for any location. * * <ul> - * <li> satellites - the number of satellites used to derive the GNSS fix + * <li> satellites - the number of satellites used to derive a GNSS fix * </ul> */ public @Nullable Bundle getExtras() { @@ -899,7 +899,13 @@ public class Location implements Parcelable { return s.toString(); } - /** Dumps location. */ + /** + * Dumps location information to the given Printer. + * + * @deprecated Prefer to use {@link #toString()} along with whatever custom formatting is + * required instead of this method. It is not this class's job to manage print representations. + */ + @Deprecated public void dump(@NonNull Printer pw, @Nullable String prefix) { pw.println(prefix + this); } @@ -1209,10 +1215,10 @@ public class Location implements Parcelable { * @throws IllegalArgumentException if results is null or has length < 1 */ public static void distanceBetween( - @FloatRange double startLatitude, - @FloatRange double startLongitude, - @FloatRange double endLatitude, - @FloatRange double endLongitude, + @FloatRange(from = -90.0, to = 90.0) double startLatitude, + @FloatRange(from = -180.0, to = 180.0) double startLongitude, + @FloatRange(from = -90.0, to = 90.0) double endLatitude, + @FloatRange(from = -180.0, to = 180.0) double endLongitude, float[] results) { if (results == null || results.length < 1) { throw new IllegalArgumentException("results is null or has length < 1"); diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index bdbb740a849b..c186700a4326 100755 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -39,6 +39,7 @@ import android.media.IRecordingConfigDispatcher; import android.media.IRingtonePlayer; import android.media.IStrategyPreferredDevicesDispatcher; import android.media.ISpatializerCallback; +import android.media.ISpatializerHeadTrackerAvailableCallback; import android.media.ISpatializerHeadTrackingModeCallback; import android.media.ISpatializerHeadToSoundStagePoseCallback; import android.media.ISpatializerOutputCallback; @@ -414,6 +415,11 @@ interface IAudioService { boolean isHeadTrackerEnabled(in AudioDeviceAttributes device); + boolean isHeadTrackerAvailable(); + + void registerSpatializerHeadTrackerAvailableCallback( + in ISpatializerHeadTrackerAvailableCallback cb, boolean register); + void setSpatializerEnabled(boolean enabled); boolean canBeSpatialized(in AudioAttributes aa, in AudioFormat af); diff --git a/core/java/com/android/internal/logging/UiEvent.java b/media/java/android/media/ISpatializerHeadTrackerAvailableCallback.aidl index 0407b0704e80..dc5ee1d4e985 100644 --- a/core/java/com/android/internal/logging/UiEvent.java +++ b/media/java/android/media/ISpatializerHeadTrackerAvailableCallback.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,17 +14,14 @@ * limitations under the License. */ -package com.android.internal.logging; +package android.media; -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.RetentionPolicy.SOURCE; - -import java.lang.annotation.Retention; -import java.lang.annotation.Target; +/** + * AIDL for the AudioService to signal whether audio device used by Spatializer has head tracker. + * + * {@hide} + */ +oneway interface ISpatializerHeadTrackerAvailableCallback { -@Retention(SOURCE) -@Target(FIELD) -public @interface UiEvent { - /** An explanation, suitable for Android analysts, of the UI event that this log represents. */ - String doc(); + void dispatchSpatializerHeadTrackerAvailable(boolean available); } diff --git a/media/java/android/media/MediaActionSound.java b/media/java/android/media/MediaActionSound.java index ec56d614f2b5..ad1405aa2356 100644 --- a/media/java/android/media/MediaActionSound.java +++ b/media/java/android/media/MediaActionSound.java @@ -25,7 +25,8 @@ import android.util.Log; /** * <p>A class for producing sounds that match those produced by various actions - * taken by the media and camera APIs. </p> + * taken by the media and camera APIs. It is recommended to call methods in this class + * in a background thread since it relies on binder calls.</p> * * <p>This class is recommended for use with the {@link android.hardware.camera2} API, since the * camera2 API does not play any sounds on its own for any capture or video recording actions.</p> @@ -109,7 +110,7 @@ public class MediaActionSound { /** * <p>Returns true if the application must play the shutter sound in accordance - * to certain regional restrictions. </p> + * to certain regional restrictions.</p> * * <p>If this method returns true, applications are strongly recommended to use * MediaActionSound.play(SHUTTER_CLICK) or START_VIDEO_RECORDING whenever it captures diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index 4563259c31f2..e39914db4d0f 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -223,19 +223,19 @@ import java.util.concurrent.locks.ReentrantLock; </thead> <tbody> <tr> - <td>{@code "crop-left"}</td> + <td>{@link MediaFormat#KEY_CROP_LEFT}</td> <td>Integer</td> <td>The left-coordinate (x) of the crop rectangle</td> </tr><tr> - <td>{@code "crop-top"}</td> + <td>{@link MediaFormat#KEY_CROP_TOP}</td> <td>Integer</td> <td>The top-coordinate (y) of the crop rectangle</td> </tr><tr> - <td>{@code "crop-right"}</td> + <td>{@link MediaFormat#KEY_CROP_RIGHT}</td> <td>Integer</td> <td>The right-coordinate (x) <strong>MINUS 1</strong> of the crop rectangle</td> </tr><tr> - <td>{@code "crop-bottom"}</td> + <td>{@link MediaFormat#KEY_CROP_BOTTOM}</td> <td>Integer</td> <td>The bottom-coordinate (y) <strong>MINUS 1</strong> of the crop rectangle</td> </tr><tr> @@ -251,12 +251,16 @@ import java.util.concurrent.locks.ReentrantLock; <pre class=prettyprint> MediaFormat format = decoder.getOutputFormat(…); int width = format.getInteger(MediaFormat.KEY_WIDTH); - if (format.containsKey("crop-left") && format.containsKey("crop-right")) { - width = format.getInteger("crop-right") + 1 - format.getInteger("crop-left"); + if (format.containsKey(MediaFormat.KEY_CROP_LEFT) + && format.containsKey(MediaFormat.KEY_CROP_RIGHT)) { + width = format.getInteger(MediaFormat.KEY_CROP_RIGHT) + 1 + - format.getInteger(MediaFormat.KEY_CROP_LEFT); } int height = format.getInteger(MediaFormat.KEY_HEIGHT); - if (format.containsKey("crop-top") && format.containsKey("crop-bottom")) { - height = format.getInteger("crop-bottom") + 1 - format.getInteger("crop-top"); + if (format.containsKey(MediaFormat.KEY_CROP_TOP) + && format.containsKey(MediaFormat.KEY_CROP_BOTTOM)) { + height = format.getInteger(MediaFormat.KEY_CROP_BOTTOM) + 1 + - format.getInteger(MediaFormat.KEY_CROP_TOP); } </pre> <p class=note> diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java index 4956dbefa240..9dea5b9152b7 100644 --- a/media/java/android/media/MediaFormat.java +++ b/media/java/android/media/MediaFormat.java @@ -359,6 +359,42 @@ public final class MediaFormat { public static final String KEY_HEIGHT = "height"; /** + * A key describing the bottom-coordinate (y) of the crop rectangle. + * This is the bottom-most row included in the crop frame, + * where row indices start at 0. + * Additional information on the crop rectangle semantics can be found at + * {@link android.media.MediaCodec}. + */ + public static final String KEY_CROP_BOTTOM = "crop-bottom"; + + /** + * A key describing the left-coordinate (x) of the crop rectangle. + * This is the left-most column included in the crop frame, + * where column indices start at 0. + * Additional information on the crop rectangle semantics can be found at + * {@link android.media.MediaCodec}. + */ + public static final String KEY_CROP_LEFT = "crop-left"; + + /** + * A key describing the right-coordinate (x) of the crop rectangle. + * This is the right-most column included in the crop frame, + * where column indices start at 0. + * Additional information on the crop rectangle semantics can be found at + * {@link android.media.MediaCodec}. + */ + public static final String KEY_CROP_RIGHT = "crop-right"; + + /** + * A key describing the top-coordinate (y) of the crop rectangle. + * This is the top-most row included in the crop frame, + * where row indices start at 0. + * Additional information on the crop rectangle semantics can be found at + * {@link android.media.MediaCodec}. + */ + public static final String KEY_CROP_TOP = "crop-top"; + + /** * A key describing the maximum expected width of the content in a video * decoder format, in case there are resolution changes in the video content. * The associated value is an integer @@ -793,8 +829,11 @@ public final class MediaFormat { * By default, the decoder will output the same number of channels as present in the encoded * stream, if supported. Set this value to limit the number of output channels, and use * the downmix information in the stream, if available. - * <p>Values larger than the number of channels in the content to decode are ignored. + * <p>Values larger than the number of channels in the content to decode behave just + * like the actual channel count of the content (e.g. passing 99 for the decoding of 5.1 content + * will behave like using 6). * <p>This key is only used during decoding. + * @deprecated Use the non-AAC-specific key {@link #KEY_MAX_OUTPUT_CHANNEL_COUNT} instead */ public static final String KEY_AAC_MAX_OUTPUT_CHANNEL_COUNT = "aac-max-output-channel_count"; diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java index ee0293d629b1..283a41a81057 100644 --- a/media/java/android/media/MediaRoute2Info.java +++ b/media/java/android/media/MediaRoute2Info.java @@ -539,6 +539,7 @@ public final class MediaRoute2Info implements Parcelable { /** * Gets the Deduplication ID of the route if available. * @see RouteDiscoveryPreference#shouldRemoveDuplicates() + * @hide */ @NonNull public Set<String> getDeduplicationIds() { @@ -968,6 +969,7 @@ public final class MediaRoute2Info implements Parcelable { * <p> * If it's {@code null}, the route will not be removed. * @see RouteDiscoveryPreference#shouldRemoveDuplicates() + * @hide */ @NonNull public Builder setDeduplicationIds(@NonNull Set<String> id) { diff --git a/media/java/android/media/RouteDiscoveryPreference.java b/media/java/android/media/RouteDiscoveryPreference.java index 0ba36feb4ce9..207460af140c 100644 --- a/media/java/android/media/RouteDiscoveryPreference.java +++ b/media/java/android/media/RouteDiscoveryPreference.java @@ -119,6 +119,7 @@ public final class RouteDiscoveryPreference implements Parcelable { * first in the provided list will remain. * * @see #shouldRemoveDuplicates() + * @hide */ @NonNull public List<String> getDeduplicationPackageOrder() { @@ -130,6 +131,7 @@ public final class RouteDiscoveryPreference implements Parcelable { * <p> * If it's not empty, it will only discover routes from the provider whose package name * belongs to the list. + * @hide */ @NonNull public List<String> getAllowedPackages() { @@ -151,6 +153,7 @@ public final class RouteDiscoveryPreference implements Parcelable { * Gets whether duplicate routes removal is enabled. * * @see #getDeduplicationPackageOrder() + * @hide */ public boolean shouldRemoveDuplicates() { return !mPackageOrder.isEmpty(); @@ -293,6 +296,7 @@ public final class RouteDiscoveryPreference implements Parcelable { * <p> * If it's non-empty, media router only discovers route from the provider in the list. * The default value is empty, which discovers routes from all providers. + * @hide */ @NonNull public Builder setAllowedPackages(@NonNull List<String> allowedPackages) { @@ -324,6 +328,7 @@ public final class RouteDiscoveryPreference implements Parcelable { * * @param packageOrder ordered list of package names used to remove duplicate routes, or an * empty list if deduplication should not be enabled. + * @hide */ @NonNull public Builder setDeduplicationPackageOrder(@NonNull List<String> packageOrder) { diff --git a/media/java/android/media/Spatializer.java b/media/java/android/media/Spatializer.java index be0ef37345ed..74ca4b858ff6 100644 --- a/media/java/android/media/Spatializer.java +++ b/media/java/android/media/Spatializer.java @@ -185,6 +185,45 @@ public class Spatializer { return false; } + /** + * Returns whether a head tracker is currently available for the audio device used by the + * spatializer effect. + * @return true if a head tracker is available and the effect is enabled, false otherwise. + * @see OnHeadTrackerAvailableListener + * @see #addOnHeadTrackerAvailableListener(Executor, OnHeadTrackerAvailableListener) + */ + public boolean isHeadTrackerAvailable() { + try { + return mAm.getService().isHeadTrackerAvailable(); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + return false; + } + + /** + * Adds a listener to be notified of changes to the availability of a head tracker. + * @param executor the {@code Executor} handling the callback + * @param listener the listener to receive availability updates + * @see #removeOnHeadTrackerAvailableListener(OnHeadTrackerAvailableListener) + */ + public void addOnHeadTrackerAvailableListener(@NonNull @CallbackExecutor Executor executor, + @NonNull OnHeadTrackerAvailableListener listener) { + mHeadTrackerListenerMgr.addListener(executor, listener, + "addOnHeadTrackerAvailableListener", + () -> new SpatializerHeadTrackerAvailableDispatcherStub()); + } + + /** + * Removes a previously registered listener for the availability of a head tracker. + * @param listener the listener previously registered with + * {@link #addOnHeadTrackerAvailableListener(Executor, OnHeadTrackerAvailableListener)} + */ + public void removeOnHeadTrackerAvailableListener( + @NonNull OnHeadTrackerAvailableListener listener) { + mHeadTrackerListenerMgr.removeListener(listener, "removeOnHeadTrackerAvailableListener"); + } + /** @hide */ @IntDef(flag = false, value = { SPATIALIZER_IMMERSIVE_LEVEL_OTHER, @@ -401,6 +440,22 @@ public class Spatializer { @HeadTrackingModeSet int mode); } + /** + * Interface to be notified of changes to the availability of a head tracker on the audio + * device to be used by the spatializer effect. + */ + public interface OnHeadTrackerAvailableListener { + /** + * Called when the availability of the head tracker changed. + * @param spatializer the {@code Spatializer} instance for which the head tracker + * availability was updated + * @param available true if the audio device that would output audio processed by + * the {@code Spatializer} has a head tracker associated with it, false + * otherwise. + */ + void onHeadTrackerAvailableChanged(@NonNull Spatializer spatializer, + boolean available); + } /** * @hide @@ -827,8 +882,12 @@ public class Spatializer { /** * @hide - * Returns the id of the output stream used for the spatializer effect playback + * Returns the id of the output stream used for the spatializer effect playback. + * This getter or associated listener {@link OnSpatializerOutputChangedListener} can be used for + * handling spatializer output-specific configurations (e.g. disabling speaker post-processing + * to avoid double-processing of the spatialized path). * @return id of the output stream, or 0 if no spatializer playback is active + * @see #setOnSpatializerOutputChangedListener(Executor, OnSpatializerOutputChangedListener) */ @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS) @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS) @@ -865,6 +924,8 @@ public class Spatializer { mOutputDispatcher = new SpatializerOutputDispatcherStub(); try { mAm.getService().registerSpatializerOutputCallback(mOutputDispatcher); + // immediately report the current output + mOutputDispatcher.dispatchSpatializerOutputChanged(getOutput()); } catch (RemoteException e) { mOutputListener = null; mOutputDispatcher = null; @@ -935,6 +996,36 @@ public class Spatializer { } //----------------------------------------------------------------------------- + // head tracker availability callback management and stub + /** + * manages the OnHeadTrackerAvailableListener listeners and the + * SpatializerHeadTrackerAvailableDispatcherStub + */ + private final CallbackUtil.LazyListenerManager<OnHeadTrackerAvailableListener> + mHeadTrackerListenerMgr = new CallbackUtil.LazyListenerManager(); + + private final class SpatializerHeadTrackerAvailableDispatcherStub + extends ISpatializerHeadTrackerAvailableCallback.Stub + implements CallbackUtil.DispatcherStub { + @Override + public void register(boolean register) { + try { + mAm.getService().registerSpatializerHeadTrackerAvailableCallback(this, register); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + @Override + @SuppressLint("GuardedBy") // lock applied inside callListeners method + public void dispatchSpatializerHeadTrackerAvailable(boolean available) { + mHeadTrackerListenerMgr.callListeners( + (listener) -> listener.onHeadTrackerAvailableChanged( + Spatializer.this, available)); + } + } + + //----------------------------------------------------------------------------- // head pose callback management and stub private final Object mPoseListenerLock = new Object(); /** diff --git a/media/java/android/media/projection/IMediaProjection.aidl b/media/java/android/media/projection/IMediaProjection.aidl index 19fc0521d7aa..b136d5bc4db3 100644 --- a/media/java/android/media/projection/IMediaProjection.aidl +++ b/media/java/android/media/projection/IMediaProjection.aidl @@ -17,6 +17,7 @@ package android.media.projection; import android.media.projection.IMediaProjectionCallback; +import android.window.WindowContainerToken; /** {@hide} */ interface IMediaProjection { @@ -28,4 +29,16 @@ interface IMediaProjection { int applyVirtualDisplayFlags(int flags); void registerCallback(IMediaProjectionCallback callback); void unregisterCallback(IMediaProjectionCallback callback); + + /** + * Returns the {@link android.window.WindowContainerToken} identifying the task to record, or + * {@code null} if there is none. + */ + WindowContainerToken getTaskRecordingWindowContainerToken(); + + /** + * Updates the {@link android.window.WindowContainerToken} identifying the task to record, or + * {@code null} if there is none. + */ + void setTaskRecordingWindowContainerToken(in WindowContainerToken token); } diff --git a/media/java/android/media/projection/MediaProjection.java b/media/java/android/media/projection/MediaProjection.java index 4dde5e8d39a2..b5f95938f845 100644 --- a/media/java/android/media/projection/MediaProjection.java +++ b/media/java/android/media/projection/MediaProjection.java @@ -25,13 +25,14 @@ import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; import android.hardware.display.VirtualDisplayConfig; import android.os.Handler; -import android.os.IBinder; import android.os.RemoteException; import android.util.ArrayMap; import android.util.Log; import android.view.ContentRecordingSession; +import android.view.IWindowManager; import android.view.Surface; import android.view.WindowManagerGlobal; +import android.window.WindowContainerToken; import java.util.Map; @@ -171,16 +172,34 @@ public final class MediaProjection { @NonNull VirtualDisplayConfig.Builder virtualDisplayConfig, @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) { try { - final Context windowContext = mContext.createWindowContext( - mContext.getDisplayNoVerify(), - TYPE_APPLICATION, null /* options */); - final IBinder windowContextToken = windowContext.getWindowContextToken(); + final IWindowManager wmService = WindowManagerGlobal.getWindowManagerService(); + final WindowContainerToken taskWindowContainerToken = + mImpl.getTaskRecordingWindowContainerToken(); + Context windowContext = null; + ContentRecordingSession session; + if (taskWindowContainerToken == null) { + windowContext = mContext.createWindowContext(mContext.getDisplayNoVerify(), + TYPE_APPLICATION, null /* options */); + session = ContentRecordingSession.createDisplaySession( + windowContext.getWindowContextToken()); + } else { + session = ContentRecordingSession.createTaskSession( + taskWindowContainerToken.asBinder()); + } virtualDisplayConfig.setWindowManagerMirroring(true); final DisplayManager dm = mContext.getSystemService(DisplayManager.class); final VirtualDisplay virtualDisplay = dm.createVirtualDisplay(this, - virtualDisplayConfig.build(), - callback, handler, windowContext); - setSession(windowContextToken, virtualDisplay); + virtualDisplayConfig.build(), callback, handler, windowContext); + if (virtualDisplay == null) { + // Since WM handling a new display and DM creating a new VirtualDisplay is async, + // WM may have tried to start task recording and encountered an error that required + // stopping recording entirely. The VirtualDisplay would then be null when the + // MediaProjection is no longer active. + return null; + } + session.setDisplayId(virtualDisplay.getDisplay().getDisplayId()); + // Successfully set up, so save the current session details. + wmService.setContentRecordingSession(session); return virtualDisplay; } catch (RemoteException e) { // Can not capture if WMS is not accessible, so bail out. @@ -189,28 +208,6 @@ public final class MediaProjection { } /** - * Updates the {@link ContentRecordingSession} describing the recording taking place on this - * {@link VirtualDisplay}. - * - * @throws RemoteException if updating the session on the server failed. - */ - private void setSession(@NonNull IBinder windowContextToken, - @Nullable VirtualDisplay virtualDisplay) - throws RemoteException { - if (virtualDisplay == null) { - // Not able to set up a new VirtualDisplay. - return; - } - // Identify the VirtualDisplay that will be hosting the recording. - ContentRecordingSession session = ContentRecordingSession.createDisplaySession( - windowContextToken); - session.setDisplayId(virtualDisplay.getDisplay().getDisplayId()); - // TODO(b/216625226) handle task recording. - // Successfully set up, so save the current session details. - WindowManagerGlobal.getWindowManagerService().setContentRecordingSession(session); - } - - /** * Stops projection. */ public void stop() { diff --git a/media/java/android/media/tv/AdRequest.java b/media/java/android/media/tv/AdRequest.java index 0542c5598c01..f2fb93d803a8 100644 --- a/media/java/android/media/tv/AdRequest.java +++ b/media/java/android/media/tv/AdRequest.java @@ -163,7 +163,11 @@ public final class AdRequest implements Parcelable { /** * Gets the metadata of the media file. - * <p>This includes additional information the TV input needs to play the AD media. + * + * <p>This includes additional information the TV input needs to play the AD media. This may + * include fields in {@link android.media.MediaFormat} like + * {@link android.media.MediaFormat#KEY_SAMPLE_RATE}, or integrity information like SHA. What + * data is included depends on the format of the media file. * * @return The metadata of the media file. Can be an empty bundle for * {@link #REQUEST_TYPE_STOP}. diff --git a/media/java/android/media/tv/AitInfo.java b/media/java/android/media/tv/AitInfo.java index 8e80a62be3de..c88a2b5048df 100644 --- a/media/java/android/media/tv/AitInfo.java +++ b/media/java/android/media/tv/AitInfo.java @@ -17,7 +17,7 @@ package android.media.tv; import android.annotation.NonNull; -import android.media.tv.interactive.TvInteractiveAppInfo; +import android.media.tv.interactive.TvInteractiveAppServiceInfo; import android.os.Parcel; import android.os.Parcelable; @@ -50,7 +50,7 @@ public final class AitInfo implements Parcelable { /** * Constructs AIT info. */ - public AitInfo(@TvInteractiveAppInfo.InteractiveAppType int type, int version) { + public AitInfo(@TvInteractiveAppServiceInfo.InteractiveAppType int type, int version) { mType = type; mVersion = version; } @@ -58,7 +58,7 @@ public final class AitInfo implements Parcelable { /** * Gets interactive app type. */ - @TvInteractiveAppInfo.InteractiveAppType + @TvInteractiveAppServiceInfo.InteractiveAppType public int getType() { return mType; } diff --git a/media/java/android/media/tv/CommandRequest.java b/media/java/android/media/tv/CommandRequest.java index ffb6e07bc485..3245fb504f85 100644 --- a/media/java/android/media/tv/CommandRequest.java +++ b/media/java/android/media/tv/CommandRequest.java @@ -24,6 +24,8 @@ import android.os.Parcelable; * A request for command from broadcast signal. */ public final class CommandRequest extends BroadcastInfoRequest implements Parcelable { + public static final String ARGUMENT_TYPE_XML = "xml"; + public static final String ARGUMENT_TYPE_JSON = "json"; private static final @TvInputManager.BroadcastInfoType int REQUEST_TYPE = TvInputManager.BROADCAST_INFO_TYPE_COMMAND; @@ -41,35 +43,38 @@ public final class CommandRequest extends BroadcastInfoRequest implements Parcel } }; - private final String mNameSpace; + private final String mNamespace; private final String mName; private final String mArguments; + private final String mArgumentType; static CommandRequest createFromParcelBody(Parcel in) { return new CommandRequest(in); } - public CommandRequest(int requestId, @RequestOption int option, @NonNull String nameSpace, - @NonNull String name, @NonNull String arguments) { + public CommandRequest(int requestId, @RequestOption int option, @NonNull String namespace, + @NonNull String name, @NonNull String arguments, @NonNull String argumentType) { super(REQUEST_TYPE, requestId, option); - mNameSpace = nameSpace; + mNamespace = namespace; mName = name; mArguments = arguments; + mArgumentType = argumentType; } CommandRequest(Parcel source) { super(REQUEST_TYPE, source); - mNameSpace = source.readString(); + mNamespace = source.readString(); mName = source.readString(); mArguments = source.readString(); + mArgumentType = source.readString(); } /** * Gets the namespace of the command. */ @NonNull - public String getNameSpace() { - return mNameSpace; + public String getNamespace() { + return mNamespace; } /** @@ -89,6 +94,15 @@ public final class CommandRequest extends BroadcastInfoRequest implements Parcel return mArguments; } + /** + * Gets the argument type of the command. + * It could be either JSON or XML. + */ + @NonNull + public String getArgumentType() { + return mArgumentType; + } + @Override public int describeContents() { return 0; @@ -97,8 +111,9 @@ public final class CommandRequest extends BroadcastInfoRequest implements Parcel @Override public void writeToParcel(@NonNull Parcel dest, int flags) { super.writeToParcel(dest, flags); - dest.writeString(mNameSpace); + dest.writeString(mNamespace); dest.writeString(mName); dest.writeString(mArguments); + dest.writeString(mArgumentType); } } diff --git a/media/java/android/media/tv/CommandResponse.java b/media/java/android/media/tv/CommandResponse.java index c8853f1d8373..8e448cdbd707 100644 --- a/media/java/android/media/tv/CommandResponse.java +++ b/media/java/android/media/tv/CommandResponse.java @@ -25,6 +25,8 @@ import android.os.Parcelable; * A response for command from broadcast signal. */ public final class CommandResponse extends BroadcastInfoResponse implements Parcelable { + public static final String RESPONSE_TYPE_XML = "xml"; + public static final String RESPONSE_TYPE_JSON = "json"; private static final @TvInputManager.BroadcastInfoType int RESPONSE_TYPE = TvInputManager.BROADCAST_INFO_TYPE_COMMAND; @@ -43,20 +45,23 @@ public final class CommandResponse extends BroadcastInfoResponse implements Parc }; private final String mResponse; + private final String mResponseType; static CommandResponse createFromParcelBody(Parcel in) { return new CommandResponse(in); } - public CommandResponse(int requestId, int sequence, - @ResponseResult int responseResult, @Nullable String response) { + public CommandResponse(int requestId, int sequence, @ResponseResult int responseResult, + @Nullable String response, @NonNull String responseType) { super(RESPONSE_TYPE, requestId, sequence, responseResult); mResponse = response; + mResponseType = responseType; } CommandResponse(Parcel source) { super(RESPONSE_TYPE, source); mResponse = source.readString(); + mResponseType = source.readString(); } /** @@ -68,6 +73,15 @@ public final class CommandResponse extends BroadcastInfoResponse implements Parc return mResponse; } + /** + * Gets the type of the command response. + * It could be either JSON or XML. + */ + @NonNull + public String getResponseType() { + return mResponseType; + } + @Override public int describeContents() { return 0; @@ -77,5 +91,6 @@ public final class CommandResponse extends BroadcastInfoResponse implements Parc public void writeToParcel(@NonNull Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeString(mResponse); + dest.writeString(mResponseType); } } diff --git a/media/java/android/media/tv/SectionRequest.java b/media/java/android/media/tv/SectionRequest.java index 5957528554dc..078e83222e4e 100644 --- a/media/java/android/media/tv/SectionRequest.java +++ b/media/java/android/media/tv/SectionRequest.java @@ -80,6 +80,11 @@ public final class SectionRequest extends BroadcastInfoRequest implements Parcel /** * Gets the version number of requested session. If it is null, value will be -1. + * <p>The consistency of version numbers between request and response depends on + * {@link BroadcastInfoRequest.RequestOption}. If the request has RequestOption value + * REQUEST_OPTION_AUTO_UPDATE, then the response may be set to the latest version which may be + * different from the version of the request. Otherwise, response with a different version from + * its request will be considered invalid. */ public int getVersion() { return mVersion; diff --git a/media/java/android/media/tv/SectionResponse.java b/media/java/android/media/tv/SectionResponse.java index 35836bed385f..f38ea9dfac99 100644 --- a/media/java/android/media/tv/SectionResponse.java +++ b/media/java/android/media/tv/SectionResponse.java @@ -74,14 +74,20 @@ public final class SectionResponse extends BroadcastInfoResponse implements Parc } /** - * Gets the Version number of requested session. + * Gets the Version number of requested session. If it is null, value will be -1. + * <p>The consistency of version numbers between request and response depends on + * {@link BroadcastInfoRequest.RequestOption}. If the request has RequestOption value + * REQUEST_OPTION_AUTO_UPDATE, then the response may be set to the latest version which may be + * different from the version of the request. Otherwise, response with a different version from + * its request will be considered invalid. */ public int getVersion() { return mVersion; } /** - * Gets the raw data of session. + * Gets the raw data of session. The sessionData field represents payload data of the session + * after session header, which includes version and sessionId. */ @NonNull public Bundle getSessionData() { diff --git a/media/java/android/media/tv/StreamEventResponse.java b/media/java/android/media/tv/StreamEventResponse.java index f952ce929842..28dff374d539 100644 --- a/media/java/android/media/tv/StreamEventResponse.java +++ b/media/java/android/media/tv/StreamEventResponse.java @@ -43,7 +43,7 @@ public final class StreamEventResponse extends BroadcastInfoResponse implements }; private final int mEventId; - private final long mNpt; + private final long mNptMillis; private final byte[] mData; static StreamEventResponse createFromParcelBody(Parcel in) { @@ -51,17 +51,17 @@ public final class StreamEventResponse extends BroadcastInfoResponse implements } public StreamEventResponse(int requestId, int sequence, @ResponseResult int responseResult, - int eventId, long npt, @Nullable byte[] data) { + int eventId, long nptMillis, @Nullable byte[] data) { super(RESPONSE_TYPE, requestId, sequence, responseResult); mEventId = eventId; - mNpt = npt; + mNptMillis = nptMillis; mData = data; } private StreamEventResponse(@NonNull Parcel source) { super(RESPONSE_TYPE, source); mEventId = source.readInt(); - mNpt = source.readLong(); + mNptMillis = source.readLong(); int dataLength = source.readInt(); mData = new byte[dataLength]; source.readByteArray(mData); @@ -76,9 +76,10 @@ public final class StreamEventResponse extends BroadcastInfoResponse implements /** * Returns the NPT(Normal Play Time) value when the event occurred or will occur. + * <p>The time unit of NPT is millisecond. */ - public long getNpt() { - return mNpt; + public long getNptMillis() { + return mNptMillis; } /** @@ -98,7 +99,7 @@ public final class StreamEventResponse extends BroadcastInfoResponse implements public void writeToParcel(@NonNull Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeInt(mEventId); - dest.writeLong(mNpt); + dest.writeLong(mNptMillis); dest.writeInt(mData.length); dest.writeByteArray(mData); } diff --git a/media/java/android/media/tv/TableRequest.java b/media/java/android/media/tv/TableRequest.java index 37df4eaf1ed0..a1a6b516859c 100644 --- a/media/java/android/media/tv/TableRequest.java +++ b/media/java/android/media/tv/TableRequest.java @@ -91,7 +91,12 @@ public final class TableRequest extends BroadcastInfoRequest implements Parcelab } /** - * Gets the version number of requested table. + * Gets the version number of requested table. If it is null, value will be -1. + * <p>The consistency of version numbers between request and response depends on + * {@link BroadcastInfoRequest.RequestOption}. If the request has RequestOption value + * REQUEST_OPTION_AUTO_UPDATE, then the response may be set to the latest version which may be + * different from the version of the request. Otherwise, response with a different version from + * its request will be considered invalid. */ public int getVersion() { return mVersion; diff --git a/media/java/android/media/tv/TableResponse.java b/media/java/android/media/tv/TableResponse.java index e9f1136875f5..afc9bee5fb85 100644 --- a/media/java/android/media/tv/TableResponse.java +++ b/media/java/android/media/tv/TableResponse.java @@ -76,7 +76,12 @@ public final class TableResponse extends BroadcastInfoResponse implements Parcel } /** - * Gets the Version number of table. + * Gets the version number of requested table. If it is null, value will be -1. + * <p>The consistency of version numbers between request and response depends on + * {@link BroadcastInfoRequest.RequestOption}. If the request has RequestOption value + * REQUEST_OPTION_AUTO_UPDATE, then the response may be set to the latest version which may be + * different from the version of the request. Otherwise, response with a different version from + * its request will be considered invalid. */ public int getVersion() { return mVersion; diff --git a/media/java/android/media/tv/TimelineResponse.java b/media/java/android/media/tv/TimelineResponse.java index fbeb0c4bf268..7de30f579a35 100644 --- a/media/java/android/media/tv/TimelineResponse.java +++ b/media/java/android/media/tv/TimelineResponse.java @@ -18,6 +18,7 @@ package android.media.tv; import android.annotation.NonNull; import android.annotation.Nullable; +import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; @@ -79,8 +80,8 @@ public final class TimelineResponse extends BroadcastInfoResponse implements Par * that conveys Time Values on it. */ @Nullable - public String getSelector() { - return mSelector; + public Uri getSelector() { + return Uri.parse(mSelector); } /** diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index 69fe5ee49872..149c2f471a4c 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -680,7 +680,7 @@ public final class TvInputManager { * @param session A {@link TvInputManager.Session} associated with this callback. * @param strength The current signal strength. */ - public void onSignalStrength(Session session, @SignalStrength int strength) { + public void onSignalStrengthUpdated(Session session, @SignalStrength int strength) { } /** @@ -898,7 +898,7 @@ public final class TvInputManager { mHandler.post(new Runnable() { @Override public void run() { - mSessionCallback.onSignalStrength(mSession, strength); + mSessionCallback.onSignalStrengthUpdated(mSession, strength); if (mSession.mIAppNotificationEnabled && mSession.getInteractiveAppSession() != null) { mSession.getInteractiveAppSession().notifySignalStrength(strength); diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java index 4d63af7be474..ff3d06c5bd69 100644 --- a/media/java/android/media/tv/TvView.java +++ b/media/java/android/media/tv/TvView.java @@ -1083,7 +1083,7 @@ public class TvView extends ViewGroup { * @param inputId The ID of the TV input bound to this view. * @param strength The current signal strength. */ - public void onSignalStrength( + public void onSignalStrengthUpdated( @NonNull String inputId, @TvInputManager.SignalStrength int strength) { } @@ -1406,16 +1406,16 @@ public class TvView extends ViewGroup { } @Override - public void onSignalStrength(Session session, int strength) { + public void onSignalStrengthUpdated(Session session, int strength) { if (DEBUG) { - Log.d(TAG, "onSignalStrength(strength=" + strength + ")"); + Log.d(TAG, "onSignalStrengthUpdated(strength=" + strength + ")"); } if (this != mSessionCallback) { - Log.w(TAG, "onSignalStrength - session not created"); + Log.w(TAG, "onSignalStrengthUpdated - session not created"); return; } if (mCallback != null) { - mCallback.onSignalStrength(mInputId, strength); + mCallback.onSignalStrengthUpdated(mInputId, strength); } } diff --git a/media/java/android/media/tv/interactive/AppLinkInfo.aidl b/media/java/android/media/tv/interactive/AppLinkInfo.aidl index 6759fc499e1c..f551c9fcf6c7 100644 --- a/media/java/android/media/tv/interactive/AppLinkInfo.aidl +++ b/media/java/android/media/tv/interactive/AppLinkInfo.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * 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. diff --git a/media/java/android/media/tv/interactive/AppLinkInfo.java b/media/java/android/media/tv/interactive/AppLinkInfo.java index 0eb6fa8996d6..d5e995cbcfab 100644 --- a/media/java/android/media/tv/interactive/AppLinkInfo.java +++ b/media/java/android/media/tv/interactive/AppLinkInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * 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. @@ -17,8 +17,8 @@ package android.media.tv.interactive; import android.annotation.NonNull; -import android.annotation.Nullable; import android.content.ComponentName; +import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; @@ -27,68 +27,41 @@ import android.os.Parcelable; */ public final class AppLinkInfo implements Parcelable { private @NonNull ComponentName mComponentName; - private @Nullable String mUriScheme; - private @Nullable String mUriHost; - private @Nullable String mUriPrefix; - + private @NonNull Uri mUri; /** * Creates a new AppLinkInfo. + * + * @param packageName Package Name of AppLinkInfo. + * @param className Class Name of AppLinkInfo. + * @param uriString Uri of AppLinkInfo. */ - private AppLinkInfo( + public AppLinkInfo( @NonNull String packageName, @NonNull String className, - @Nullable String uriScheme, - @Nullable String uriHost, - @Nullable String uriPrefix) { + @NonNull String uriString) { com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, packageName); com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, className); this.mComponentName = new ComponentName(packageName, className); - this.mUriScheme = uriScheme; - this.mUriHost = uriHost; - this.mUriPrefix = uriPrefix; + this.mUri = Uri.parse(uriString); } /** - * Gets package name of the App link. + * Gets component name of the App link, which contains package name and class name. */ @NonNull - public String getPackageName() { - return mComponentName.getPackageName(); + public ComponentName getComponentName() { + return mComponentName; } /** - * Gets package class of the App link. + * Gets URI of the App link. */ @NonNull - public String getClassName() { - return mComponentName.getClassName(); - } - - /** - * Gets URI scheme of the App link. - */ - @Nullable - public String getUriScheme() { - return mUriScheme; - } - - /** - * Gets URI host of the App link. - */ - @Nullable - public String getUriHost() { - return mUriHost; - } - - /** - * Gets URI prefix of the App link. - */ - @Nullable - public String getUriPrefix() { - return mUriPrefix; + public Uri getUri() { + return mUri; } @Override @@ -96,19 +69,15 @@ public final class AppLinkInfo implements Parcelable { return "AppLinkInfo { " + "packageName = " + mComponentName.getPackageName() + ", " + "className = " + mComponentName.getClassName() + ", " - + "uriScheme = " + mUriScheme + ", " - + "uriHost = " + mUriHost + ", " - + "uriPrefix = " + mUriPrefix + + "uri = " + mUri.toString() + " }"; } @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeString(mComponentName.getPackageName()); - dest.writeString(mComponentName.getClassName()); - dest.writeString(mUriScheme); - dest.writeString(mUriHost); - dest.writeString(mUriPrefix); + mComponentName.writeToParcel(dest, flags); + String uriString = mUri == null ? null : mUri.toString(); + dest.writeString(uriString); } @Override @@ -117,20 +86,13 @@ public final class AppLinkInfo implements Parcelable { } /* package-private */ AppLinkInfo(@NonNull Parcel in) { - String packageName = in.readString(); - String className = in.readString(); - String uriScheme = in.readString(); - String uriHost = in.readString(); - String uriPrefix = in.readString(); - + mComponentName = ComponentName.readFromParcel(in); com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, packageName); + NonNull.class, null, mComponentName.getPackageName()); com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, className); - this.mComponentName = new ComponentName(packageName, className); - this.mUriScheme = uriScheme; - this.mUriHost = uriHost; - this.mUriPrefix = uriPrefix; + NonNull.class, null, mComponentName.getClassName()); + String uriString = in.readString(); + mUri = uriString == null ? null : Uri.parse(uriString); } @NonNull @@ -146,68 +108,4 @@ public final class AppLinkInfo implements Parcelable { return new AppLinkInfo(in); } }; - - /** - * A builder for {@link AppLinkInfo} - */ - public static final class Builder { - private @NonNull String mPackageName; - private @NonNull String mClassName; - private @Nullable String mUriScheme; - private @Nullable String mUriHost; - private @Nullable String mUriPrefix; - - /** - * Creates a new Builder. - */ - public Builder( - @NonNull String packageName, - @NonNull String className) { - mPackageName = packageName; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mPackageName); - mClassName = className; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mClassName); - } - - /** - * Sets URI scheme of the App link. - */ - @NonNull - public Builder setUriScheme(@NonNull String value) { - mUriScheme = value; - return this; - } - - /** - * Sets URI host of the App link. - */ - @NonNull - public Builder setUriHost(@NonNull String value) { - mUriHost = value; - return this; - } - - /** - * Sets URI prefix of the App link. - */ - @NonNull - public Builder setUriPrefix(@NonNull String value) { - mUriPrefix = value; - return this; - } - - /** Builds the instance. This builder should not be touched after calling this! */ - @NonNull - public AppLinkInfo build() { - AppLinkInfo o = new AppLinkInfo( - mPackageName, - mClassName, - mUriScheme, - mUriHost, - mUriPrefix); - return o; - } - } } diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl index a3e58d16f655..50aa6febacf7 100644 --- a/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl +++ b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * 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. @@ -44,5 +44,7 @@ oneway interface ITvInteractiveAppClient { void onRequestStreamVolume(int seq); void onRequestTrackInfoList(int seq); void onRequestCurrentTvInputId(int seq); + void onRequestSigning( + in String id, in String algorithm, in String alias, in byte[] data, int seq); void onAdRequest(in AdRequest request, int Seq); } diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl index aaabe342d9f1..9ff564ea3737 100644 --- a/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl +++ b/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * 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. @@ -23,7 +23,7 @@ import android.media.tv.TvTrackInfo; import android.media.tv.interactive.AppLinkInfo; import android.media.tv.interactive.ITvInteractiveAppClient; import android.media.tv.interactive.ITvInteractiveAppManagerCallback; -import android.media.tv.interactive.TvInteractiveAppInfo; +import android.media.tv.interactive.TvInteractiveAppServiceInfo; import android.net.Uri; import android.os.Bundle; import android.view.Surface; @@ -33,8 +33,7 @@ import android.view.Surface; * @hide */ interface ITvInteractiveAppManager { - List<TvInteractiveAppInfo> getTvInteractiveAppServiceList(int userId); - void prepare(String tiasId, int type, int userId); + List<TvInteractiveAppServiceInfo> getTvInteractiveAppServiceList(int userId); void registerAppLinkInfo(String tiasId, in AppLinkInfo info, int userId); void unregisterAppLinkInfo(String tiasId, in AppLinkInfo info, int userId); void sendAppLinkCommand(String tiasId, in Bundle command, int userId); @@ -50,6 +49,8 @@ interface ITvInteractiveAppManager { void sendStreamVolume(in IBinder sessionToken, float volume, int userId); void sendTrackInfoList(in IBinder sessionToken, in List<TvTrackInfo> tracks, int userId); void sendCurrentTvInputId(in IBinder sessionToken, in String inputId, int userId); + void sendSigningResult(in IBinder sessionToken, in String signingId, in byte[] result, + int userId); void createSession(in ITvInteractiveAppClient client, in String iAppServiceId, int type, int seq, int userId); void releaseSession(in IBinder sessionToken, int userId); diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppManagerCallback.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppManagerCallback.aidl index 23be4c64fcc4..fed86dc9e0a8 100644 --- a/media/java/android/media/tv/interactive/ITvInteractiveAppManagerCallback.aidl +++ b/media/java/android/media/tv/interactive/ITvInteractiveAppManagerCallback.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * 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. @@ -16,7 +16,7 @@ package android.media.tv.interactive; -import android.media.tv.interactive.TvInteractiveAppInfo; +import android.media.tv.interactive.TvInteractiveAppServiceInfo; /** * Interface to receive callbacks from ITvInteractiveAppManager regardless of sessions. @@ -26,6 +26,6 @@ interface ITvInteractiveAppManagerCallback { void onInteractiveAppServiceAdded(in String iAppServiceId); void onInteractiveAppServiceRemoved(in String iAppServiceId); void onInteractiveAppServiceUpdated(in String iAppServiceId); - void onTvInteractiveAppInfoUpdated(in TvInteractiveAppInfo tvIAppInfo); + void onTvInteractiveAppServiceInfoUpdated(in TvInteractiveAppServiceInfo tvIAppInfo); void onStateChanged(in String iAppServiceId, int type, int state, int err); }
\ No newline at end of file diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppService.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppService.aidl index b6d518ff7242..fb58ca7843ef 100644 --- a/media/java/android/media/tv/interactive/ITvInteractiveAppService.aidl +++ b/media/java/android/media/tv/interactive/ITvInteractiveAppService.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * 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. @@ -32,7 +32,6 @@ oneway interface ITvInteractiveAppService { void unregisterCallback(in ITvInteractiveAppServiceCallback callback); void createSession(in InputChannel channel, in ITvInteractiveAppSessionCallback callback, in String iAppServiceId, int type); - void prepare(int type); void registerAppLinkInfo(in AppLinkInfo info); void unregisterAppLinkInfo(in AppLinkInfo info); void sendAppLinkCommand(in Bundle command); diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppServiceCallback.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppServiceCallback.aidl index 970b94327572..87b3c1df6197 100644 --- a/media/java/android/media/tv/interactive/ITvInteractiveAppServiceCallback.aidl +++ b/media/java/android/media/tv/interactive/ITvInteractiveAppServiceCallback.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * 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. diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl index c449d2475428..e14b2bb18ea6 100644 --- a/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl +++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * 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. @@ -42,6 +42,7 @@ oneway interface ITvInteractiveAppSession { void sendStreamVolume(float volume); void sendTrackInfoList(in List<TvTrackInfo> tracks); void sendCurrentTvInputId(in String inputId); + void sendSigningResult(in String signingId, in byte[] result); void release(); void notifyTuned(in Uri channelUri); void notifyTrackSelected(int type, in String trackId); diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl index 385f0d4f766a..32b08b7042fe 100644 --- a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl +++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * 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. @@ -43,5 +43,6 @@ oneway interface ITvInteractiveAppSessionCallback { void onRequestStreamVolume(); void onRequestTrackInfoList(); void onRequestCurrentTvInputId(); + void onRequestSigning(in String id, in String algorithm, in String alias, in byte[] data); void onAdRequest(in AdRequest request); } diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java index 9eb4a6c393ab..d3cbcdc9a255 100755 --- a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java +++ b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * 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. @@ -28,7 +28,6 @@ import android.media.tv.AdResponse; import android.media.tv.BroadcastInfoRequest; import android.media.tv.BroadcastInfoResponse; import android.media.tv.TvContentRating; -import android.media.tv.TvInputInfo; import android.media.tv.TvInputManager; import android.media.tv.TvTrackInfo; import android.net.Uri; @@ -249,7 +248,7 @@ public final class TvInteractiveAppManager { * * @see #sendAppLinkCommand(String, Bundle) * @see #ACTION_APP_LINK_COMMAND - * @see android.media.tv.interactive.TvInteractiveAppInfo#getId() + * @see TvInteractiveAppServiceInfo#getId() */ public static final String INTENT_KEY_INTERACTIVE_APP_SERVICE_ID = "interactive_app_id"; @@ -269,7 +268,7 @@ public final class TvInteractiveAppManager { * * @see #sendAppLinkCommand(String, Bundle) * @see #ACTION_APP_LINK_COMMAND - * @see android.media.tv.interactive.TvInteractiveAppInfo#getSupportedTypes() + * @see android.media.tv.interactive.TvInteractiveAppServiceInfo#getSupportedTypes() * @see android.media.tv.interactive.TvInteractiveAppView#createBiInteractiveApp(Uri, Bundle) */ public static final String INTENT_KEY_BI_INTERACTIVE_APP_TYPE = "bi_interactive_app_type"; @@ -285,6 +284,16 @@ public final class TvInteractiveAppManager { */ public static final String INTENT_KEY_BI_INTERACTIVE_APP_URI = "bi_interactive_app_uri"; + /** + * Intent key for command type. It's used to send app command to TV app. The value of this key + * could vary according to TV apps. + * <p>Type: String + * + * @see #sendAppLinkCommand(String, Bundle) + * @see #ACTION_APP_LINK_COMMAND + */ + public static final String INTENT_KEY_COMMAND_TYPE = "command_type"; + private final ITvInteractiveAppManager mService; private final int mUserId; @@ -478,6 +487,19 @@ public final class TvInteractiveAppManager { } @Override + public void onRequestSigning( + String id, String algorithm, String alias, byte[] data, int seq) { + synchronized (mSessionCallbackRecordMap) { + SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); + if (record == null) { + Log.e(TAG, "Callback not found for seq " + seq); + return; + } + record.postRequestSigning(id, algorithm, alias, data); + } + } + + @Override public void onSessionStateChanged(int state, int err, int seq) { synchronized (mSessionCallbackRecordMap) { SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); @@ -543,11 +565,11 @@ public final class TvInteractiveAppManager { } @Override - public void onTvInteractiveAppInfoUpdated(TvInteractiveAppInfo iAppInfo) { + public void onTvInteractiveAppServiceInfoUpdated(TvInteractiveAppServiceInfo iAppInfo) { // TODO: add public API updateInteractiveAppInfo() synchronized (mLock) { for (TvInteractiveAppCallbackRecord record : mCallbackRecords) { - record.postTvInteractiveAppInfoUpdated(iAppInfo); + record.postTvInteractiveAppServiceInfoUpdated(iAppInfo); } } } @@ -611,16 +633,17 @@ public final class TvInteractiveAppManager { * This is called when the information about an existing TV Interactive App service has been * updated. * - * <p>Because the system automatically creates a <code>TvInteractiveAppInfo</code> object - * for each TV Interactive App service based on the information collected from the + * <p>Because the system automatically creates a <code>TvInteractiveAppServiceInfo</code> + * object for each TV Interactive App service based on the information collected from the * <code>AndroidManifest.xml</code>, this method is only called back when such information * has changed dynamically. * - * @param iAppInfo The <code>TvInteractiveAppInfo</code> object that contains new + * @param iAppInfo The <code>TvInteractiveAppServiceInfo</code> object that contains new * information. * @hide */ - public void onTvInteractiveAppInfoUpdated(@NonNull TvInteractiveAppInfo iAppInfo) { + public void onTvInteractiveAppServiceInfoUpdated( + @NonNull TvInteractiveAppServiceInfo iAppInfo) { } /** @@ -634,7 +657,7 @@ public final class TvInteractiveAppManager { */ public void onTvInteractiveAppServiceStateChanged( @NonNull String iAppServiceId, - @TvInteractiveAppInfo.InteractiveAppType int type, + @TvInteractiveAppServiceInfo.InteractiveAppType int type, @ServiceState int state, @ErrorCode int err) { } @@ -680,11 +703,12 @@ public final class TvInteractiveAppManager { }); } - public void postTvInteractiveAppInfoUpdated(final TvInteractiveAppInfo iAppInfo) { + public void postTvInteractiveAppServiceInfoUpdated( + final TvInteractiveAppServiceInfo iAppInfo) { mExecutor.execute(new Runnable() { @Override public void run() { - mCallback.onTvInteractiveAppInfoUpdated(iAppInfo); + mCallback.onTvInteractiveAppServiceInfoUpdated(iAppInfo); } }); } @@ -737,11 +761,11 @@ public final class TvInteractiveAppManager { /** * Returns the complete list of TV Interactive App service on the system. * - * @return List of {@link TvInteractiveAppInfo} for each TV Interactive App service that + * @return List of {@link TvInteractiveAppServiceInfo} for each TV Interactive App service that * describes its meta information. */ @NonNull - public List<TvInteractiveAppInfo> getTvInteractiveAppServiceList() { + public List<TvInteractiveAppServiceInfo> getTvInteractiveAppServiceList() { try { return mService.getTvInteractiveAppServiceList(mUserId); } catch (RemoteException e) { @@ -750,22 +774,11 @@ public final class TvInteractiveAppManager { } /** - * Prepares TV Interactive App service environment for the given type. - */ - public void prepare(@NonNull String tvIAppServiceId, int type) { - try { - mService.prepare(tvIAppServiceId, type, mUserId); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** * Registers an Android application link info record which can be used to launch the specific * Android application by TV interactive App RTE. * * @param tvIAppServiceId The ID of TV interactive service which the command to be sent to. The - * ID can be found in {@link TvInputInfo#getId()}. + * ID can be found in {@link TvInteractiveAppServiceInfo#getId()}. * @param appLinkInfo The Android application link info record to be registered. */ public void registerAppLinkInfo( @@ -782,7 +795,7 @@ public final class TvInteractiveAppManager { * Android application by TV interactive App RTE. * * @param tvIAppServiceId The ID of TV interactive service which the command to be sent to. The - * ID can be found in {@link TvInputInfo#getId()}. + * ID can be found in {@link TvInteractiveAppServiceInfo#getId()}. * @param appLinkInfo The Android application link info record to be unregistered. */ public void unregisterAppLinkInfo( @@ -798,7 +811,7 @@ public final class TvInteractiveAppManager { * Sends app link command. * * @param tvIAppServiceId The ID of TV interactive service which the command to be sent to. The - * ID can be found in {@link TvInputInfo#getId()}. + * ID can be found in {@link TvInteractiveAppServiceInfo#getId()}. * @param command The command to be sent. */ public void sendAppLinkCommand(@NonNull String tvIAppServiceId, @NonNull Bundle command) { @@ -1022,6 +1035,18 @@ public final class TvInteractiveAppManager { } } + void sendSigningResult(@NonNull String signingId, @NonNull byte[] result) { + if (mToken == null) { + Log.w(TAG, "The session has been already released"); + return; + } + try { + mService.sendSigningResult(mToken, signingId, result, mUserId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Sets the {@link android.view.Surface} for this session. * @@ -1655,6 +1680,15 @@ public final class TvInteractiveAppManager { }); } + void postRequestSigning(String id, String algorithm, String alias, byte[] data) { + mHandler.post(new Runnable() { + @Override + public void run() { + mSessionCallback.onRequestSigning(mSession, id, algorithm, alias, data); + } + }); + } + void postAdRequest(final AdRequest request) { mHandler.post(new Runnable() { @Override @@ -1792,12 +1826,27 @@ public final class TvInteractiveAppManager { * called. * * @param session A {@link TvInteractiveAppService.Session} associated with this callback. - * @hide */ public void onRequestCurrentTvInputId(Session session) { } /** + * This is called when + * {@link TvInteractiveAppService.Session#requestSigning(String, String, String, byte[])} is + * called. + * + * @param session A {@link TvInteractiveAppService.Session} associated with this callback. + * @param signingId the ID to identify the request. + * @param algorithm the standard name of the signature algorithm requested, such as + * MD5withRSA, SHA256withDSA, etc. + * @param alias the alias of the corresponding {@link java.security.KeyStore}. + * @param data the original bytes to be signed. + */ + public void onRequestSigning( + Session session, String signingId, String algorithm, String alias, byte[] data) { + } + + /** * This is called when {@link TvInteractiveAppService.Session#notifySessionStateChanged} is * called. * diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java index d22fd83872e1..b103b1036303 100755 --- a/media/java/android/media/tv/interactive/TvInteractiveAppService.java +++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * 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. @@ -20,6 +20,7 @@ import android.annotation.CallSuper; import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.Px; import android.annotation.SdkConstant; import android.annotation.StringDef; import android.annotation.SuppressLint; @@ -217,11 +218,6 @@ public abstract class TvInteractiveAppService extends Service { } @Override - public void prepare(int type) { - onPrepare(type); - } - - @Override public void registerAppLinkInfo(AppLinkInfo appLinkInfo) { onRegisterAppLinkInfo(appLinkInfo); } @@ -240,11 +236,6 @@ public abstract class TvInteractiveAppService extends Service { } /** - * Prepares TV Interactive App service for the given type. - */ - public abstract void onPrepare(@TvInteractiveAppInfo.InteractiveAppType int type); - - /** * Called when a request to register an Android application link info record is received. */ public void onRegisterAppLinkInfo(@NonNull AppLinkInfo appLinkInfo) { @@ -277,7 +268,7 @@ public abstract class TvInteractiveAppService extends Service { @Nullable public abstract Session onCreateSession( @NonNull String iAppServiceId, - @TvInteractiveAppInfo.InteractiveAppType int type); + @TvInteractiveAppServiceInfo.InteractiveAppType int type); /** * Notifies the system when the state of the interactive app RTE has been changed. @@ -289,7 +280,7 @@ public abstract class TvInteractiveAppService extends Service { * {@link TvInteractiveAppManager#SERVICE_STATE_ERROR}. */ public final void notifyStateChanged( - @TvInteractiveAppInfo.InteractiveAppType int type, + @TvInteractiveAppServiceInfo.InteractiveAppType int type, @TvInteractiveAppManager.ServiceState int state, @TvInteractiveAppManager.ErrorCode int error) { SomeArgs args = SomeArgs.obtain(); @@ -373,6 +364,15 @@ public abstract class TvInteractiveAppService extends Service { } /** + * Returns {@code true} if media view is enabled, {@code false} otherwise. + * + * @see #setMediaViewEnabled(boolean) + */ + public boolean isMediaViewEnabled() { + return mMediaViewEnabled; + } + + /** * Starts TvInteractiveAppService session. */ public void onStartInteractiveApp() { @@ -397,9 +397,10 @@ public abstract class TvInteractiveAppService extends Service { * no matter if it's created successfully or not. * * @see #notifyBiInteractiveAppCreated(Uri, String) - * @see #onDestroyBiInteractiveApp(String) + * @see #onDestroyBiInteractiveAppRequest(String) */ - public void onCreateBiInteractiveApp(@NonNull Uri biIAppUri, @Nullable Bundle params) { + public void onCreateBiInteractiveAppRequest( + @NonNull Uri biIAppUri, @Nullable Bundle params) { } @@ -407,11 +408,11 @@ public abstract class TvInteractiveAppService extends Service { * Destroys broadcast-independent(BI) interactive application. * * @param biIAppId the BI interactive app ID from - * {@link #onCreateBiInteractiveApp(Uri, Bundle)}} + * {@link #onCreateBiInteractiveAppRequest(Uri, Bundle)} * - * @see #onCreateBiInteractiveApp(Uri, Bundle) + * @see #onCreateBiInteractiveAppRequest(Uri, Bundle) */ - public void onDestroyBiInteractiveApp(@NonNull String biIAppId) { + public void onDestroyBiInteractiveAppRequest(@NonNull String biIAppId) { } /** @@ -435,6 +436,8 @@ public abstract class TvInteractiveAppService extends Service { /** * Receives current stream volume. + * + * @param volume a volume value between {@code 0.0f} and {@code 1.0f}, inclusive. */ public void onStreamVolume(float volume) { } @@ -452,6 +455,17 @@ public abstract class TvInteractiveAppService extends Service { } /** + * Receives signing result. + * @param signingId the ID to identify the request. It's the same as the corresponding ID in + * {@link Session#requestSigning(String, String, String, byte[])} + * @param result the signed result. + * + * @see #requestSigning(String, String, String, byte[]) + */ + public void onSigningResult(@NonNull String signingId, @NonNull byte[] result) { + } + + /** * Called when the application sets the surface. * * <p>The TV Interactive App service should render interactive app UI onto the given @@ -484,10 +498,10 @@ public abstract class TvInteractiveAppService extends Service { * containing {@link TvInteractiveAppView}. Note that the size of the underlying surface can * be different if the surface was changed by calling {@link #layoutSurface}. * - * @param width The width of the media view. - * @param height The height of the media view. + * @param width The width of the media view, in pixels. + * @param height The height of the media view, in pixels. */ - public void onMediaViewSizeChanged(int width, int height) { + public void onMediaViewSizeChanged(@Px int width, @Px int height) { } /** @@ -877,6 +891,47 @@ public abstract class TvInteractiveAppService extends Service { } /** + * Requests signing of the given data. + * + * <p>This is used when the corresponding server of the broadcast-independent interactive + * app requires signing during handshaking, and the interactive app service doesn't have + * the built-in private key. The private key is provided by the content providers and + * pre-built in the related app, such as TV app. + * + * @param signingId the ID to identify the request. When a result is received, this ID can + * be used to correlate the result with the request. + * @param algorithm the standard name of the signature algorithm requested, such as + * MD5withRSA, SHA256withDSA, etc. The name is from standards like + * FIPS PUB 186-4 and PKCS #1. + * @param alias the alias of the corresponding {@link java.security.KeyStore}. + * @param data the original bytes to be signed. + * + * @see #onSigningResult(String, byte[]) + * @see TvInteractiveAppView#createBiInteractiveApp(Uri, Bundle) + * @see TvInteractiveAppView#BI_INTERACTIVE_APP_KEY_ALIAS + */ + @CallSuper + public void requestSigning(@NonNull String signingId, @NonNull String algorithm, + @NonNull String alias, @NonNull byte[] data) { + executeOrPostRunnableOnMainThread(new Runnable() { + @MainThread + @Override + public void run() { + try { + if (DEBUG) { + Log.d(TAG, "requestSigning"); + } + if (mSessionCallback != null) { + mSessionCallback.onRequestSigning(signingId, algorithm, alias, data); + } + } catch (RemoteException e) { + Log.w(TAG, "error in requestSigning", e); + } + } + }); + } + + /** * Sends an advertisement request to be processed by the related TV input. * * @param request The advertisement request @@ -914,11 +969,11 @@ public abstract class TvInteractiveAppService extends Service { } void createBiInteractiveApp(@NonNull Uri biIAppUri, @Nullable Bundle params) { - onCreateBiInteractiveApp(biIAppUri, params); + onCreateBiInteractiveAppRequest(biIAppUri, params); } void destroyBiInteractiveApp(@NonNull String biIAppId) { - onDestroyBiInteractiveApp(biIAppId); + onDestroyBiInteractiveAppRequest(biIAppId); } void setTeletextAppEnabled(boolean enable) { @@ -945,6 +1000,10 @@ public abstract class TvInteractiveAppService extends Service { onCurrentTvInputId(inputId); } + void sendSigningResult(String signingId, byte[] result) { + onSigningResult(signingId, result); + } + void release() { onRelease(); if (mSurface != null) { @@ -1074,7 +1133,7 @@ public abstract class TvInteractiveAppService extends Service { * @param biIAppId BI interactive app ID, which can be used to destroy the BI interactive * app. {@code null} if it's not created successfully. * - * @see #onCreateBiInteractiveApp(Uri, Bundle) + * @see #onCreateBiInteractiveAppRequest(Uri, Bundle) */ @CallSuper public final void notifyBiInteractiveAppCreated( @@ -1413,6 +1472,11 @@ public abstract class TvInteractiveAppService extends Service { } @Override + public void sendSigningResult(@NonNull String signingId, @NonNull byte[] result) { + mSessionImpl.sendSigningResult(signingId, result); + } + + @Override public void release() { mSessionImpl.scheduleMediaViewCleanup(); mSessionImpl.release(); diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppInfo.aidl b/media/java/android/media/tv/interactive/TvInteractiveAppServiceInfo.aidl index 5e1501677b3b..4b6127c01d1e 100644 --- a/media/java/android/media/tv/interactive/TvInteractiveAppInfo.aidl +++ b/media/java/android/media/tv/interactive/TvInteractiveAppServiceInfo.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * 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. @@ -16,4 +16,4 @@ package android.media.tv.interactive; -parcelable TvInteractiveAppInfo;
\ No newline at end of file +parcelable TvInteractiveAppServiceInfo;
\ No newline at end of file diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppInfo.java b/media/java/android/media/tv/interactive/TvInteractiveAppServiceInfo.java index 6103db001b19..3e0885214dcd 100644 --- a/media/java/android/media/tv/interactive/TvInteractiveAppInfo.java +++ b/media/java/android/media/tv/interactive/TvInteractiveAppServiceInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * 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. @@ -45,9 +45,9 @@ import java.util.List; /** * This class is used to specify meta information of a TV interactive app. */ -public final class TvInteractiveAppInfo implements Parcelable { +public final class TvInteractiveAppServiceInfo implements Parcelable { private static final boolean DEBUG = false; - private static final String TAG = "TvInteractiveAppInfo"; + private static final String TAG = "TvInteractiveAppServiceInfo"; private static final String XML_START_TAG_NAME = "tv-interactive-app"; @@ -71,7 +71,13 @@ public final class TvInteractiveAppInfo implements Parcelable { private final String mId; private int mTypes; - public TvInteractiveAppInfo(@NonNull Context context, @NonNull ComponentName component) { + /** + * Constructs a TvInteractiveAppServiceInfo object. + * + * @param context the application context + * @param component the component name of the TvInteractiveAppService + */ + public TvInteractiveAppServiceInfo(@NonNull Context context, @NonNull ComponentName component) { if (context == null) { throw new IllegalArgumentException("context cannot be null."); } @@ -94,28 +100,28 @@ public final class TvInteractiveAppInfo implements Parcelable { mId = id; mTypes = toTypesFlag(types); } - private TvInteractiveAppInfo(ResolveInfo service, String id, int types) { + private TvInteractiveAppServiceInfo(ResolveInfo service, String id, int types) { mService = service; mId = id; mTypes = types; } - private TvInteractiveAppInfo(@NonNull Parcel in) { + private TvInteractiveAppServiceInfo(@NonNull Parcel in) { mService = ResolveInfo.CREATOR.createFromParcel(in); mId = in.readString(); mTypes = in.readInt(); } - public static final @NonNull Creator<TvInteractiveAppInfo> CREATOR = - new Creator<TvInteractiveAppInfo>() { + public static final @NonNull Creator<TvInteractiveAppServiceInfo> CREATOR = + new Creator<TvInteractiveAppServiceInfo>() { @Override - public TvInteractiveAppInfo createFromParcel(Parcel in) { - return new TvInteractiveAppInfo(in); + public TvInteractiveAppServiceInfo createFromParcel(Parcel in) { + return new TvInteractiveAppServiceInfo(in); } @Override - public TvInteractiveAppInfo[] newArray(int size) { - return new TvInteractiveAppInfo[size]; + public TvInteractiveAppServiceInfo[] newArray(int size) { + return new TvInteractiveAppServiceInfo[size]; } }; diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppView.java b/media/java/android/media/tv/interactive/TvInteractiveAppView.java index 773e54f30744..1df757b2d9d7 100755 --- a/media/java/android/media/tv/interactive/TvInteractiveAppView.java +++ b/media/java/android/media/tv/interactive/TvInteractiveAppView.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * 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. @@ -46,6 +46,7 @@ import android.view.View; import android.view.ViewGroup; import android.view.ViewRootImpl; +import java.security.KeyStore; import java.util.List; import java.util.concurrent.Executor; @@ -61,6 +62,41 @@ public class TvInteractiveAppView extends ViewGroup { private static final int UNSET_TVVIEW_SUCCESS = 3; private static final int UNSET_TVVIEW_FAIL = 4; + /** + * Used to share client {@link java.security.cert.Certificate} with + * {@link TvInteractiveAppService}. + * @see #createBiInteractiveApp(Uri, Bundle) + * @see java.security.cert.Certificate + */ + public static final String BI_INTERACTIVE_APP_KEY_CERTIFICATE = "certificate"; + /** + * Used to share the {@link KeyStore} alias with {@link TvInteractiveAppService}. + * @see #createBiInteractiveApp(Uri, Bundle) + * @see KeyStore#aliases() + */ + public static final String BI_INTERACTIVE_APP_KEY_ALIAS = "alias"; + /** + * Used to share the {@link java.security.PrivateKey} with {@link TvInteractiveAppService}. + * <p>The private key is optional. It is used to encrypt data when necessary. + * + * @see #createBiInteractiveApp(Uri, Bundle) + * @see java.security.PrivateKey + */ + public static final String BI_INTERACTIVE_APP_KEY_PRIVATE_KEY = "private_key"; + /** + * Additional HTTP headers to be used by {@link TvInteractiveAppService} to load the + * broadcast-independent interactive application. + * @see #createBiInteractiveApp(Uri, Bundle) + */ + public static final String BI_INTERACTIVE_APP_KEY_HTTP_ADDITIONAL_HEADERS = + "http_additional_headers"; + /** + * HTTP user agent to be used by {@link TvInteractiveAppService} for broadcast-independent + * interactive application. + * @see #createBiInteractiveApp(Uri, Bundle) + */ + public static final String BI_INTERACTIVE_APP_KEY_HTTP_USER_AGENT = "http_user_agent"; + private final TvInteractiveAppManager mTvInteractiveAppManager; private final Handler mHandler = new Handler(); private final Object mCallbackLock = new Object(); @@ -148,12 +184,14 @@ public class TvInteractiveAppView extends ViewGroup { /** * Sets the callback to be invoked when an event is dispatched to this TvInteractiveAppView. * - * @param callback The callback to receive events. A value of {@code null} removes the existing - * callback. + * @param callback the callback to receive events. MUST NOT be {@code null}. + * + * @see #clearCallback() */ public void setCallback( @NonNull @CallbackExecutor Executor executor, @NonNull TvInteractiveAppCallback callback) { + com.android.internal.util.AnnotationValidations.validate(NonNull.class, null, callback); synchronized (mCallbackLock) { mCallbackExecutor = executor; mCallback = callback; @@ -162,6 +200,8 @@ public class TvInteractiveAppView extends ViewGroup { /** * Clears the callback. + * + * @see #setCallback(Executor, TvInteractiveAppCallback) */ public void clearCallback() { synchronized (mCallbackLock) { @@ -238,7 +278,9 @@ public class TvInteractiveAppView extends ViewGroup { } /** - * Resets this TvInteractiveAppView. + * Resets this TvInteractiveAppView to release its resources. + * + * <p>It can be reused by call {@link #prepareInteractiveApp(String, int)}. */ public void reset() { if (DEBUG) Log.d(TAG, "reset()"); @@ -364,6 +406,19 @@ public class TvInteractiveAppView extends ViewGroup { mOnUnhandledInputEventListener = listener; // TODO: handle CallbackExecutor } + + /** + * Gets the {@link OnUnhandledInputEventListener}. + * <p>Returns {@code null} if the listener is not set or is cleared. + * + * @see #setOnUnhandledInputEventListener(Executor, OnUnhandledInputEventListener) + * @see #clearOnUnhandledInputEventListener() + */ + @Nullable + public OnUnhandledInputEventListener getOnUnhandledInputEventListener() { + return mOnUnhandledInputEventListener; + } + /** * Clears the {@link OnUnhandledInputEventListener}. */ @@ -386,16 +441,17 @@ public class TvInteractiveAppView extends ViewGroup { } /** - * Prepares the interactive application. + * Prepares the interactive application runtime environment of corresponding + * {@link TvInteractiveAppService}. * * @param iAppServiceId the interactive app service ID, which can be found in - * {@link TvInteractiveAppInfo#getId()}. + * {@link TvInteractiveAppServiceInfo#getId()}. * * @see android.media.tv.interactive.TvInteractiveAppManager#getTvInteractiveAppServiceList() */ public void prepareInteractiveApp( @NonNull String iAppServiceId, - @TvInteractiveAppInfo.InteractiveAppType int type) { + @TvInteractiveAppServiceInfo.InteractiveAppType int type) { // TODO: document and handle the cases that this method is called multiple times. if (DEBUG) { Log.d(TAG, "prepareInteractiveApp"); @@ -432,6 +488,8 @@ public class TvInteractiveAppView extends ViewGroup { /** * Resets the interactive application. + * + * <p>This releases the resources of the corresponding {@link TvInteractiveAppService.Session}. */ public void resetInteractiveApp() { if (DEBUG) { @@ -471,6 +529,8 @@ public class TvInteractiveAppView extends ViewGroup { /** * Sends stream volume to related TV interactive app. + * + * @param volume a volume value between {@code 0.0f} and {@code 1.0f}, inclusive. */ public void sendStreamVolume(float volume) { if (DEBUG) { @@ -509,6 +569,27 @@ public class TvInteractiveAppView extends ViewGroup { } } + /** + * Sends signing result to related TV interactive app. + * + * <p>This is used when the corresponding server of the broadcast-independent interactive + * app requires signing during handshaking, and the interactive app service doesn't have + * the built-in private key. The private key is provided by the content providers and + * pre-built in the related app, such as TV app. + * + * @param signingId the ID to identify the request. It's the same as the corresponding ID in + * {@link TvInteractiveAppService.Session#requestSigning(String, String, String, byte[])} + * @param result the signed result. + */ + public void sendSigningResult(@NonNull String signingId, @NonNull byte[] result) { + if (DEBUG) { + Log.d(TAG, "sendSigningResult"); + } + if (mSession != null) { + mSession.sendSigningResult(signingId, result); + } + } + private void resetInternal() { mSessionCallback = null; if (mSession != null) { @@ -527,7 +608,14 @@ public class TvInteractiveAppView extends ViewGroup { * <p>{@link TvInteractiveAppCallback#onBiInteractiveAppCreated(String, Uri, String)} will be * called for the result. * + * @param biIAppUri URI associated this BI interactive app. + * @param params optional parameters for broadcast-independent interactive application, such as + * {@link #BI_INTERACTIVE_APP_KEY_CERTIFICATE}. + * * @see TvInteractiveAppCallback#onBiInteractiveAppCreated(String, Uri, String) + * @see #BI_INTERACTIVE_APP_KEY_CERTIFICATE + * @see #BI_INTERACTIVE_APP_KEY_HTTP_ADDITIONAL_HEADERS + * @see #BI_INTERACTIVE_APP_KEY_HTTP_USER_AGENT */ public void createBiInteractiveApp(@NonNull Uri biIAppUri, @Nullable Bundle params) { if (DEBUG) { @@ -721,6 +809,22 @@ public class TvInteractiveAppView extends ViewGroup { public void onRequestCurrentTvInputId(@NonNull String iAppServiceId) { } + /** + * This is called when + * {@link TvInteractiveAppService.Session#requestSigning(String, String, String, byte[])} is + * called. + * + * @param iAppServiceId The ID of the TV interactive app service bound to this view. + * @param signingId the ID to identify the request. + * @param algorithm the standard name of the signature algorithm requested, such as + * MD5withRSA, SHA256withDSA, etc. + * @param alias the alias of the corresponding {@link java.security.KeyStore}. + * @param data the original bytes to be signed. + */ + public void onRequestSigning(@NonNull String iAppServiceId, @NonNull String signingId, + @NonNull String algorithm, @NonNull String alias, @NonNull byte[] data) { + } + } /** @@ -1027,5 +1131,20 @@ public class TvInteractiveAppView extends ViewGroup { mCallback.onRequestCurrentTvInputId(mIAppServiceId); } } + + @Override + public void onRequestSigning( + Session session, String id, String algorithm, String alias, byte[] data) { + if (DEBUG) { + Log.d(TAG, "onRequestSigning"); + } + if (this != mSessionCallback) { + Log.w(TAG, "onRequestSigning - session not created"); + return; + } + if (mCallback != null) { + mCallback.onRequestSigning(mIAppServiceId, id, algorithm, alias, data); + } + } } } diff --git a/packages/CompanionDeviceManager/res/color/selector.xml b/packages/CompanionDeviceManager/res/color/selector.xml index 56e5dca0f72f..aebc5d5adf6e 100644 --- a/packages/CompanionDeviceManager/res/color/selector.xml +++ b/packages/CompanionDeviceManager/res/color/selector.xml @@ -1,3 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> <!-- ~ Copyright (C) 2022 The Android Open Source Project ~ diff --git a/packages/CompanionDeviceManager/res/drawable/btn_negative_multiple_devices.xml b/packages/CompanionDeviceManager/res/drawable/btn_negative_multiple_devices.xml index ece7bbafc654..ebe16a7a14e5 100644 --- a/packages/CompanionDeviceManager/res/drawable/btn_negative_multiple_devices.xml +++ b/packages/CompanionDeviceManager/res/drawable/btn_negative_multiple_devices.xml @@ -1,3 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> <!-- ~ Copyright (C) 2022 The Android Open Source Project ~ diff --git a/packages/CompanionDeviceManager/res/drawable/btn_negative_top.xml b/packages/CompanionDeviceManager/res/drawable/btn_negative_top.xml index 7df92bb145cb..3cd7929bdd83 100644 --- a/packages/CompanionDeviceManager/res/drawable/btn_negative_top.xml +++ b/packages/CompanionDeviceManager/res/drawable/btn_negative_top.xml @@ -1,3 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> <!-- ~ Copyright (C) 2022 The Android Open Source Project ~ @@ -17,6 +18,6 @@ <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <solid android:color="@android:color/system_accent1_100"/> - <corners android:topLeftRadius="12dp" android:topRightRadius="12dp" - android:bottomLeftRadius="4dp" android:bottomRightRadius="4dp"/> + <corners android:topLeftRadius="4dp" android:topRightRadius="4dp" + android:bottomLeftRadius="12dp" android:bottomRightRadius="12dp"/> </shape> diff --git a/packages/CompanionDeviceManager/res/drawable/btn_positive_bottom.xml b/packages/CompanionDeviceManager/res/drawable/btn_positive_bottom.xml index 55e96f6d7512..2cff4737cabf 100644 --- a/packages/CompanionDeviceManager/res/drawable/btn_positive_bottom.xml +++ b/packages/CompanionDeviceManager/res/drawable/btn_positive_bottom.xml @@ -1,3 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> <!-- ~ Copyright (C) 2022 The Android Open Source Project ~ @@ -17,6 +18,6 @@ <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <solid android:color="@android:color/system_accent1_100"/> - <corners android:topLeftRadius="4dp" android:topRightRadius="4dp" - android:bottomLeftRadius="12dp" android:bottomRightRadius="12dp"/> + <corners android:topLeftRadius="12dp" android:topRightRadius="12dp" + android:bottomLeftRadius="4dp" android:bottomRightRadius="4dp"/> </shape> diff --git a/packages/CompanionDeviceManager/res/drawable/helper_ok_button.xml b/packages/CompanionDeviceManager/res/drawable/helper_ok_button.xml index f9ec5d0dce55..8e92051faf6c 100644 --- a/packages/CompanionDeviceManager/res/drawable/helper_ok_button.xml +++ b/packages/CompanionDeviceManager/res/drawable/helper_ok_button.xml @@ -1,3 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> <!-- ~ Copyright (C) 2022 The Android Open Source Project ~ diff --git a/packages/CompanionDeviceManager/res/drawable/ic_apps.xml b/packages/CompanionDeviceManager/res/drawable/ic_apps.xml index 93a0cba769c6..d1ec8637775c 100644 --- a/packages/CompanionDeviceManager/res/drawable/ic_apps.xml +++ b/packages/CompanionDeviceManager/res/drawable/ic_apps.xml @@ -1,3 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> <!-- ~ Copyright (C) 2022 The Android Open Source Project ~ diff --git a/packages/CompanionDeviceManager/res/drawable/ic_device_other.xml b/packages/CompanionDeviceManager/res/drawable/ic_device_other.xml index f8515c33d57f..2a8eb24bcf1f 100644 --- a/packages/CompanionDeviceManager/res/drawable/ic_device_other.xml +++ b/packages/CompanionDeviceManager/res/drawable/ic_device_other.xml @@ -1,3 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> <!-- ~ Copyright (C) 2022 The Android Open Source Project ~ diff --git a/packages/CompanionDeviceManager/res/drawable/ic_notifications.xml b/packages/CompanionDeviceManager/res/drawable/ic_notifications.xml index 4ac4d04b184e..e5825bcbf70c 100644 --- a/packages/CompanionDeviceManager/res/drawable/ic_notifications.xml +++ b/packages/CompanionDeviceManager/res/drawable/ic_notifications.xml @@ -1,3 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> <!-- ~ Copyright (C) 2022 The Android Open Source Project ~ diff --git a/packages/CompanionDeviceManager/res/drawable/ic_storage.xml b/packages/CompanionDeviceManager/res/drawable/ic_storage.xml index d8b7f59185c8..406a3b5dada5 100644 --- a/packages/CompanionDeviceManager/res/drawable/ic_storage.xml +++ b/packages/CompanionDeviceManager/res/drawable/ic_storage.xml @@ -1,3 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> <!-- ~ Copyright (C) 2022 The Android Open Source Project ~ diff --git a/packages/CompanionDeviceManager/res/drawable/ic_watch.xml b/packages/CompanionDeviceManager/res/drawable/ic_watch.xml index 44a40b9fede3..d7a28d949997 100644 --- a/packages/CompanionDeviceManager/res/drawable/ic_watch.xml +++ b/packages/CompanionDeviceManager/res/drawable/ic_watch.xml @@ -1,3 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> <!-- ~ Copyright (C) 2022 The Android Open Source Project ~ diff --git a/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml index 8eec33aeb4ee..c37054e9e98d 100644 --- a/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml +++ b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml @@ -1,6 +1,5 @@ <?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 @@ -20,35 +19,26 @@ <!-- A header for selfManaged devices only. --> <include layout="@layout/vendor_header" /> + <!-- Do NOT change the ID of the root LinearLayout above: it's referenced in CTS tests. --> + <ImageView android:id="@+id/profile_icon" android:layout_width="match_parent" android:layout_height="32dp" android:gravity="center" - android:layout_marginBottom="12dp" - android:layout_marginTop="1dp" + android:layout_marginTop="18dp" android:tint="@android:color/system_accent1_600"/> - <!-- Do NOT change the ID of the root LinearLayout above: it's referenced in CTS tests. --> + <LinearLayout style="@style/Description"> + <TextView + android:id="@+id/title" + style="@style/DescriptionTitle" /> - <TextView - android:id="@+id/title" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center" - android:paddingHorizontal="12dp" - android:layout_marginBottom="12dp" - style="@*android:style/TextAppearance.Widget.Toolbar.Title" /> + <TextView + android:id="@+id/summary" + style="@style/DescriptionSummary" /> - <TextView - android:id="@+id/summary" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="12dp" - android:layout_marginBottom="12dp" - android:gravity="center" - android:textColor="?android:attr/textColorSecondary" - android:textSize="14sp" /> + </LinearLayout> <RelativeLayout android:layout_width="match_parent" @@ -59,18 +49,20 @@ android:id="@+id/multiple_device_list" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_marginTop="12dp" + android:layout_marginBottom="12dp" android:orientation="vertical" android:visibility="gone"> <View android:id="@+id/border_top" - android:layout_marginTop="12dp" style="@style/DeviceListBorder" /> <androidx.recyclerview.widget.RecyclerView android:id="@+id/device_list" android:layout_width="match_parent" android:scrollbars="vertical" + android:layout_marginBottom="12dp" android:layout_height="200dp" /> <View @@ -84,35 +76,56 @@ android:layout_width="match_parent" android:layout_height="wrap_content" /> + <ProgressBar + android:id="@+id/spinner" + android:layout_width="56dp" + android:layout_height="56dp" + android:layout_centerInParent="true" + android:indeterminate="true" + android:tint="@android:color/system_accent1_600" + android:visibility="gone" + style="?android:attr/progressBarStyleLarge" /> + </RelativeLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" + android:gravity="center" android:orientation="vertical" - android:layout_marginTop="24dp"> + android:layout_marginTop="16dp"> <!-- Do NOT change the IDs of the buttons: they are referenced in CTS tests. --> <Button + android:id="@+id/btn_positive" + style="@style/PositiveButton" + android:text="@string/consent_yes" /> + + <Button android:id="@+id/btn_negative" + android:layout_marginBottom="12dp" style="@style/NegativeButton" android:text="@string/consent_no" /> - <Button - android:id="@+id/btn_positive" - style="@style/PositiveButton" - android:text="@string/consent_yes" /> + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="bottom|right" + android:orientation="vertical" + android:layout_marginRight="16dp" + android:layout_marginBottom="16dp"> + + <!-- Do NOT change the IDs of the buttons: they are referenced in CTS tests. --> <Button android:id="@+id/btn_negative_multiple_devices" - android:layout_marginLeft="170dp" - android:layout_marginBottom="10dp" style="@style/NegativeButtonMultipleDevices" - android:textColor = "?android:textColorPrimary" + android:textColor="?android:textColorPrimary" android:visibility="gone" android:text="@string/consent_no" /> - </LinearLayout> </LinearLayout>
\ No newline at end of file diff --git a/packages/CompanionDeviceManager/res/layout/data_transfer_confirmation.xml b/packages/CompanionDeviceManager/res/layout/data_transfer_confirmation.xml index 7c508147e0ac..3d0849356281 100644 --- a/packages/CompanionDeviceManager/res/layout/data_transfer_confirmation.xml +++ b/packages/CompanionDeviceManager/res/layout/data_transfer_confirmation.xml @@ -15,54 +15,42 @@ ~ limitations under the License. --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/activity_confirmation" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="@drawable/dialog_background" - android:elevation="16dp" - android:maxHeight="400dp" - android:orientation="vertical" - android:padding="18dp" - android:layout_gravity="center"> + android:id="@+id/data_transfer_confirmation" + style="@style/ContainerLayout"> <!-- Do NOT change the ID of the root LinearLayout above: it's referenced in CTS tests. --> - <TextView - android:id="@+id/title" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center" - android:paddingHorizontal="12dp" - style="@*android:style/TextAppearance.Widget.Toolbar.Title"/> + <LinearLayout style="@style/Description"> + <TextView + android:id="@+id/title" + style="@style/DescriptionTitle" /> - <TextView - android:id="@+id/summary" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="12dp" - android:layout_marginBottom="12dp" - android:gravity="center" - android:textColor="?android:attr/textColorSecondary" - android:textSize="14sp" /> + <TextView + android:id="@+id/summary" + style="@style/DescriptionSummary" /> + + </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" + android:gravity="center" android:orientation="vertical" - android:layout_marginTop="24dp"> + android:layout_marginTop="16dp"> <!-- Do NOT change the IDs of the buttons: they are referenced in CTS tests. --> <Button - android:id="@+id/btn_negative" - style="@style/NegativeButton" - android:text="@string/consent_no" /> - - <Button android:id="@+id/btn_positive" style="@style/PositiveButton" android:text="@string/consent_yes" /> + <Button + android:id="@+id/btn_negative" + android:layout_marginBottom="12dp" + style="@style/NegativeButton" + android:text="@string/consent_no" /> + </LinearLayout> </LinearLayout>
\ No newline at end of file diff --git a/packages/CompanionDeviceManager/res/layout/helper_confirmation.xml b/packages/CompanionDeviceManager/res/layout/helper_confirmation.xml index c177039891d2..a22ca941c5eb 100644 --- a/packages/CompanionDeviceManager/res/layout/helper_confirmation.xml +++ b/packages/CompanionDeviceManager/res/layout/helper_confirmation.xml @@ -1,3 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> <!-- ~ Copyright (C) 2022 The Android Open Source Project ~ @@ -17,6 +18,7 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/helper_confirmation" android:theme="@style/ChooserActivity" + android:padding="12dp" style="@style/ContainerLayout"> <ImageView @@ -24,8 +26,8 @@ android:layout_width="match_parent" android:layout_height="32dp" android:gravity="center" - android:layout_marginBottom="12dp" - android:layout_marginTop="1dp"/> + android:layout_marginTop="12dp" + android:layout_marginBottom="12dp"/> <TextView android:id="@+id/helper_title" @@ -33,17 +35,18 @@ android:layout_height="wrap_content" android:gravity="center" android:paddingHorizontal="12dp" - style="@*android:style/TextAppearance.Widget.Toolbar.Title" - android:textSize="20sp" /> + android:textColor="?android:attr/textColorPrimary" + android:textSize="22sp" /> <TextView android:id="@+id/helper_summary" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_marginLeft="24dp" + android:layout_marginRight="24dp" android:layout_marginTop="12dp" - android:layout_marginLeft="20dp" android:layout_marginBottom="24dp" - android:gravity="start" + android:gravity="center" android:textColor="?android:attr/textColorSecondary" android:textSize="14sp" /> @@ -51,6 +54,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" + android:layout_marginRight="12dp" + android:layout_marginBottom="12dp" android:gravity="end"> <Button diff --git a/packages/CompanionDeviceManager/res/layout/list_item_device.xml b/packages/CompanionDeviceManager/res/layout/list_item_device.xml index 3c8a81f4fe83..eeb988f364b2 100644 --- a/packages/CompanionDeviceManager/res/layout/list_item_device.xml +++ b/packages/CompanionDeviceManager/res/layout/list_item_device.xml @@ -28,6 +28,7 @@ android:id="@android:id/icon" android:layout_width="24dp" android:layout_height="24dp" + android:layout_marginLeft="24dp" android:layout_marginRight="12dp" android:tint="@android:color/system_accent1_600"/> diff --git a/packages/CompanionDeviceManager/res/layout/list_item_permission.xml b/packages/CompanionDeviceManager/res/layout/list_item_permission.xml index 79aa4e7e8927..3dce38d0dc20 100644 --- a/packages/CompanionDeviceManager/res/layout/list_item_permission.xml +++ b/packages/CompanionDeviceManager/res/layout/list_item_permission.xml @@ -1,3 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> <!-- ~ Copyright (C) 2022 The Android Open Source Project ~ @@ -19,13 +20,15 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" - android:padding="5dp"> + android:paddingLeft="32dp" + android:paddingRight="32dp" + android:paddingBottom="14dp"> <ImageView android:id="@+id/permission_icon" android:layout_width="24dp" android:layout_height="24dp" - android:layout_marginTop="7dp" + android:layout_marginTop="8dp" android:layout_marginEnd="12dp" android:tint="@android:color/system_accent1_600" android:contentDescription="Permission Icon"/> @@ -48,6 +51,7 @@ android:id="@+id/permission_summary" android:layout_width="match_parent" android:layout_height="wrap_content" + android:paddingRight="24dp" android:textSize="14sp" android:textColor="?android:attr/textColorSecondary"/> diff --git a/packages/CompanionDeviceManager/res/layout/vendor_header.xml b/packages/CompanionDeviceManager/res/layout/vendor_header.xml index d04eadfb62f4..5f6aa9419c6e 100644 --- a/packages/CompanionDeviceManager/res/layout/vendor_header.xml +++ b/packages/CompanionDeviceManager/res/layout/vendor_header.xml @@ -1,3 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> <!-- ~ Copyright (C) 2022 The Android Open Source Project ~ @@ -21,7 +22,10 @@ android:layout_height="wrap_content" android:orientation="horizontal" android:layout_gravity="center" - android:layout_marginBottom="16dp" + android:paddingTop="24dp" + android:paddingBottom="4dp" + android:paddingLeft="24dp" + android:paddingRight="24dp" android:visibility="gone" > <ImageView @@ -42,7 +46,6 @@ style="?android:attr/actionOverflowButtonStyle" android:layout_width="31dp" android:layout_height="32dp" - android:layout_marginLeft="100dp" android:layout_alignParentRight="true" /> </RelativeLayout>
\ No newline at end of file diff --git a/packages/CompanionDeviceManager/res/values/styles.xml b/packages/CompanionDeviceManager/res/values/styles.xml index 6eaffd4c3cfe..a55f30c9be6a 100644 --- a/packages/CompanionDeviceManager/res/values/styles.xml +++ b/packages/CompanionDeviceManager/res/values/styles.xml @@ -1,3 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> <!-- ~ Copyright (C) 2022 The Android Open Source Project ~ @@ -16,51 +17,86 @@ <resources> <style name="ContainerLayout"> - <item name="android:padding">18dp</item> - <item name="android:elevation">16dp</item> - <item name="android:maxHeight">400dp</item> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> <item name="android:orientation">vertical</item> <item name="android:layout_gravity">center</item> + <item name="android:minWidth">340dp</item> + <item name="android:background">@drawable/dialog_background</item> + </style> + + <style name="Description"> <item name="android:layout_width">match_parent</item> <item name="android:layout_height">wrap_content</item> - <item name="android:background">@drawable/dialog_background</item> + <item name="android:orientation">vertical</item> + <item name="android:layout_marginTop">18dp</item> + <item name="android:layout_marginBottom">18dp</item> + <item name="android:layout_marginLeft">24dp</item> + <item name="android:layout_marginRight">24dp</item> + </style> + + <style name="DescriptionTitle" + parent="@*android:style/TextAppearance.Widget.Toolbar.Title"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:gravity">center</item> + <item name="android:layout_marginLeft">14dp</item> + <item name="android:layout_marginRight">14dp</item> + <item name="android:textSize">20sp</item> + <item name="android:textColor">?android:attr/textColorPrimary</item> + </style> + + <style name="DescriptionSummary"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:gravity">center</item> + <item name="android:layout_marginTop">18dp</item> + <item name="android:layout_marginLeft">18dp</item> + <item name="android:layout_marginRight">18dp</item> + <item name="android:textSize">14sp</item> + <item name="android:textColor">?android:attr/textColorSecondary</item> </style> <style name="VendorHelperOkButton" parent="@android:style/Widget.Material.Button.Borderless.Colored"> <item name="android:layout_width">50dp</item> - <item name="android:layout_height">35dp</item> + <item name="android:layout_height">36dp</item> <item name="android:layout_marginTop">20dp</item> <item name="android:textColor">@android:color/system_neutral1_900</item> <item name="android:background">@drawable/helper_ok_button</item> </style> - <style name="NegativeButton" + <style name="PositiveButton" parent="@android:style/Widget.Material.Button.Borderless.Colored"> - <item name="android:layout_width">match_parent</item> - <item name="android:layout_height">wrap_content</item> + <item name="android:layout_width">300dp</item> + <item name="android:layout_height">56dp</item> + <item name="android:layout_marginLeft">24dp</item> + <item name="android:layout_marginRight">24dp</item> + <item name="android:layout_marginBottom">2dp</item> <item name="android:textAllCaps">false</item> <item name="android:textSize">14sp</item> <item name="android:textColor">@android:color/system_neutral1_900</item> - <item name="android:background">@drawable/btn_negative_top</item> + <item name="android:background">@drawable/btn_positive_bottom</item> </style> - <style name="PositiveButton" + <style name="NegativeButton" parent="@android:style/Widget.Material.Button.Borderless.Colored"> - <item name="android:layout_width">match_parent</item> - <item name="android:layout_height">wrap_content</item> + <item name="android:layout_width">300dp</item> + <item name="android:layout_height">56dp</item> + <item name="android:layout_marginLeft">24dp</item> + <item name="android:layout_marginRight">24dp</item> + <item name="android:layout_marginTop">2dp</item> <item name="android:textAllCaps">false</item> <item name="android:textSize">14sp</item> <item name="android:textColor">@android:color/system_neutral1_900</item> <item name="android:layout_marginTop">4dp</item> - <item name="android:background">@drawable/btn_positive_bottom</item> + <item name="android:background">@drawable/btn_negative_top</item> </style> <style name="NegativeButtonMultipleDevices" parent="@android:style/Widget.Material.Button.Colored"> <item name="android:layout_width">100dp</item> - <item name="android:layout_height">35dp</item> - <item name="android:layout_marginTop">20dp</item> + <item name="android:layout_height">36dp</item> <item name="android:textAllCaps">false</item> <item name="android:background">@drawable/btn_negative_multiple_devices</item> </style> diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java index f752b697c4a5..0bdf65d8ef55 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java @@ -57,6 +57,7 @@ import android.widget.Button; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; +import android.widget.ProgressBar; import android.widget.RelativeLayout; import android.widget.TextView; @@ -116,14 +117,14 @@ public class CompanionDeviceActivity extends FragmentActivity implements private ImageButton mVendorHeaderButton; // Progress indicator is only shown while we are looking for the first suitable device for a - // "regular" (ie. not self-managed) association. - private View mProgressIndicator; + // multiple device association. + private ProgressBar mProgressIndicator; // Present for self-managed association requests and "single-device" regular association // regular. private Button mButtonAllow; private Button mButtonNotAllow; - // Present for multiple device association requests only. + // Present for multiple devices' association requests only. private Button mButtonNotAllowMultipleDevices; private LinearLayout mAssociationConfirmationDialog; @@ -263,6 +264,8 @@ public class CompanionDeviceActivity extends FragmentActivity implements mVendorHeaderButton = findViewById(R.id.vendor_header_button); mDeviceListRecyclerView = findViewById(R.id.device_list); + + mProgressIndicator = findViewById(R.id.spinner); mDeviceAdapter = new DeviceListAdapter(this, this::onListItemClick); mPermissionListRecyclerView = findViewById(R.id.permission_list); @@ -502,21 +505,25 @@ public class CompanionDeviceActivity extends FragmentActivity implements mSummary.setText(summary); mProfileIcon.setImageDrawable(profileIcon); - mDeviceAdapter = new DeviceListAdapter(this, this::onListItemClick); - - // TODO: hide the list and show a spinner until a first device matching device is found. mDeviceListRecyclerView.setAdapter(mDeviceAdapter); mDeviceListRecyclerView.setLayoutManager(new LinearLayoutManager(this)); - CompanionDeviceDiscoveryService.getScanResult().observe( - /* lifecycleOwner */ this, - /* observer */ mDeviceAdapter); + CompanionDeviceDiscoveryService.getScanResult().observe(this, + deviceFilterPairs -> { + // Dismiss the progress bar once there's one device found for multiple devices. + if (deviceFilterPairs.size() >= 1) { + mProgressIndicator.setVisibility(View.GONE); + } + + mDeviceAdapter.setDevices(deviceFilterPairs); + }); // "Remove" consent button: users would need to click on the list item. mButtonAllow.setVisibility(View.GONE); mButtonNotAllow.setVisibility(View.GONE); mButtonNotAllowMultipleDevices.setVisibility(View.VISIBLE); mMultipleDeviceList.setVisibility(View.VISIBLE); + mProgressIndicator.setVisibility(View.VISIBLE); } private void onListItemClick(int position) { @@ -564,7 +571,7 @@ public class CompanionDeviceActivity extends FragmentActivity implements CompanionVendorHelperDialogFragment.newInstance(mRequest.getPackageName(), mRequest.getUserId(), mRequest.getDeviceProfile()); - mAssociationConfirmationDialog.setVisibility(View.GONE); + mAssociationConfirmationDialog.setVisibility(View.INVISIBLE); fragmentDialog.show(fragmentManager, /* Tag */ FRAGMENT_DIALOG_TAG); } diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java index 5f07fcfd8565..e8a1a5cc1916 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java @@ -150,6 +150,8 @@ public class CompanionDeviceDiscoveryService extends Service { mBtAdapter = mBtManager.getAdapter(); mBleScanner = mBtAdapter.getBluetoothLeScanner(); mWifiManager = getSystemService(WifiManager.class); + + sScanResultsLiveData.setValue(Collections.emptyList()); } @Override @@ -186,7 +188,6 @@ public class CompanionDeviceDiscoveryService extends Service { mStopAfterFirstMatch = request.isSingleDevice(); mDiscoveryStarted = true; sStateLiveData.setValue(DiscoveryState.DISCOVERY_IN_PROGRESS); - sScanResultsLiveData.setValue(Collections.emptyList()); final List<DeviceFilter<?>> allFilters = request.getDeviceFilters(); final List<BluetoothDeviceFilter> btFilters = diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java index 8babd3ade1eb..328c67ebb216 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java @@ -25,15 +25,13 @@ import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; -import androidx.lifecycle.Observer; import androidx.recyclerview.widget.RecyclerView; import java.util.List; /** * Adapter for the list of "found" devices. */ -class DeviceListAdapter extends RecyclerView.Adapter<DeviceListAdapter.ViewHolder> implements - Observer<List<DeviceFilterPair<?>>> { +class DeviceListAdapter extends RecyclerView.Adapter<DeviceListAdapter.ViewHolder> { public int mSelectedPosition = RecyclerView.NO_POSITION; private final Context mContext; @@ -96,9 +94,8 @@ class DeviceListAdapter extends RecyclerView.Adapter<DeviceListAdapter.ViewHolde mSelectedPosition = position; } - @Override - public void onChanged(List<DeviceFilterPair<?>> deviceFilterPairs) { - mDevices = deviceFilterPairs; + void setDevices(List<DeviceFilterPair<?>> devices) { + mDevices = devices; notifyDataSetChanged(); } diff --git a/packages/ConnectivityT/framework-t/Android.bp b/packages/ConnectivityT/framework-t/Android.bp index 217a1f67c336..bc278528570f 100644 --- a/packages/ConnectivityT/framework-t/Android.bp +++ b/packages/ConnectivityT/framework-t/Android.bp @@ -131,8 +131,8 @@ filegroup { "src/android/net/EthernetNetworkUpdateRequest.java", "src/android/net/EthernetNetworkUpdateRequest.aidl", "src/android/net/IEthernetManager.aidl", - "src/android/net/IEthernetNetworkManagementListener.aidl", "src/android/net/IEthernetServiceListener.aidl", + "src/android/net/INetworkInterfaceOutcomeReceiver.aidl", "src/android/net/ITetheredInterfaceCallback.aidl", ], path: "src", diff --git a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java index 2b6570a6ecb0..74fe4bd46cde 100644 --- a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java +++ b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java @@ -17,6 +17,7 @@ package android.app.usage; import android.annotation.IntDef; +import android.annotation.Nullable; import android.content.Context; import android.net.INetworkStatsService; import android.net.INetworkStatsSession; @@ -474,10 +475,11 @@ public final class NetworkStats implements AutoCloseable { /** * Fills the recycled bucket with data of the next bin in the enumeration. - * @param bucketOut Bucket to be filled with data. + * @param bucketOut Bucket to be filled with data. If null, the method does + * nothing and returning false. * @return true if successfully filled the bucket, false otherwise. */ - public boolean getNextBucket(Bucket bucketOut) { + public boolean getNextBucket(@Nullable Bucket bucketOut) { if (mSummary != null) { return getNextSummaryBucket(bucketOut); } else { @@ -651,7 +653,7 @@ public final class NetworkStats implements AutoCloseable { * @param bucketOut Next item will be set here. * @return true if a next item could be set. */ - private boolean getNextSummaryBucket(Bucket bucketOut) { + private boolean getNextSummaryBucket(@Nullable Bucket bucketOut) { if (bucketOut != null && mEnumerationIndex < mSummary.size()) { mRecycledSummaryEntry = mSummary.getValues(mEnumerationIndex++, mRecycledSummaryEntry); fillBucketFromSummaryEntry(bucketOut); @@ -678,7 +680,7 @@ public final class NetworkStats implements AutoCloseable { * @param bucketOut Next item will be set here. * @return true if a next item could be set. */ - private boolean getNextHistoryBucket(Bucket bucketOut) { + private boolean getNextHistoryBucket(@Nullable Bucket bucketOut) { if (bucketOut != null && mHistory != null) { if (mEnumerationIndex < mHistory.size()) { mRecycledHistoryEntry = mHistory.getValues(mEnumerationIndex++, diff --git a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java index bf518b2d5a29..f41475bea190 100644 --- a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java +++ b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java @@ -290,7 +290,7 @@ public class NetworkStatsManager { * statistics collection. */ @WorkerThread - public Bucket querySummaryForDevice(int networkType, String subscriberId, + public Bucket querySummaryForDevice(int networkType, @Nullable String subscriberId, long startTime, long endTime) throws SecurityException, RemoteException { NetworkTemplate template; try { @@ -335,8 +335,8 @@ public class NetworkStatsManager { * statistics collection. */ @WorkerThread - public Bucket querySummaryForUser(int networkType, String subscriberId, long startTime, - long endTime) throws SecurityException, RemoteException { + public Bucket querySummaryForUser(int networkType, @Nullable String subscriberId, + long startTime, long endTime) throws SecurityException, RemoteException { NetworkTemplate template; try { template = createTemplate(networkType, subscriberId); @@ -384,7 +384,7 @@ public class NetworkStatsManager { * statistics collection. */ @WorkerThread - public NetworkStats querySummary(int networkType, String subscriberId, long startTime, + public NetworkStats querySummary(int networkType, @Nullable String subscriberId, long startTime, long endTime) throws SecurityException, RemoteException { NetworkTemplate template; try { @@ -508,15 +508,17 @@ public class NetworkStatsManager { * * @see #queryDetailsForUidTagState(int, String, long, long, int, int, int) */ + @NonNull @WorkerThread - public NetworkStats queryDetailsForUid(int networkType, String subscriberId, + public NetworkStats queryDetailsForUid(int networkType, @Nullable String subscriberId, long startTime, long endTime, int uid) throws SecurityException { return queryDetailsForUidTagState(networkType, subscriberId, startTime, endTime, uid, NetworkStats.Bucket.TAG_NONE, NetworkStats.Bucket.STATE_ALL); } /** @hide */ - public NetworkStats queryDetailsForUid(NetworkTemplate template, + @NonNull + public NetworkStats queryDetailsForUid(@NonNull NetworkTemplate template, long startTime, long endTime, int uid) throws SecurityException { return queryDetailsForUidTagState(template, startTime, endTime, uid, NetworkStats.Bucket.TAG_NONE, NetworkStats.Bucket.STATE_ALL); @@ -524,23 +526,59 @@ public class NetworkStatsManager { /** * Query network usage statistics details for a given uid and tag. + * + * This may take a long time, and apps should avoid calling this on their main thread. + * Only usable for uids belonging to calling user. Result is not aggregated over time. + * This means buckets' start and end timestamps are going to be between 'startTime' and + * 'endTime' parameters. The uid is going to be the same as the 'uid' parameter, the tag + * the same as the 'tag' parameter, and the state the same as the 'state' parameter. + * defaultNetwork is going to be {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL}, + * metered is going to be {@link NetworkStats.Bucket#METERED_ALL}, and + * roaming is going to be {@link NetworkStats.Bucket#ROAMING_ALL}. + * <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't + * interpolate across partial buckets. Since bucket length is in the order of hours, this + * method cannot be used to measure data usage on a fine grained time scale. * This may take a long time, and apps should avoid calling this on their main thread. * - * @see #queryDetailsForUidTagState(int, String, long, long, int, int, int) + * @param networkType As defined in {@link ConnectivityManager}, e.g. + * {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI} + * etc. + * @param subscriberId If applicable, the subscriber id of the network interface. + * <p>Starting with API level 29, the {@code subscriberId} is guarded by + * additional restrictions. Calling apps that do not meet the new + * requirements to access the {@code subscriberId} can provide a {@code + * null} value when querying for the mobile network type to receive usage + * for all mobile networks. For additional details see {@link + * TelephonyManager#getSubscriberId()}. + * <p>Starting with API level 31, calling apps can provide a + * {@code subscriberId} with wifi network type to receive usage for + * wifi networks which is under the given subscription if applicable. + * Otherwise, pass {@code null} when querying all wifi networks. + * @param startTime Start of period. Defined in terms of "Unix time", see + * {@link java.lang.System#currentTimeMillis}. + * @param endTime End of period. Defined in terms of "Unix time", see + * {@link java.lang.System#currentTimeMillis}. + * @param uid UID of app + * @param tag TAG of interest. Use {@link NetworkStats.Bucket#TAG_NONE} for aggregated data + * across all the tags. + * @return Statistics which is described above. + * @throws SecurityException if permissions are insufficient to read network statistics. */ + @NonNull @WorkerThread - public NetworkStats queryDetailsForUidTag(int networkType, String subscriberId, + public NetworkStats queryDetailsForUidTag(int networkType, @Nullable String subscriberId, long startTime, long endTime, int uid, int tag) throws SecurityException { return queryDetailsForUidTagState(networkType, subscriberId, startTime, endTime, uid, tag, NetworkStats.Bucket.STATE_ALL); } /** - * Query network usage statistics details for a given uid, tag, and state. Only usable for uids - * belonging to calling user. Result is not aggregated over time. This means buckets' start and - * end timestamps are going to be between 'startTime' and 'endTime' parameters. The uid is going - * to be the same as the 'uid' parameter, the tag the same as the 'tag' parameter, and the state - * the same as the 'state' parameter. + * Query network usage statistics details for a given uid, tag, and state. + * + * Only usable for uids belonging to calling user. Result is not aggregated over time. + * This means buckets' start and end timestamps are going to be between 'startTime' and + * 'endTime' parameters. The uid is going to be the same as the 'uid' parameter, the tag + * the same as the 'tag' parameter, and the state the same as the 'state' parameter. * defaultNetwork is going to be {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL}, * metered is going to be {@link NetworkStats.Bucket#METERED_ALL}, and * roaming is going to be {@link NetworkStats.Bucket#ROAMING_ALL}. @@ -572,11 +610,12 @@ public class NetworkStatsManager { * across all the tags. * @param state state of interest. Use {@link NetworkStats.Bucket#STATE_ALL} to aggregate * traffic from all states. - * @return Statistics object or null if an error happened during statistics collection. + * @return Statistics which is described above. * @throws SecurityException if permissions are insufficient to read network statistics. */ + @NonNull @WorkerThread - public NetworkStats queryDetailsForUidTagState(int networkType, String subscriberId, + public NetworkStats queryDetailsForUidTagState(int networkType, @Nullable String subscriberId, long startTime, long endTime, int uid, int tag, int state) throws SecurityException { NetworkTemplate template; template = createTemplate(networkType, subscriberId); @@ -669,7 +708,7 @@ public class NetworkStatsManager { * statistics collection. */ @WorkerThread - public NetworkStats queryDetails(int networkType, String subscriberId, long startTime, + public NetworkStats queryDetails(int networkType, @Nullable String subscriberId, long startTime, long endTime) throws SecurityException, RemoteException { NetworkTemplate template; try { @@ -698,7 +737,7 @@ public class NetworkStatsManager { * * @hide */ - @SystemApi + @SystemApi(client = MODULE_LIBRARIES) @RequiresPermission(anyOf = { NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) @@ -724,7 +763,7 @@ public class NetworkStatsManager { * * @hide */ - @SystemApi + @SystemApi(client = MODULE_LIBRARIES) @RequiresPermission(anyOf = { NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) @@ -785,10 +824,28 @@ public class NetworkStatsManager { /** * Registers to receive notifications about data usage on specified networks. * - * @see #registerUsageCallback(int, String, long, UsageCallback, Handler) + * <p>The callbacks will continue to be called as long as the process is live or + * {@link #unregisterUsageCallback} is called. + * + * @param networkType Type of network to monitor. Either + {@link ConnectivityManager#TYPE_MOBILE} or {@link ConnectivityManager#TYPE_WIFI}. + * @param subscriberId If applicable, the subscriber id of the network interface. + * <p>Starting with API level 29, the {@code subscriberId} is guarded by + * additional restrictions. Calling apps that do not meet the new + * requirements to access the {@code subscriberId} can provide a {@code + * null} value when registering for the mobile network type to receive + * notifications for all mobile networks. For additional details see {@link + * TelephonyManager#getSubscriberId()}. + * <p>Starting with API level 31, calling apps can provide a + * {@code subscriberId} with wifi network type to receive usage for + * wifi networks which is under the given subscription if applicable. + * Otherwise, pass {@code null} when querying all wifi networks. + * @param thresholdBytes Threshold in bytes to be notified on. + * @param callback The {@link UsageCallback} that the system will call when data usage + * has exceeded the specified threshold. */ - public void registerUsageCallback(int networkType, String subscriberId, long thresholdBytes, - UsageCallback callback) { + public void registerUsageCallback(int networkType, @Nullable String subscriberId, + long thresholdBytes, @NonNull UsageCallback callback) { registerUsageCallback(networkType, subscriberId, thresholdBytes, callback, null /* handler */); } @@ -818,8 +875,8 @@ public class NetworkStatsManager { * @param handler to dispatch callback events through, otherwise if {@code null} it uses * the calling thread. */ - public void registerUsageCallback(int networkType, String subscriberId, long thresholdBytes, - UsageCallback callback, @Nullable Handler handler) { + public void registerUsageCallback(int networkType, @Nullable String subscriberId, + long thresholdBytes, @NonNull UsageCallback callback, @Nullable Handler handler) { NetworkTemplate template = createTemplate(networkType, subscriberId); if (DBG) { Log.d(TAG, "registerUsageCallback called with: {" @@ -839,7 +896,7 @@ public class NetworkStatsManager { * * @param callback The {@link UsageCallback} used when registering. */ - public void unregisterUsageCallback(UsageCallback callback) { + public void unregisterUsageCallback(@NonNull UsageCallback callback) { if (callback == null || callback.request == null || callback.request.requestId == DataUsageRequest.REQUEST_ID_UNSET) { throw new IllegalArgumentException("Invalid UsageCallback"); @@ -880,7 +937,7 @@ public class NetworkStatsManager { /** * Called when data usage has reached the given threshold. */ - public abstract void onThresholdReached(int networkType, String subscriberId); + public abstract void onThresholdReached(int networkType, @Nullable String subscriberId); /** * @hide used for internal bookkeeping @@ -924,7 +981,7 @@ public class NetworkStatsManager { @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_STATS_PROVIDER, NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) - @NonNull public void registerNetworkStatsProvider( + public void registerNetworkStatsProvider( @NonNull String tag, @NonNull NetworkStatsProvider provider) { try { @@ -950,7 +1007,7 @@ public class NetworkStatsManager { @RequiresPermission(anyOf = { android.Manifest.permission.NETWORK_STATS_PROVIDER, NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) - @NonNull public void unregisterNetworkStatsProvider(@NonNull NetworkStatsProvider provider) { + public void unregisterNetworkStatsProvider(@NonNull NetworkStatsProvider provider) { try { provider.getProviderCallbackBinderOrThrow().unregister(); } catch (RemoteException e) { @@ -958,7 +1015,7 @@ public class NetworkStatsManager { } } - private static NetworkTemplate createTemplate(int networkType, String subscriberId) { + private static NetworkTemplate createTemplate(int networkType, @Nullable String subscriberId) { final NetworkTemplate template; switch (networkType) { case ConnectivityManager.TYPE_MOBILE: diff --git a/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java b/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java index 793f28d5aa59..e02ea897dbe6 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java +++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java @@ -30,6 +30,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.pm.PackageManager; import android.os.Build; +import android.os.OutcomeReceiver; import android.os.RemoteException; import com.android.internal.annotations.GuardedBy; @@ -38,9 +39,10 @@ import com.android.modules.utils.BackgroundThread; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.List; import java.util.Objects; import java.util.concurrent.Executor; -import java.util.function.BiConsumer; +import java.util.function.IntConsumer; /** * A class that manages and configures Ethernet interfaces. @@ -53,15 +55,31 @@ public class EthernetManager { private static final String TAG = "EthernetManager"; private final IEthernetManager mService; - @GuardedBy("mListeners") - private final ArrayList<ListenerInfo> mListeners = new ArrayList<>(); + @GuardedBy("mListenerLock") + private final ArrayList<ListenerInfo<InterfaceStateListener>> mIfaceListeners = + new ArrayList<>(); + @GuardedBy("mListenerLock") + private final ArrayList<ListenerInfo<IntConsumer>> mEthernetStateListeners = + new ArrayList<>(); + final Object mListenerLock = new Object(); private final IEthernetServiceListener.Stub mServiceListener = new IEthernetServiceListener.Stub() { @Override + public void onEthernetStateChanged(int state) { + synchronized (mListenerLock) { + for (ListenerInfo<IntConsumer> li : mEthernetStateListeners) { + li.executor.execute(() -> { + li.listener.accept(state); + }); + } + } + } + + @Override public void onInterfaceStateChanged(String iface, int state, int role, IpConfiguration configuration) { - synchronized (mListeners) { - for (ListenerInfo li : mListeners) { + synchronized (mListenerLock) { + for (ListenerInfo<InterfaceStateListener> li : mIfaceListeners) { li.executor.execute(() -> li.listener.onInterfaceStateChanged(iface, state, role, configuration)); @@ -70,13 +88,29 @@ public class EthernetManager { } }; - private static class ListenerInfo { + /** + * Indicates that Ethernet is disabled. + * + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + public static final int ETHERNET_STATE_DISABLED = 0; + + /** + * Indicates that Ethernet is enabled. + * + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + public static final int ETHERNET_STATE_ENABLED = 1; + + private static class ListenerInfo<T> { @NonNull public final Executor executor; @NonNull - public final InterfaceStateListener listener; + public final T listener; - private ListenerInfo(@NonNull Executor executor, @NonNull InterfaceStateListener listener) { + private ListenerInfo(@NonNull Executor executor, @NonNull T listener) { this.executor = executor; this.listener = listener; } @@ -289,16 +323,22 @@ public class EthernetManager { if (listener == null || executor == null) { throw new NullPointerException("listener and executor must not be null"); } - synchronized (mListeners) { - mListeners.add(new ListenerInfo(executor, listener)); - if (mListeners.size() == 1) { - try { - mService.addListener(mServiceListener); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } + synchronized (mListenerLock) { + maybeAddServiceListener(); + mIfaceListeners.add(new ListenerInfo<InterfaceStateListener>(executor, listener)); + } + } + + @GuardedBy("mListenerLock") + private void maybeAddServiceListener() { + if (!mIfaceListeners.isEmpty() || !mEthernetStateListeners.isEmpty()) return; + + try { + mService.addListener(mServiceListener); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); } + } /** @@ -323,15 +363,20 @@ public class EthernetManager { @SystemApi(client = MODULE_LIBRARIES) public void removeInterfaceStateListener(@NonNull InterfaceStateListener listener) { Objects.requireNonNull(listener); - synchronized (mListeners) { - mListeners.removeIf(l -> l.listener == listener); - if (mListeners.isEmpty()) { - try { - mService.removeListener(mServiceListener); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } + synchronized (mListenerLock) { + mIfaceListeners.removeIf(l -> l.listener == listener); + maybeRemoveServiceListener(); + } + } + + @GuardedBy("mListenerLock") + private void maybeRemoveServiceListener() { + if (!mIfaceListeners.isEmpty() || !mEthernetStateListeners.isEmpty()) return; + + try { + mService.removeListener(mServiceListener); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); } } @@ -443,41 +488,45 @@ public class EthernetManager { return new TetheredInterfaceRequest(mService, cbInternal); } - private static final class InternalNetworkManagementListener - extends IEthernetNetworkManagementListener.Stub { + private static final class NetworkInterfaceOutcomeReceiver + extends INetworkInterfaceOutcomeReceiver.Stub { @NonNull private final Executor mExecutor; @NonNull - private final BiConsumer<Network, EthernetNetworkManagementException> mListener; + private final OutcomeReceiver<String, EthernetNetworkManagementException> mCallback; - InternalNetworkManagementListener( + NetworkInterfaceOutcomeReceiver( @NonNull final Executor executor, - @NonNull final BiConsumer<Network, EthernetNetworkManagementException> listener) { + @NonNull final OutcomeReceiver<String, EthernetNetworkManagementException> + callback) { Objects.requireNonNull(executor, "Pass a non-null executor"); - Objects.requireNonNull(listener, "Pass a non-null listener"); + Objects.requireNonNull(callback, "Pass a non-null callback"); mExecutor = executor; - mListener = listener; + mCallback = callback; + } + + @Override + public void onResult(@NonNull String iface) { + mExecutor.execute(() -> mCallback.onResult(iface)); } @Override - public void onComplete( - @Nullable final Network network, - @Nullable final EthernetNetworkManagementException e) { - mExecutor.execute(() -> mListener.accept(network, e)); + public void onError(@NonNull EthernetNetworkManagementException e) { + mExecutor.execute(() -> mCallback.onError(e)); } } - private InternalNetworkManagementListener getInternalNetworkManagementListener( + private NetworkInterfaceOutcomeReceiver makeNetworkInterfaceOutcomeReceiver( @Nullable final Executor executor, - @Nullable final BiConsumer<Network, EthernetNetworkManagementException> listener) { - if (null != listener) { - Objects.requireNonNull(executor, "Pass a non-null executor, or a null listener"); + @Nullable final OutcomeReceiver<String, EthernetNetworkManagementException> callback) { + if (null != callback) { + Objects.requireNonNull(executor, "Pass a non-null executor, or a null callback"); } - final InternalNetworkManagementListener proxy; - if (null == listener) { + final NetworkInterfaceOutcomeReceiver proxy; + if (null == callback) { proxy = null; } else { - proxy = new InternalNetworkManagementListener(executor, listener); + proxy = new NetworkInterfaceOutcomeReceiver(executor, callback); } return proxy; } @@ -492,14 +541,17 @@ public class EthernetManager { * Similarly, use {@link NetworkCapabilities.Builder} to build a {@code NetworkCapabilities} * object for this network to put inside the {@code request}. * - * If non-null, the listener will be called exactly once after this is called, unless - * a synchronous exception was thrown. + * This function accepts an {@link OutcomeReceiver} that is called once the operation has + * finished execution. * * @param iface the name of the interface to act upon. * @param request the {@link EthernetNetworkUpdateRequest} used to set an ethernet network's * {@link StaticIpConfiguration} and {@link NetworkCapabilities} values. - * @param executor an {@link Executor} to execute the listener on. Optional if listener is null. - * @param listener an optional {@link BiConsumer} to listen for completion of the operation. + * @param executor an {@link Executor} to execute the callback on. Optional if callback is null. + * @param callback an optional {@link OutcomeReceiver} to listen for completion of the + * operation. On success, {@link OutcomeReceiver#onResult} is called with the + * interface name. On error, {@link OutcomeReceiver#onError} is called with more + * information about the error. * @throws SecurityException if the process doesn't hold * {@link android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}. * @throws UnsupportedOperationException if called on a non-automotive device or on an @@ -515,11 +567,11 @@ public class EthernetManager { @NonNull String iface, @NonNull EthernetNetworkUpdateRequest request, @Nullable @CallbackExecutor Executor executor, - @Nullable BiConsumer<Network, EthernetNetworkManagementException> listener) { + @Nullable OutcomeReceiver<String, EthernetNetworkManagementException> callback) { Objects.requireNonNull(iface, "iface must be non-null"); Objects.requireNonNull(request, "request must be non-null"); - final InternalNetworkManagementListener proxy = getInternalNetworkManagementListener( - executor, listener); + final NetworkInterfaceOutcomeReceiver proxy = makeNetworkInterfaceOutcomeReceiver( + executor, callback); try { mService.updateConfiguration(iface, request, proxy); } catch (RemoteException e) { @@ -528,20 +580,20 @@ public class EthernetManager { } /** - * Set an ethernet network's link state up. + * Enable a network interface. * - * When the link is successfully turned up, the listener will be called with the resulting - * network. If any error or unexpected condition happens while the system tries to turn the - * interface up, the listener will be called with an appropriate exception. - * The listener is guaranteed to be called exactly once for each call to this method, but this - * may take an unbounded amount of time depending on the actual network conditions. + * Enables a previously disabled network interface. + * This function accepts an {@link OutcomeReceiver} that is called once the operation has + * finished execution. * - * @param iface the name of the interface to act upon. - * @param executor an {@link Executor} to execute the listener on. Optional if listener is null. - * @param listener an optional {@link BiConsumer} to listen for completion of the operation. + * @param iface the name of the interface to enable. + * @param executor an {@link Executor} to execute the callback on. Optional if callback is null. + * @param callback an optional {@link OutcomeReceiver} to listen for completion of the + * operation. On success, {@link OutcomeReceiver#onResult} is called with the + * interface name. On error, {@link OutcomeReceiver#onError} is called with more + * information about the error. * @throws SecurityException if the process doesn't hold * {@link android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}. - * @throws UnsupportedOperationException if called on a non-automotive device. * @hide */ @SystemApi @@ -550,13 +602,13 @@ public class EthernetManager { android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}) @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE) - public void connectNetwork( + public void enableInterface( @NonNull String iface, @Nullable @CallbackExecutor Executor executor, - @Nullable BiConsumer<Network, EthernetNetworkManagementException> listener) { + @Nullable OutcomeReceiver<String, EthernetNetworkManagementException> callback) { Objects.requireNonNull(iface, "iface must be non-null"); - final InternalNetworkManagementListener proxy = getInternalNetworkManagementListener( - executor, listener); + final NetworkInterfaceOutcomeReceiver proxy = makeNetworkInterfaceOutcomeReceiver( + executor, callback); try { mService.connectNetwork(iface, proxy); } catch (RemoteException e) { @@ -565,19 +617,21 @@ public class EthernetManager { } /** - * Set an ethernet network's link state down. + * Disable a network interface. * - * When the link is successfully turned down, the listener will be called with the network that - * was torn down, if any. If any error or unexpected condition happens while the system tries to - * turn the interface down, the listener will be called with an appropriate exception. - * The listener is guaranteed to be called exactly once for each call to this method. + * Disables the use of a network interface to fulfill network requests. If the interface + * currently serves a request, the network will be torn down. + * This function accepts an {@link OutcomeReceiver} that is called once the operation has + * finished execution. * - * @param iface the name of the interface to act upon. - * @param executor an {@link Executor} to execute the listener on. Optional if listener is null. - * @param listener an optional {@link BiConsumer} to listen for completion of the operation. + * @param iface the name of the interface to disable. + * @param executor an {@link Executor} to execute the callback on. Optional if callback is null. + * @param callback an optional {@link OutcomeReceiver} to listen for completion of the + * operation. On success, {@link OutcomeReceiver#onResult} is called with the + * interface name. On error, {@link OutcomeReceiver#onError} is called with more + * information about the error. * @throws SecurityException if the process doesn't hold * {@link android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}. - * @throws UnsupportedOperationException if called on a non-automotive device. * @hide */ @SystemApi @@ -586,17 +640,90 @@ public class EthernetManager { android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}) @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE) - public void disconnectNetwork( + public void disableInterface( @NonNull String iface, @Nullable @CallbackExecutor Executor executor, - @Nullable BiConsumer<Network, EthernetNetworkManagementException> listener) { + @Nullable OutcomeReceiver<String, EthernetNetworkManagementException> callback) { Objects.requireNonNull(iface, "iface must be non-null"); - final InternalNetworkManagementListener proxy = getInternalNetworkManagementListener( - executor, listener); + final NetworkInterfaceOutcomeReceiver proxy = makeNetworkInterfaceOutcomeReceiver( + executor, callback); try { mService.disconnectNetwork(iface, proxy); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } + + /** + * Change ethernet setting. + * + * @param enabled enable or disable ethernet settings. + * + * @hide + */ + @RequiresPermission(anyOf = { + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, + android.Manifest.permission.NETWORK_STACK, + android.Manifest.permission.NETWORK_SETTINGS}) + @SystemApi(client = MODULE_LIBRARIES) + public void setEthernetEnabled(boolean enabled) { + try { + mService.setEthernetEnabled(enabled); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Listen to changes in the state of ethernet. + * + * @param executor to run callbacks on. + * @param listener to listen ethernet state changed. + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) + @SystemApi(client = MODULE_LIBRARIES) + public void addEthernetStateListener(@NonNull Executor executor, + @NonNull IntConsumer listener) { + Objects.requireNonNull(executor); + Objects.requireNonNull(listener); + synchronized (mListenerLock) { + maybeAddServiceListener(); + mEthernetStateListeners.add(new ListenerInfo<IntConsumer>(executor, listener)); + } + } + + /** + * Removes a listener. + * + * @param listener to listen ethernet state changed. + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) + @SystemApi(client = MODULE_LIBRARIES) + public void removeEthernetStateListener(@NonNull IntConsumer listener) { + Objects.requireNonNull(listener); + synchronized (mListenerLock) { + mEthernetStateListeners.removeIf(l -> l.listener == listener); + maybeRemoveServiceListener(); + } + } + + /** + * Returns an array of existing Ethernet interface names regardless whether the interface + * is available or not currently. + * @hide + */ + @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) + @SystemApi(client = MODULE_LIBRARIES) + @NonNull + public List<String> getInterfaceList() { + try { + return mService.getInterfaceList(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } } diff --git a/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java index 43f4c40f2d27..1691942c3675 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java +++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java @@ -33,17 +33,20 @@ import java.util.Objects; */ @SystemApi public final class EthernetNetworkUpdateRequest implements Parcelable { - @NonNull + @Nullable private final IpConfiguration mIpConfig; @Nullable private final NetworkCapabilities mNetworkCapabilities; /** - * @return the new {@link IpConfiguration}. + * Setting the {@link IpConfiguration} is optional in {@link EthernetNetworkUpdateRequest}. + * When set to null, the existing IpConfiguration is not updated. + * + * @return the new {@link IpConfiguration} or null. */ - @NonNull + @Nullable public IpConfiguration getIpConfiguration() { - return new IpConfiguration(mIpConfig); + return mIpConfig == null ? null : new IpConfiguration(mIpConfig); } /** @@ -57,9 +60,8 @@ public final class EthernetNetworkUpdateRequest implements Parcelable { return mNetworkCapabilities == null ? null : new NetworkCapabilities(mNetworkCapabilities); } - private EthernetNetworkUpdateRequest(@NonNull final IpConfiguration ipConfig, + private EthernetNetworkUpdateRequest(@Nullable final IpConfiguration ipConfig, @Nullable final NetworkCapabilities networkCapabilities) { - Objects.requireNonNull(ipConfig); mIpConfig = ipConfig; mNetworkCapabilities = networkCapabilities; } @@ -90,7 +92,8 @@ public final class EthernetNetworkUpdateRequest implements Parcelable { */ public Builder(@NonNull final EthernetNetworkUpdateRequest request) { Objects.requireNonNull(request); - mBuilderIpConfig = new IpConfiguration(request.mIpConfig); + mBuilderIpConfig = null == request.mIpConfig + ? null : new IpConfiguration(request.mIpConfig); mBuilderNetworkCapabilities = null == request.mNetworkCapabilities ? null : new NetworkCapabilities(request.mNetworkCapabilities); } @@ -101,8 +104,8 @@ public final class EthernetNetworkUpdateRequest implements Parcelable { * @return The builder to facilitate chaining. */ @NonNull - public Builder setIpConfiguration(@NonNull final IpConfiguration ipConfig) { - mBuilderIpConfig = new IpConfiguration(ipConfig); + public Builder setIpConfiguration(@Nullable final IpConfiguration ipConfig) { + mBuilderIpConfig = ipConfig == null ? null : new IpConfiguration(ipConfig); return this; } @@ -119,9 +122,16 @@ public final class EthernetNetworkUpdateRequest implements Parcelable { /** * Build {@link EthernetNetworkUpdateRequest} return the current update request. + * + * @throws IllegalStateException when both mBuilderNetworkCapabilities and mBuilderIpConfig + * are null. */ @NonNull public EthernetNetworkUpdateRequest build() { + if (mBuilderIpConfig == null && mBuilderNetworkCapabilities == null) { + throw new IllegalStateException( + "Cannot construct an empty EthernetNetworkUpdateRequest"); + } return new EthernetNetworkUpdateRequest(mBuilderIpConfig, mBuilderNetworkCapabilities); } } diff --git a/packages/ConnectivityT/framework-t/src/android/net/IEthernetManager.aidl b/packages/ConnectivityT/framework-t/src/android/net/IEthernetManager.aidl index 544d02ba76ff..42e4c1ac55aa 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/IEthernetManager.aidl +++ b/packages/ConnectivityT/framework-t/src/android/net/IEthernetManager.aidl @@ -18,10 +18,13 @@ package android.net; import android.net.IpConfiguration; import android.net.IEthernetServiceListener; -import android.net.IEthernetNetworkManagementListener; +import android.net.EthernetNetworkManagementException; import android.net.EthernetNetworkUpdateRequest; +import android.net.INetworkInterfaceOutcomeReceiver; import android.net.ITetheredInterfaceCallback; +import java.util.List; + /** * Interface that answers queries about, and allows changing * ethernet configuration. @@ -39,7 +42,9 @@ interface IEthernetManager void requestTetheredInterface(in ITetheredInterfaceCallback callback); void releaseTetheredInterface(in ITetheredInterfaceCallback callback); void updateConfiguration(String iface, in EthernetNetworkUpdateRequest request, - in IEthernetNetworkManagementListener listener); - void connectNetwork(String iface, in IEthernetNetworkManagementListener listener); - void disconnectNetwork(String iface, in IEthernetNetworkManagementListener listener); + in INetworkInterfaceOutcomeReceiver listener); + void connectNetwork(String iface, in INetworkInterfaceOutcomeReceiver listener); + void disconnectNetwork(String iface, in INetworkInterfaceOutcomeReceiver listener); + void setEthernetEnabled(boolean enabled); + List<String> getInterfaceList(); } diff --git a/packages/ConnectivityT/framework-t/src/android/net/IEthernetServiceListener.aidl b/packages/ConnectivityT/framework-t/src/android/net/IEthernetServiceListener.aidl index 6d2ba03f78d4..751605bb3849 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/IEthernetServiceListener.aidl +++ b/packages/ConnectivityT/framework-t/src/android/net/IEthernetServiceListener.aidl @@ -21,6 +21,7 @@ import android.net.IpConfiguration; /** @hide */ oneway interface IEthernetServiceListener { + void onEthernetStateChanged(int state); void onInterfaceStateChanged(String iface, int state, int role, in IpConfiguration configuration); } diff --git a/packages/ConnectivityT/framework-t/src/android/net/IEthernetNetworkManagementListener.aidl b/packages/ConnectivityT/framework-t/src/android/net/INetworkInterfaceOutcomeReceiver.aidl index 93edccfdafd9..85795ead7aea 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/IEthernetNetworkManagementListener.aidl +++ b/packages/ConnectivityT/framework-t/src/android/net/INetworkInterfaceOutcomeReceiver.aidl @@ -17,9 +17,9 @@ package android.net; import android.net.EthernetNetworkManagementException; -import android.net.Network; /** @hide */ -oneway interface IEthernetNetworkManagementListener { - void onComplete(in Network network, in EthernetNetworkManagementException exception); +oneway interface INetworkInterfaceOutcomeReceiver { + void onResult(in String iface); + void onError(in EthernetNetworkManagementException e); }
\ No newline at end of file diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java index 06f2a621bcae..bcfeab96081a 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java +++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java @@ -16,6 +16,8 @@ package android.net; +import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; + import static com.android.net.module.util.NetworkStatsUtils.multiplySafeByRational; import android.annotation.IntDef; @@ -124,7 +126,6 @@ public final class NetworkStats implements Parcelable, Iterable<NetworkStats.Ent public @Nullable static final String[] INTERFACES_ALL = null; /** {@link #tag} value for total data across all tags. */ - // TODO: Rename TAG_NONE to TAG_ALL. public static final int TAG_NONE = 0; /** {@link #metered} value to account for all metered states. */ @@ -390,77 +391,102 @@ public final class NetworkStats implements Parcelable, Iterable<NetworkStats.Ent /** * @return the uid of this entry. + * @hide */ + @SystemApi(client = MODULE_LIBRARIES) public int getUid() { return uid; } /** * @return the set state of this entry. + * @hide */ + @SystemApi(client = MODULE_LIBRARIES) @State public int getSet() { return set; } /** * @return the tag value of this entry. + * @hide */ + @SystemApi(client = MODULE_LIBRARIES) public int getTag() { return tag; } /** * @return the metered state. + * @hide */ - @Meteredness public int getMetered() { + @Meteredness + @SystemApi(client = MODULE_LIBRARIES) + public int getMetered() { return metered; } /** * @return the roaming state. + * @hide */ - @Roaming public int getRoaming() { + @Roaming + @SystemApi(client = MODULE_LIBRARIES) + public int getRoaming() { return roaming; } /** * @return the default network state. + * @hide */ - @DefaultNetwork public int getDefaultNetwork() { + @DefaultNetwork + @SystemApi(client = MODULE_LIBRARIES) + public int getDefaultNetwork() { return defaultNetwork; } /** * @return the number of received bytes. + * @hide */ + @SystemApi(client = MODULE_LIBRARIES) public long getRxBytes() { return rxBytes; } /** * @return the number of received packets. + * @hide */ + @SystemApi(client = MODULE_LIBRARIES) public long getRxPackets() { return rxPackets; } /** * @return the number of transmitted bytes. + * @hide */ + @SystemApi(client = MODULE_LIBRARIES) public long getTxBytes() { return txBytes; } /** * @return the number of transmitted packets. + * @hide */ + @SystemApi(client = MODULE_LIBRARIES) public long getTxPackets() { return txPackets; } /** * @return the count of network operations performed for this entry. + * @hide */ + @SystemApi(client = MODULE_LIBRARIES) public long getOperations() { return operations; } @@ -682,7 +708,7 @@ public final class NetworkStats implements Parcelable, Iterable<NetworkStats.Ent * The remove() method is not implemented and will throw UnsupportedOperationException. * @hide */ - @SystemApi + @SystemApi(client = MODULE_LIBRARIES) @NonNull public Iterator<Entry> iterator() { return new Iterator<Entry>() { int mIndex = 0; diff --git a/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java b/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java index bc836d857e3e..dc4ac552a4bb 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java +++ b/packages/ConnectivityT/framework-t/src/android/net/TrafficStats.java @@ -205,7 +205,7 @@ public class TrafficStats { * server context. * @hide */ - @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @SystemApi(client = MODULE_LIBRARIES) @SuppressLint("VisiblySynchronized") public static synchronized void init(@NonNull final Context context) { if (sStatsService != null) { @@ -376,7 +376,7 @@ public class TrafficStats { * * @hide */ - @SystemApi + @SystemApi(client = MODULE_LIBRARIES) public static void setThreadStatsTagDownload() { setThreadStatsTag(TAG_SYSTEM_DOWNLOAD); } @@ -468,7 +468,7 @@ public class TrafficStats { * * @see #setThreadStatsTag(int) */ - public static void tagSocket(Socket socket) throws SocketException { + public static void tagSocket(@NonNull Socket socket) throws SocketException { SocketTagger.get().tag(socket); } @@ -483,7 +483,7 @@ public class TrafficStats { * calling {@code untagSocket()} before sending the socket to another * process. */ - public static void untagSocket(Socket socket) throws SocketException { + public static void untagSocket(@NonNull Socket socket) throws SocketException { SocketTagger.get().untag(socket); } @@ -496,14 +496,14 @@ public class TrafficStats { * * @see #setThreadStatsTag(int) */ - public static void tagDatagramSocket(DatagramSocket socket) throws SocketException { + public static void tagDatagramSocket(@NonNull DatagramSocket socket) throws SocketException { SocketTagger.get().tag(socket); } /** * Remove any statistics parameters from the given {@link DatagramSocket}. */ - public static void untagDatagramSocket(DatagramSocket socket) throws SocketException { + public static void untagDatagramSocket(@NonNull DatagramSocket socket) throws SocketException { SocketTagger.get().untag(socket); } @@ -516,7 +516,7 @@ public class TrafficStats { * * @see #setThreadStatsTag(int) */ - public static void tagFileDescriptor(FileDescriptor fd) throws IOException { + public static void tagFileDescriptor(@NonNull FileDescriptor fd) throws IOException { SocketTagger.get().tag(fd); } @@ -524,7 +524,7 @@ public class TrafficStats { * Remove any statistics parameters from the given {@link FileDescriptor} * socket. */ - public static void untagFileDescriptor(FileDescriptor fd) throws IOException { + public static void untagFileDescriptor(@NonNull FileDescriptor fd) throws IOException { SocketTagger.get().untag(fd); } diff --git a/packages/SettingsLib/SearchWidget/res/values-or/strings.xml b/packages/SettingsLib/SearchWidget/res/values-or/strings.xml index cf824deecd79..322571adc89c 100644 --- a/packages/SettingsLib/SearchWidget/res/values-or/strings.xml +++ b/packages/SettingsLib/SearchWidget/res/values-or/strings.xml @@ -17,5 +17,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="search_menu" msgid="1914043873178389845">"ସେଟିଂସ୍ ସନ୍ଧାନ କରନ୍ତୁ"</string> + <string name="search_menu" msgid="1914043873178389845">"ସେଟିଂସ ସନ୍ଧାନ କରନ୍ତୁ"</string> </resources> diff --git a/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_text_color_primary.xml b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_text_color_primary.xml new file mode 100644 index 000000000000..221d2db00031 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_text_color_primary.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_enabled="false" + android:alpha="?android:attr/disabledAlpha" + android:color="@color/settingslib_text_color_primary_device_default"/> + <item android:color="@color/settingslib_text_color_primary_device_default"/> +</selector> diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml index 9d3991119338..cba1a9cf0003 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml @@ -17,7 +17,7 @@ <resources> <style name="TextAppearance.PreferenceTitle.SettingsLib" parent="@android:style/TextAppearance.Material.Subhead"> - <item name="android:textColor">@color/settingslib_text_color_primary_device_default</item> + <item name="android:textColor">@color/settingslib_text_color_primary</item> <item name="android:fontFamily">@string/settingslib_config_headlineFontFamily</item> <item name="android:textSize">20sp</item> </style> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 579e3d7a6d39..31036ec152d9 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1038,23 +1038,6 @@ <!-- Developer settings: text for the WebView provider selection toast shown if an invalid provider was chosen (i.e. the setting list was stale). [CHAR LIMIT=NONE] --> <string name="select_webview_provider_toast_text">This choice is no longer valid. Try again.</string> - <!-- Developer settings screen, convert userdata to file encryption option name --> - <string name="convert_to_file_encryption">Convert to file encryption</string> - <!-- Developer settings screen, convert userdata to file encryption summary when option is available --> - <string name="convert_to_file_encryption_enabled">Convert\u2026</string> - <!-- Developer settings screen, convert userdata to file encryption summary when option is already done --> - <string name="convert_to_file_encryption_done">Already file encrypted</string> - <!-- Title used on dialog with final prompt for converting to file encryption --> - <string name="title_convert_fbe">Converting to file based encryption</string> - <!-- Warning displayed on dialog with final prompt for converting to file encryption --> - <string name="convert_to_fbe_warning"> - Convert data partition to file based encryption.\n - !!Warning!! This will erase all your data.\n - This feature is alpha, and may not work correctly.\n - Press \'Wipe and convert\u2026\' to continue.</string> - <!-- Button on dialog that triggers convertion to file encryption --> - <string name="button_convert_fbe">Wipe and convert\u2026</string> - <!-- Name of feature to change color setting for the display [CHAR LIMIT=60] --> <string name="picture_color_mode">Picture color mode</string> @@ -1582,4 +1565,11 @@ <string name="keyboard_layout_dialog_title">Choose keyboard layout</string> <!-- Label of the default keyboard layout. [CHAR LIMIT=35] --> <string name="keyboard_layout_default_label">Default</string> + + <!-- Special access > Title for managing turn screen on settings. [CHAR LIMIT=50] --> + <string name="turn_screen_on_title">Turn screen on</string> + <!-- Label for a setting which controls whether an app can turn the screen on [CHAR LIMIT=45] --> + <string name="allow_turn_screen_on">Allow turning the screen on</string> + <!-- Description for a setting which controls whether an app can turn the screen on [CHAR LIMIT=NONE] --> + <string name="allow_turn_screen_on_description">Allow an app to turn the screen on. If granted, the app may turn on the screen at any time without your explicit intent.</string> </resources> diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java index e4efae2f1a36..7e8ce2b5b29d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java @@ -23,12 +23,15 @@ import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import android.app.admin.DevicePolicyManager; import android.content.Context; import android.content.res.TypedArray; +import android.os.Build; import android.os.UserHandle; import android.text.TextUtils; import android.util.AttributeSet; import android.util.TypedValue; import android.widget.TextView; +import androidx.annotation.RequiresApi; +import androidx.core.os.BuildCompat; import androidx.preference.Preference; import androidx.preference.PreferenceViewHolder; @@ -102,11 +105,9 @@ public class RestrictedPreferenceHelper { if (mDisabledSummary) { final TextView summaryView = (TextView) holder.findViewById(android.R.id.summary); if (summaryView != null) { - final CharSequence disabledText = mContext - .getSystemService(DevicePolicyManager.class) - .getString(CONTROLLED_BY_ADMIN_SUMMARY, - () -> summaryView.getContext().getString( - R.string.disabled_by_admin_summary_text)); + final CharSequence disabledText = BuildCompat.isAtLeastT() + ? getDisabledByAdminUpdatableString() + : mContext.getString(R.string.disabled_by_admin_summary_text); if (mDisabledByAdmin) { summaryView.setText(disabledText); } else if (mDisabledByAppOps) { @@ -119,6 +120,13 @@ public class RestrictedPreferenceHelper { } } + @RequiresApi(Build.VERSION_CODES.TIRAMISU) + private String getDisabledByAdminUpdatableString() { + return mContext.getSystemService(DevicePolicyManager.class).getString( + CONTROLLED_BY_ADMIN_SUMMARY, + () -> mContext.getString(R.string.disabled_by_admin_summary_text)); + } + public void useAdminDisabledSummary(boolean useSummary) { mDisabledSummary = useSummary; } diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java index 19114cf147e4..38fad9d99aa4 100644 --- a/packages/SettingsLib/src/com/android/settingslib/Utils.java +++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java @@ -30,6 +30,7 @@ import android.net.TetheringManager; import android.net.vcn.VcnTransportInfo; import android.net.wifi.WifiInfo; import android.os.BatteryManager; +import android.os.Build; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; @@ -41,8 +42,10 @@ import android.telephony.ServiceState; import android.telephony.TelephonyManager; import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; import androidx.core.graphics.drawable.RoundedBitmapDrawable; import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory; +import androidx.core.os.BuildCompat; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.UserIcons; @@ -127,8 +130,9 @@ public class Utils { String name = info != null ? info.name : null; if (info.isManagedProfile()) { // We use predefined values for managed profiles - return context.getSystemService(DevicePolicyManager.class).getString( - WORK_PROFILE_USER_LABEL, () -> context.getString(R.string.managed_user_title)); + return BuildCompat.isAtLeastT() + ? getUpdatableManagedUserTitle(context) + : context.getString(R.string.managed_user_title); } else if (info.isGuest()) { name = context.getString(R.string.user_guest); } @@ -140,6 +144,13 @@ public class Utils { return context.getResources().getString(R.string.running_process_item_user_label, name); } + @RequiresApi(Build.VERSION_CODES.TIRAMISU) + private static String getUpdatableManagedUserTitle(Context context) { + return context.getSystemService(DevicePolicyManager.class).getString( + WORK_PROFILE_USER_LABEL, + () -> context.getString(R.string.managed_user_title)); + } + /** * Returns a circular icon for a user. */ diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 7f300cafaa08..2a28891e9158 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -753,7 +753,10 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> ParcelUuid[] uuids = mDevice.getUuids(); if (uuids == null) return false; - ParcelUuid[] localUuids = mLocalAdapter.getUuids(); + List<ParcelUuid> uuidsList = mLocalAdapter.getUuidsList(); + ParcelUuid[] localUuids = new ParcelUuid[uuidsList.size()]; + uuidsList.toArray(localUuids); + if (localUuids == null) return false; /* diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java index e7a6b327bacd..31cc6a4ba412 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java @@ -126,7 +126,10 @@ public class LocalBluetoothAdapter { } public ParcelUuid[] getUuids() { - return mAdapter.getUuids(); + List<ParcelUuid> uuidsList = mAdapter.getUuidsList(); + ParcelUuid[] uuidsArray = new ParcelUuid[uuidsList.size()]; + uuidsList.toArray(uuidsArray); + return uuidsArray; } public boolean isDiscovering() { diff --git a/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java b/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java index c61f8a9c3b53..be420ac8bd24 100644 --- a/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java @@ -31,6 +31,8 @@ import android.telephony.TelephonyCallback; import android.telephony.TelephonyManager; import android.util.Log; +import androidx.annotation.VisibleForTesting; + /** * An interface class to manage connectivity subsystem recovery/restart operations. */ @@ -193,22 +195,30 @@ public class ConnectivitySubsystemsRecoveryManager { mApmMonitorRegistered = false; } - private void startTrackingWifiRestart() { + @VisibleForTesting + void startTrackingWifiRestart() { + if (mWifiManager == null) return; mWifiManager.registerSubsystemRestartTrackingCallback(new HandlerExecutor(mHandler), mWifiSubsystemRestartTrackingCallback); } - private void stopTrackingWifiRestart() { + @VisibleForTesting + void stopTrackingWifiRestart() { + if (mWifiManager == null) return; mWifiManager.unregisterSubsystemRestartTrackingCallback( mWifiSubsystemRestartTrackingCallback); } - private void startTrackingTelephonyRestart() { + @VisibleForTesting + void startTrackingTelephonyRestart() { + if (mTelephonyManager == null) return; mTelephonyManager.registerTelephonyCallback(new HandlerExecutor(mHandler), mTelephonyCallback); } - private void stopTrackingTelephonyRestart() { + @VisibleForTesting + void stopTrackingTelephonyRestart() { + if (mTelephonyManager == null) return; mTelephonyManager.unregisterTelephonyCallback(mTelephonyCallback); } diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java index 0cb2c0b22a4c..63a9f0c5c7f4 100644 --- a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java +++ b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java @@ -78,6 +78,11 @@ class AvatarPhotoController { static final int REQUEST_CODE_TAKE_PHOTO = 1002; static final int REQUEST_CODE_CROP_PHOTO = 1003; + /** + * Delay to allow the photo picker exit animation to complete before the crop activity opens. + */ + private static final long DELAY_BEFORE_CROP_MILLIS = 150; + private static final String IMAGES_DIR = "multi_user"; private static final String PRE_CROP_PICTURE_FILE_NAME = "PreCropEditUserPhoto.jpg"; private static final String CROP_PICTURE_FILE_NAME = "CropEditUserPhoto.jpg"; @@ -131,13 +136,15 @@ class AvatarPhotoController { mAvatarUi.returnUriResult(pictureUri); return true; case REQUEST_CODE_TAKE_PHOTO: - case REQUEST_CODE_CHOOSE_PHOTO: if (mTakePictureUri.equals(pictureUri)) { cropPhoto(pictureUri); } else { - copyAndCropPhoto(pictureUri); + copyAndCropPhoto(pictureUri, false); } return true; + case REQUEST_CODE_CHOOSE_PHOTO: + copyAndCropPhoto(pictureUri, true); + return true; } return false; } @@ -154,7 +161,7 @@ class AvatarPhotoController { mAvatarUi.startActivityForResult(intent, REQUEST_CODE_CHOOSE_PHOTO); } - private void copyAndCropPhoto(final Uri pictureUri) { + private void copyAndCropPhoto(final Uri pictureUri, boolean delayBeforeCrop) { try { ThreadUtils.postOnBackgroundThread(() -> { final ContentResolver cr = mContextInjector.getContentResolver(); @@ -165,11 +172,17 @@ class AvatarPhotoController { Log.w(TAG, "Failed to copy photo", e); return; } - ThreadUtils.postOnMainThread(() -> { + Runnable cropRunnable = () -> { if (!mAvatarUi.isFinishing()) { cropPhoto(mPreCropPictureUri); } - }); + }; + if (delayBeforeCrop) { + ThreadUtils.postOnMainThreadDelayed(cropRunnable, DELAY_BEFORE_CROP_MILLIS); + } else { + ThreadUtils.postOnMainThread(cropRunnable); + } + }).get(); } catch (InterruptedException | ExecutionException e) { Log.e(TAG, "Error performing copy-and-crop", e); diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java b/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java index 69f1c17f97b1..2c1d5da5e941 100644 --- a/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java @@ -84,6 +84,13 @@ public class ThreadUtils { getUiThreadHandler().post(runnable); } + /** + * Posts the runnable on the main thread with a delay. + */ + public static void postOnMainThreadDelayed(Runnable runnable, long delayMillis) { + getUiThreadHandler().postDelayed(runnable, delayMillis); + } + private static synchronized ExecutorService getThreadExecutor() { if (sThreadExecutor == null) { sThreadExecutor = Executors.newFixedThreadPool( diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManagerTest.java new file mode 100644 index 000000000000..ca53a187d6f8 --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManagerTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.connectivity; + +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Handler; + +import androidx.test.core.app.ApplicationProvider; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.robolectric.RobolectricTestRunner; + +@RunWith(RobolectricTestRunner.class) +public class ConnectivitySubsystemsRecoveryManagerTest { + + @Rule + public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Spy + Context mContext = ApplicationProvider.getApplicationContext(); + @Spy + Handler mMainHandler = ApplicationProvider.getApplicationContext().getMainThreadHandler(); + @Mock + PackageManager mPackageManager; + + ConnectivitySubsystemsRecoveryManager mConnectivitySubsystemsRecoveryManager; + + @Before + public void setUp() { + when(mContext.getPackageManager()).thenReturn(mPackageManager); + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)).thenReturn(true); + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(true); + } + + @Test + public void startTrackingWifiRestart_hasNoWifiFeature_shouldNotCrash() { + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)).thenReturn(false); + mConnectivitySubsystemsRecoveryManager = + new ConnectivitySubsystemsRecoveryManager(mContext, mMainHandler); + + mConnectivitySubsystemsRecoveryManager.startTrackingWifiRestart(); + } + + @Test + public void stopTrackingWifiRestart_hasNoWifiFeature_shouldNotCrash() { + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)).thenReturn(false); + mConnectivitySubsystemsRecoveryManager = + new ConnectivitySubsystemsRecoveryManager(mContext, mMainHandler); + + mConnectivitySubsystemsRecoveryManager.stopTrackingWifiRestart(); + } + + @Test + public void startTrackingTelephonyRestart_hasNoTelephonyFeature_shouldNotCrash() { + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(false); + mConnectivitySubsystemsRecoveryManager = + new ConnectivitySubsystemsRecoveryManager(mContext, mMainHandler); + + mConnectivitySubsystemsRecoveryManager.startTrackingTelephonyRestart(); + } + + @Test + public void stopTrackingTelephonyRestart_hasNoTelephonyFeature_shouldNotCrash() { + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(false); + mConnectivitySubsystemsRecoveryManager = + new ConnectivitySubsystemsRecoveryManager(mContext, mMainHandler); + + mConnectivitySubsystemsRecoveryManager.stopTrackingTelephonyRestart(); + } +} diff --git a/packages/SettingsProvider/res/values-or/strings.xml b/packages/SettingsProvider/res/values-or/strings.xml index 486d8ffaa500..85add41b1ee1 100644 --- a/packages/SettingsProvider/res/values-or/strings.xml +++ b/packages/SettingsProvider/res/values-or/strings.xml @@ -19,7 +19,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_label" msgid="4567566098528588863">"ସେଟିଙ୍ଗ ଷ୍ଟୋରେଜ୍"</string> + <string name="app_label" msgid="4567566098528588863">"ସେଟିଂସ ଷ୍ଟୋରେଜ୍"</string> <string name="wifi_softap_config_change" msgid="5688373762357941645">"ହଟସ୍ପଟ୍ ସେଟିଂସ୍ ବଦଳାଯାଇଛି"</string> <string name="wifi_softap_config_change_summary" msgid="8946397286141531087">"ବିବରଣୀ ଦେଖିବାକୁ ଟାପ୍ କରନ୍ତୁ"</string> </resources> diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index e24b2d6c6aa9..4b45cc338a6a 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -314,6 +314,11 @@ <!-- To change system captions state --> <uses-permission android:name="android.permission.SET_SYSTEM_AUDIO_CAPTION" /> + <!-- Compat framework --> + <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE" /> + <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" /> + <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /> + <protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" /> <protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" /> <protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" /> diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml index 8d205c14144d..9a6f5edae5ec 100644 --- a/packages/SystemUI/res-keyguard/values/dimens.xml +++ b/packages/SystemUI/res-keyguard/values/dimens.xml @@ -129,4 +129,7 @@ <dimen name="user_switcher_icon_selected_width">8dp</dimen> <dimen name="user_switcher_fullscreen_button_text_size">14sp</dimen> <dimen name="user_switcher_fullscreen_button_padding">12dp</dimen> + + <!-- Translation y for appear animation --> + <dimen name="keyguard_host_view_translation_y">80dp</dimen> </resources> diff --git a/packages/SystemUI/res/drawable/media_ttt_undo_background.xml b/packages/SystemUI/res/drawable/media_ttt_undo_background.xml index ec74ee1fcbf1..3e2e4f055b14 100644 --- a/packages/SystemUI/res/drawable/media_ttt_undo_background.xml +++ b/packages/SystemUI/res/drawable/media_ttt_undo_background.xml @@ -14,9 +14,14 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<shape +<ripple xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"> - <solid android:color="?androidprv:attr/colorAccentPrimary" /> - <corners android:radius="24dp" /> -</shape> + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + android:color="?android:textColorPrimary"> + <item android:id="@android:id/background"> + <shape> + <solid android:color="?androidprv:attr/colorAccentPrimary"/> + <corners android:radius="24dp" /> + </shape> + </item> +</ripple> diff --git a/packages/SystemUI/res/drawable/user_switcher_icon_large.xml b/packages/SystemUI/res/drawable/user_switcher_icon_large.xml index 1ed75537059a..d81b518d2a9f 100644 --- a/packages/SystemUI/res/drawable/user_switcher_icon_large.xml +++ b/packages/SystemUI/res/drawable/user_switcher_icon_large.xml @@ -27,7 +27,7 @@ </shape> </item> <!-- When an item is selected, this layer will show a ring around the icon --> - <item> + <item android:id="@+id/ring"> <shape android:shape="oval"> <stroke android:width="@dimen/user_switcher_icon_selected_width" @@ -35,7 +35,7 @@ </shape> </item> <!-- Where the user drawable/bitmap will be placed --> - <item + <item android:id="@+id/user_avatar" android:drawable="@drawable/user_avatar_bg" android:width="@dimen/bouncer_user_switcher_icon_size" android:height="@dimen/bouncer_user_switcher_icon_size" diff --git a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml index ad2bc79a5c96..5aa608084510 100644 --- a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml +++ b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml @@ -37,7 +37,5 @@ android:layout_gravity="center" android:minWidth="@dimen/ongoing_appops_chip_min_width" android:maxWidth="@dimen/ongoing_appops_chip_max_width" - android:clipChildren="false" - android:clipToPadding="false" /> </com.android.systemui.privacy.OngoingPrivacyChip>
\ No newline at end of file diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml index 02a4070415dd..740697ba98af 100644 --- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml @@ -54,6 +54,14 @@ the shade (in alpha) --> <dimen name="lockscreen_shade_scrim_transition_distance">80dp</dimen> + <!-- The notifications scrim transition should start when the other scrims' transition is at + 95%. --> + <dimen name="lockscreen_shade_notifications_scrim_transition_delay">76dp</dimen> + + <!-- The notifications scrim transition duration is 66.6% of the duration of the other scrims' + transition. --> + <dimen name="lockscreen_shade_notifications_scrim_transition_distance">53.28dp</dimen> + <!-- Distance that the full shade transition takes in order for the keyguard content on NotificationPanelViewController to fully fade (e.g. Clock & Smartspace) --> <dimen name="lockscreen_shade_npvc_keyguard_content_alpha_transition_distance">80dp</dimen> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index a0a876825a6f..9ea361892b32 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -707,4 +707,18 @@ <integer name="complicationFadeInMs">500</integer> <integer name="complicationRestoreMs">1000</integer> + + <!-- Icons that don't show in a collapsed non-keyguard statusbar --> + <string-array name="config_collapsed_statusbar_icon_blocklist" translatable="false"> + <item>@*android:string/status_bar_volume</item> + <item>@*android:string/status_bar_alarm_clock</item> + <item>@*android:string/status_bar_call_strength</item> + </string-array> + + <!-- Icons that don't show in a collapsed statusbar on keyguard --> + <string-array name="config_keyguard_statusbar_icon_blocklist" translatable="false"> + <item>@*android:string/status_bar_volume</item> + <item>@*android:string/status_bar_alarm_clock</item> + <item>@*android:string/status_bar_call_strength</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index ffae6015c732..8f4e11527d95 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -941,6 +941,9 @@ <!-- Three privacy items. This value must not be exceeded --> <dimen name="ongoing_appops_chip_max_width">76dp</dimen> <dimen name="ongoing_appops_dot_diameter">6dp</dimen> + <dimen name="ongoing_appops_chip_min_animation_width">10dp</dimen> + <dimen name="ongoing_appops_chip_animation_in_status_bar_translation_x">15dp</dimen> + <dimen name="ongoing_appops_chip_animation_out_status_bar_translation_x">7dp</dimen> <!-- Total minimum padding to enforce to ensure that the dot can always show --> <dimen name="ongoing_appops_dot_min_padding">20dp</dimen> @@ -1005,6 +1008,9 @@ <!-- Media tap-to-transfer chip for receiver device --> <dimen name="media_ttt_chip_size_receiver">100dp</dimen> <dimen name="media_ttt_icon_size_receiver">95dp</dimen> + <!-- Since the generic icon isn't circular, we need to scale it down so it still fits within + the circular chip. --> + <dimen name="media_ttt_generic_icon_size_receiver">70dp</dimen> <!-- Window magnification --> <dimen name="magnification_border_drag_size">35dp</dimen> @@ -1138,6 +1144,12 @@ the shade (in alpha) --> <dimen name="lockscreen_shade_scrim_transition_distance">@dimen/lockscreen_shade_full_transition_distance</dimen> + <!-- Distance that it takes in order for the notifications scrim fade in to start. --> + <dimen name="lockscreen_shade_notifications_scrim_transition_delay">0dp</dimen> + + <!-- Distance that it takes for the notifications scrim to fully fade if after it started. --> + <dimen name="lockscreen_shade_notifications_scrim_transition_distance">@dimen/lockscreen_shade_scrim_transition_distance</dimen> + <!-- Distance that the full shade transition takes in order for the keyguard content on NotificationPanelViewController to fully fade (e.g. Clock & Smartspace) --> <dimen name="lockscreen_shade_npvc_keyguard_content_alpha_transition_distance">@dimen/lockscreen_shade_full_transition_distance</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index d230f33f4939..6dc6214fec65 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2184,7 +2184,7 @@ <!-- Text informing the user that their media is now playing on this device. [CHAR LIMIT=50] --> <string name="media_transfer_playing_this_device">Playing on this phone</string> <!-- Text informing the user that the media transfer has failed because something went wrong. [CHAR LIMIT=50] --> - <string name="media_transfer_failed">Something went wrong</string> + <string name="media_transfer_failed">Something went wrong. Try again.</string> <!-- Error message indicating that a control timed out while waiting for an update [CHAR_LIMIT=30] --> <string name="controls_error_timeout">Inactive, check app</string> diff --git a/packages/SystemUI/src/com/android/keyguard/BouncerPanelExpansionCalculator.kt b/packages/SystemUI/src/com/android/keyguard/BouncerPanelExpansionCalculator.kt new file mode 100644 index 000000000000..497d81f6bdc5 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/BouncerPanelExpansionCalculator.kt @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.keyguard + +import android.util.MathUtils + +object BouncerPanelExpansionCalculator { + /** + * Scale the alpha/position of the host view. + */ + @JvmStatic + fun getHostViewScaledExpansion(fraction: Float): Float { + return when { + fraction >= 0.9f -> 1f + fraction < 0.6 -> 0f + else -> (fraction - 0.6f) / 0.3f + } + } + + /** + * Scale the alpha/tint of the back scrim. + */ + @JvmStatic + fun getBackScrimScaledExpansion(fraction: Float): Float { + return MathUtils.constrain((fraction - 0.9f) / 0.1f, 0f, 1f) + } + + /** + * This will scale the alpha/position of the clock. + */ + @JvmStatic + fun getKeyguardClockScaledExpansion(fraction: Float): Float { + return MathUtils.constrain((fraction - 0.7f) / 0.3f, 0f, 1f) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java index b6910961bcb4..8c3e066849b9 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java @@ -37,7 +37,6 @@ import com.android.keyguard.dagger.KeyguardBouncerScope; import com.android.settingslib.Utils; import com.android.systemui.R; import com.android.systemui.plugins.ActivityStarter; -import com.android.systemui.statusbar.phone.KeyguardBouncer; import com.android.systemui.util.ViewController; import java.io.File; @@ -64,6 +63,7 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView> private ActivityStarter.OnDismissAction mDismissAction; private Runnable mCancelAction; + private int mTranslationY; private final KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() { @@ -322,12 +322,14 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView> /** * Fades and translates in/out the security screen. + * Fades in as expansion approaches 0. + * Animation duration is between 0.33f and 0.67f of panel expansion fraction. * @param fraction amount of the screen that should show. */ public void setExpansion(float fraction) { - float alpha = MathUtils.map(KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1, 1, 0, fraction); - mView.setAlpha(MathUtils.constrain(alpha, 0f, 1f)); - mView.setTranslationY(fraction * mView.getHeight()); + float scaledFraction = BouncerPanelExpansionCalculator.getHostViewScaledExpansion(fraction); + mView.setAlpha(MathUtils.constrain(1 - scaledFraction, 0f, 1f)); + mView.setTranslationY(scaledFraction * mTranslationY); } /** @@ -490,6 +492,8 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView> gravity = resources.getInteger(R.integer.keyguard_host_view_gravity); } + mTranslationY = resources + .getDimensionPixelSize(R.dimen.keyguard_host_view_translation_y); // Android SysUI uses a FrameLayout as the top-level, but Auto uses RelativeLayout. // We're just changing the gravity here though (which can't be applied to RelativeLayout), // so only attempt the update if mView is inside a FrameLayout. diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index 362fbed7055a..46a883194e25 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -152,6 +152,7 @@ public class KeyguardSecurityContainer extends FrameLayout { private SwipeListener mSwipeListener; private ViewMode mViewMode = new DefaultViewMode(); private @Mode int mCurrentMode = MODE_DEFAULT; + private int mWidth = -1; private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback = new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) { @@ -649,9 +650,11 @@ public class KeyguardSecurityContainer extends FrameLayout { protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); - // After a layout pass, we need to re-place the inner bouncer, as our bounds may have - // changed. - mViewMode.updateSecurityViewLocation(); + int width = right - left; + if (changed && mWidth != width) { + mWidth = width; + mViewMode.updateSecurityViewLocation(); + } } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java index 74659f71eb1f..e36e984380e2 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java @@ -211,6 +211,23 @@ public class LockIconViewController extends ViewController<LockIconView> impleme mDownDetected = false; updateBurnInOffsets(); updateVisibility(); + + updateAccessibility(); + } + + private void updateAccessibility() { + if (mAccessibilityManager.isTouchExplorationEnabled()) { + mView.setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View v) { + onLongPress(); + } + } + ); + } else { + mView.setOnClickListener(null); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt index a27b9cd357f4..b811c51c952b 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt @@ -116,7 +116,7 @@ class AuthRippleController @Inject constructor( } fun showRipple(biometricSourceType: BiometricSourceType?) { - if (!keyguardUpdateMonitor.isKeyguardVisible || + if (!(keyguardUpdateMonitor.isKeyguardVisible || keyguardUpdateMonitor.isDreaming) || keyguardUpdateMonitor.userNeedsStrongAuth()) { return } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index bc7a3f6f4b13..975e0c5b32cd 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -49,7 +49,6 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.Surface; import android.view.VelocityTracker; -import android.view.View; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; @@ -92,7 +91,7 @@ import kotlin.Unit; * Note that the current architecture is designed so that a single {@link UdfpsController} * controls/manages all UDFPS sensors. In other words, a single controller is registered with * {@link com.android.server.biometrics.sensors.fingerprint.FingerprintService}, and interfaces such - * as {@link FingerprintManager#onPointerDown(int, int, int, float, float)} or + * as {@link FingerprintManager#onPointerDown(long, int, int, int, float, float)} or * {@link IUdfpsOverlayController#showUdfpsOverlay} should all have * {@code sensorId} parameters. */ @@ -193,7 +192,7 @@ public class UdfpsController implements DozeReceiver { public class UdfpsOverlayController extends IUdfpsOverlayController.Stub { @Override - public void showUdfpsOverlay(int sensorId, int reason, + public void showUdfpsOverlay(long requestId, int sensorId, int reason, @NonNull IUdfpsOverlayControllerCallback callback) { mFgExecutor.execute( () -> UdfpsController.this.showUdfpsOverlay(new UdfpsControllerOverlay( @@ -203,8 +202,10 @@ public class UdfpsController implements DozeReceiver { mKeyguardUpdateMonitor, mDialogManager, mDumpManager, mLockscreenShadeTransitionController, mConfigurationController, mSystemClock, mKeyguardStateController, - mUnlockedScreenOffAnimationController, mSensorProps, mHbmProvider, - reason, callback, UdfpsController.this::onTouch, + mUnlockedScreenOffAnimationController, mSensorProps, + mHbmProvider, requestId, reason, callback, + (view, event, fromUdfpsView) -> + onTouch(requestId, event, fromUdfpsView), mActivityLaunchAnimator))); } @@ -318,7 +319,8 @@ public class UdfpsController implements DozeReceiver { if (mOverlay == null || mOverlay.isHiding()) { return false; } - return onTouch(mOverlay.getOverlayView(), event, false); + // TODO(b/225068271): may not be correct but no way to get the id yet + return onTouch(mOverlay.getRequestId(), event, false); } /** @@ -342,8 +344,18 @@ public class UdfpsController implements DozeReceiver { && getSensorLocation().contains(x, y); } - private boolean onTouch(@NonNull View view, @NonNull MotionEvent event, boolean fromUdfpsView) { - UdfpsView udfpsView = (UdfpsView) view; + private boolean onTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) { + if (mOverlay == null) { + Log.w(TAG, "ignoring onTouch with null overlay"); + return false; + } + if (!mOverlay.matchesRequestId(requestId)) { + Log.w(TAG, "ignoring stale touch event: " + requestId + " current: " + + mOverlay.getRequestId()); + return false; + } + + final UdfpsView udfpsView = mOverlay.getOverlayView(); final boolean isIlluminationRequested = udfpsView.isIlluminationRequested(); boolean handled = false; switch (event.getActionMasked()) { @@ -453,7 +465,7 @@ public class UdfpsController implements DozeReceiver { // Do nothing to stay in portrait mode. } - onFingerDown(x, y, minor, major); + onFingerDown(requestId, x, y, minor, major); Log.v(TAG, "onTouch | finger down: " + touchInfo); mTouchLogTime = mSystemClock.elapsedRealtime(); mPowerManager.userActivity(mSystemClock.uptimeMillis(), @@ -465,7 +477,7 @@ public class UdfpsController implements DozeReceiver { } } else { Log.v(TAG, "onTouch | finger outside"); - onFingerUp(udfpsView); + onFingerUp(requestId, udfpsView); } } Trace.endSection(); @@ -482,7 +494,7 @@ public class UdfpsController implements DozeReceiver { } Log.v(TAG, "onTouch | finger up"); mAttemptedToDismissKeyguard = false; - onFingerUp(udfpsView); + onFingerUp(requestId, udfpsView); mFalsingManager.isFalseTouch(UDFPS_AUTHENTICATION); Trace.endSection(); break; @@ -679,7 +691,7 @@ public class UdfpsController implements DozeReceiver { // Reset the controller back to its starting state. final UdfpsView oldView = mOverlay.getOverlayView(); if (oldView != null) { - onFingerUp(oldView); + onFingerUp(mOverlay.getRequestId(), oldView); } final boolean removed = mOverlay.hide(); if (mKeyguardViewManager.isShowingAlternateAuth()) { @@ -710,6 +722,8 @@ public class UdfpsController implements DozeReceiver { return; } + // TODO(b/225068271): this may not be correct but there isn't a way to track it + final long requestId = mOverlay != null ? mOverlay.getRequestId() : -1; mAodInterruptRunnable = () -> { mIsAodInterruptActive = true; // Since the sensor that triggers the AOD interrupt doesn't provide @@ -719,10 +733,10 @@ public class UdfpsController implements DozeReceiver { mCancelAodTimeoutAction = mFgExecutor.executeDelayed(this::onCancelUdfps, AOD_INTERRUPT_TIMEOUT_MILLIS); // using a hard-coded value for major and minor until it is available from the sensor - onFingerDown(screenX, screenY, minor, major); + onFingerDown(requestId, screenX, screenY, minor, major); }; - if (mScreenOn && mAodInterruptRunnable != null) { + if (mScreenOn) { mAodInterruptRunnable.run(); mAodInterruptRunnable = null; } @@ -755,7 +769,7 @@ public class UdfpsController implements DozeReceiver { */ void onCancelUdfps() { if (mOverlay != null && mOverlay.getOverlayView() != null) { - onFingerUp(mOverlay.getOverlayView()); + onFingerUp(mOverlay.getRequestId(), mOverlay.getOverlayView()); } if (!mIsAodInterruptActive) { return; @@ -771,12 +785,17 @@ public class UdfpsController implements DozeReceiver { return mOnFingerDown; } - private void onFingerDown(int x, int y, float minor, float major) { + private void onFingerDown(long requestId, int x, int y, float minor, float major) { mExecution.assertIsMainThread(); if (mOverlay == null) { Log.w(TAG, "Null request in onFingerDown"); return; } + if (!mOverlay.matchesRequestId(requestId)) { + Log.w(TAG, "Mismatched fingerDown: " + requestId + + " current: " + mOverlay.getRequestId()); + return; + } if (mOverlay.getAnimationViewController() instanceof UdfpsKeyguardViewController && !mStatusBarStateController.isDozing()) { @@ -791,14 +810,14 @@ public class UdfpsController implements DozeReceiver { } } mOnFingerDown = true; - mFingerprintManager.onPointerDown(mSensorProps.sensorId, x, y, minor, major); + mFingerprintManager.onPointerDown(requestId, mSensorProps.sensorId, x, y, minor, major); Trace.endAsyncSection("UdfpsController.e2e.onPointerDown", 0); final UdfpsView view = mOverlay.getOverlayView(); if (view != null) { Trace.beginAsyncSection("UdfpsController.e2e.startIllumination", 0); view.startIllumination(() -> { - mFingerprintManager.onUiReady(mSensorProps.sensorId); + mFingerprintManager.onUiReady(requestId, mSensorProps.sensorId); mLatencyTracker.onActionEnd(LatencyTracker.ACTION_UDFPS_ILLUMINATE); Trace.endAsyncSection("UdfpsController.e2e.startIllumination", 0); }); @@ -809,12 +828,12 @@ public class UdfpsController implements DozeReceiver { } } - private void onFingerUp(@NonNull UdfpsView view) { + private void onFingerUp(long requestId, @NonNull UdfpsView view) { mExecution.assertIsMainThread(); mActivePointerId = -1; mAcquiredReceived = false; if (mOnFingerDown) { - mFingerprintManager.onPointerUp(mSensorProps.sensorId); + mFingerprintManager.onPointerUp(requestId, mSensorProps.sensorId); for (Callback cb : mCallbacks) { cb.onFingerUp(); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt index 086894d2e670..ee43e932b344 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt @@ -80,6 +80,7 @@ class UdfpsControllerOverlay( private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController, private val sensorProps: FingerprintSensorPropertiesInternal, private var hbmProvider: UdfpsHbmProvider, + val requestId: Long, @ShowReason val requestReason: Int, private val controllerCallback: IUdfpsOverlayControllerCallback, private val onTouch: (View, MotionEvent, Boolean) -> Boolean, @@ -276,6 +277,9 @@ class UdfpsControllerOverlay( } } + /** Checks if the id is relevant for this overlay. */ + fun matchesRequestId(id: Long): Boolean = requestId == -1L || requestId == id + private fun WindowManager.LayoutParams.updateForLocation( location: SensorLocationInternal, animation: UdfpsAnimationViewController<*>? diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt index f78929f75b04..a9f340854689 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt @@ -35,6 +35,7 @@ import com.android.systemui.power.PowerUI import com.android.systemui.recents.Recents import com.android.systemui.shortcut.ShortcutKeyDispatcher import com.android.systemui.statusbar.notification.InstantAppNotifier +import com.android.systemui.statusbar.phone.KeyguardLiftController import com.android.systemui.theme.ThemeOverlayController import com.android.systemui.toast.ToastUI import com.android.systemui.usb.StorageNotification @@ -198,4 +199,10 @@ abstract class SystemUICoreStartableModule { @IntoMap @ClassKey(WMShell::class) abstract fun bindWMShell(sysui: WMShell): CoreStartable + + /** Inject into KeyguardLiftController. */ + @Binds + @IntoMap + @ClassKey(KeyguardLiftController::class) + abstract fun bindKeyguardLiftController(sysui: KeyguardLiftController): CoreStartable }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java index d2ab61149d26..59a17bad5069 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java @@ -88,10 +88,6 @@ public class DreamOverlayStatusBarView extends ConstraintLayout { fetchStatusIconForResId(R.id.dream_overlay_priority_mode)); } - void showIcon(@StatusIconType int iconType, boolean show) { - showIcon(iconType, show, null); - } - void showIcon(@StatusIconType int iconType, boolean show, @Nullable String contentDescription) { View icon = mStatusIcons.get(iconType); if (icon == null) { diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java index a25a7423770e..761f28c5ac3b 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java @@ -16,6 +16,7 @@ package com.android.systemui.dreams; +import android.annotation.Nullable; import android.app.AlarmManager; import android.content.res.Resources; import android.hardware.SensorPrivacyManager; @@ -45,6 +46,7 @@ import com.android.systemui.util.time.DateFormatUtil; import java.util.Locale; import java.util.Map; +import java.util.concurrent.Executor; import javax.inject.Inject; @@ -62,6 +64,9 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve private final IndividualSensorPrivacyController mSensorPrivacyController; private final NotificationListener mNotificationListener; private final ZenModeController mZenModeController; + private final Executor mMainExecutor; + + private boolean mIsAttached; private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder() .clearCapabilities() @@ -131,6 +136,7 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve public DreamOverlayStatusBarViewController( DreamOverlayStatusBarView view, @Main Resources resources, + @Main Executor mainExecutor, ConnectivityManager connectivityManager, TouchInsetManager.TouchInsetSession touchInsetSession, AlarmManager alarmManager, @@ -141,6 +147,7 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve ZenModeController zenModeController) { super(view); mResources = resources; + mMainExecutor = mainExecutor; mConnectivityManager = connectivityManager; mTouchInsetSession = touchInsetSession; mAlarmManager = alarmManager; @@ -157,6 +164,8 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve @Override protected void onViewAttached() { + mIsAttached = true; + updateNotificationsStatusIcon(); mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback); @@ -181,6 +190,8 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve mNextAlarmController.removeCallback(mNextAlarmCallback); mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); mTouchInsetSession.clear(); + + mIsAttached = false; } private void updateWifiUnavailableStatusIcon() { @@ -189,14 +200,14 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve mConnectivityManager.getActiveNetwork()); final boolean available = capabilities != null && capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI); - mView.showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, !available); + showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, !available); } private void updateAlarmStatusIcon() { final AlarmManager.AlarmClockInfo alarm = mAlarmManager.getNextAlarmClock(UserHandle.USER_CURRENT); final boolean hasAlarm = alarm != null && alarm.getTriggerTime() > 0; - mView.showIcon( + showIcon( DreamOverlayStatusBarView.STATUS_ICON_ALARM_SET, hasAlarm, hasAlarm ? buildAlarmContentDescription(alarm) : null); @@ -215,7 +226,7 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve .isSensorBlocked(SensorPrivacyManager.Sensors.MICROPHONE); final boolean cameraBlocked = mSensorPrivacyController .isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA); - mView.showIcon( + showIcon( DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, micBlocked && cameraBlocked); } @@ -230,7 +241,7 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve final StatusBarNotification[] notifications = mNotificationListener.getActiveNotifications(); final int notificationCount = notifications != null ? notifications.length : 0; - mView.showIcon( + showIcon( DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS, notificationCount > 0, notificationCount > 0 @@ -246,8 +257,23 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve } private void updatePriorityModeStatusIcon() { - mView.showIcon( + showIcon( DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, mZenModeController.getZen() != Settings.Global.ZEN_MODE_OFF); } + + private void showIcon(@DreamOverlayStatusBarView.StatusIconType int iconType, boolean show) { + showIcon(iconType, show, null); + } + + private void showIcon( + @DreamOverlayStatusBarView.StatusIconType int iconType, + boolean show, + @Nullable String contentDescription) { + mMainExecutor.execute(() -> { + if (mIsAttached) { + mView.showIcon(iconType, show, contentDescription); + } + }); + } } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockDateComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockDateComplication.java index 6861c7479161..1ca06b25aa9f 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockDateComplication.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockDateComplication.java @@ -16,32 +16,32 @@ package com.android.systemui.dreams.complication; -import static com.android.systemui.dreams.complication.dagger.DreamClockDateComplicationComponent.DreamClockDateComplicationModule.DREAM_CLOCK_DATE_COMPLICATION_LAYOUT_PARAMS; -import static com.android.systemui.dreams.complication.dagger.DreamClockDateComplicationComponent.DreamClockDateComplicationModule.DREAM_CLOCK_DATE_COMPLICATION_VIEW; +import static com.android.systemui.dreams.complication.dagger.DreamClockDateComplicationModule.DREAM_CLOCK_DATE_COMPLICATION_LAYOUT_PARAMS; +import static com.android.systemui.dreams.complication.dagger.DreamClockDateComplicationModule.DREAM_CLOCK_DATE_COMPLICATION_VIEW; import android.content.Context; import android.view.View; import com.android.systemui.CoreStartable; import com.android.systemui.dreams.DreamOverlayStateController; -import com.android.systemui.dreams.complication.dagger.DreamClockDateComplicationComponent; import javax.inject.Inject; import javax.inject.Named; +import javax.inject.Provider; /** * Clock Date Complication that produce Clock Date view holder. */ public class DreamClockDateComplication implements Complication { - DreamClockDateComplicationComponent.Factory mComponentFactory; + private final Provider<DreamClockDateViewHolder> mDreamClockDateViewHolderProvider; /** * Default constructor for {@link DreamClockDateComplication}. */ @Inject public DreamClockDateComplication( - DreamClockDateComplicationComponent.Factory componentFactory) { - mComponentFactory = componentFactory; + Provider<DreamClockDateViewHolder> dreamClockDateViewHolderProvider) { + mDreamClockDateViewHolderProvider = dreamClockDateViewHolderProvider; } @Override @@ -54,11 +54,11 @@ public class DreamClockDateComplication implements Complication { */ @Override public ViewHolder createView(ComplicationViewModel model) { - return mComponentFactory.create().getViewHolder(); + return mDreamClockDateViewHolderProvider.get(); } /** - * {@link CoreStartable} responsbile for registering {@link DreamClockDateComplication} with + * {@link CoreStartable} responsible for registering {@link DreamClockDateComplication} with * SystemUI. */ public static class Registrant extends CoreStartable { @@ -84,7 +84,7 @@ public class DreamClockDateComplication implements Complication { } /** - * ViewHolder to contain value/logic associated with a Clock Date Complication View. + * {@link ViewHolder} to contain value/logic associated with {@link DreamClockDateComplication}. */ public static class DreamClockDateViewHolder implements ViewHolder { private final View mView; diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java index 936767a60233..7f67ecd19175 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java @@ -16,32 +16,32 @@ package com.android.systemui.dreams.complication; -import static com.android.systemui.dreams.complication.dagger.DreamClockTimeComplicationComponent.DreamClockTimeComplicationModule.DREAM_CLOCK_TIME_COMPLICATION_LAYOUT_PARAMS; -import static com.android.systemui.dreams.complication.dagger.DreamClockTimeComplicationComponent.DreamClockTimeComplicationModule.DREAM_CLOCK_TIME_COMPLICATION_VIEW; +import static com.android.systemui.dreams.complication.dagger.DreamClockTimeComplicationModule.DREAM_CLOCK_TIME_COMPLICATION_LAYOUT_PARAMS; +import static com.android.systemui.dreams.complication.dagger.DreamClockTimeComplicationModule.DREAM_CLOCK_TIME_COMPLICATION_VIEW; import android.content.Context; import android.view.View; import com.android.systemui.CoreStartable; import com.android.systemui.dreams.DreamOverlayStateController; -import com.android.systemui.dreams.complication.dagger.DreamClockTimeComplicationComponent; import javax.inject.Inject; import javax.inject.Named; +import javax.inject.Provider; /** * Clock Time Complication that produce Clock Time view holder. */ public class DreamClockTimeComplication implements Complication { - DreamClockTimeComplicationComponent.Factory mComponentFactory; + private final Provider<DreamClockTimeViewHolder> mDreamClockTimeViewHolderProvider; /** * Default constructor for {@link DreamClockTimeComplication}. */ @Inject public DreamClockTimeComplication( - DreamClockTimeComplicationComponent.Factory componentFactory) { - mComponentFactory = componentFactory; + Provider<DreamClockTimeViewHolder> dreamClockTimeViewHolderProvider) { + mDreamClockTimeViewHolderProvider = dreamClockTimeViewHolderProvider; } @Override @@ -54,11 +54,11 @@ public class DreamClockTimeComplication implements Complication { */ @Override public ViewHolder createView(ComplicationViewModel model) { - return mComponentFactory.create().getViewHolder(); + return mDreamClockTimeViewHolderProvider.get(); } /** - * {@link CoreStartable} responsbile for registering {@link DreamClockTimeComplication} with + * {@link CoreStartable} responsible for registering {@link DreamClockTimeComplication} with * SystemUI. */ public static class Registrant extends CoreStartable { @@ -84,7 +84,7 @@ public class DreamClockTimeComplication implements Complication { } /** - * ViewHolder to contain value/logic associated with a Clock Time Complication View. + * {@link ViewHolder} to contain value/logic associated with {@link DreamClockTimeComplication}. */ public static class DreamClockTimeViewHolder implements ViewHolder { private final View mView; diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockDateComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockDateComplicationComponent.java deleted file mode 100644 index dd7f10c204e1..000000000000 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockDateComplicationComponent.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.dreams.complication.dagger; - - -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import com.android.internal.util.Preconditions; -import com.android.systemui.R; -import com.android.systemui.dreams.complication.ComplicationLayoutParams; -import com.android.systemui.dreams.complication.DreamClockDateComplication.DreamClockDateViewHolder; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; - -import javax.inject.Named; -import javax.inject.Scope; - -import dagger.Module; -import dagger.Provides; -import dagger.Subcomponent; - -/** - * {@link DreamClockDateComplicationComponent} is responsible for generating dependencies - * surrounding the - * Clock Date {@link com.android.systemui.dreams.complication.Complication}, such as the layout - * details. - */ -@Subcomponent(modules = { - DreamClockDateComplicationComponent.DreamClockDateComplicationModule.class, -}) -@DreamClockDateComplicationComponent.DreamClockDateComplicationScope -public interface DreamClockDateComplicationComponent { - /** - * Creates {@link DreamClockDateViewHolder}. - */ - DreamClockDateViewHolder getViewHolder(); - - @Documented - @Retention(RUNTIME) - @Scope - @interface DreamClockDateComplicationScope { - } - - /** - * Generates {@link DreamClockDateComplicationComponent}. - */ - @Subcomponent.Factory - interface Factory { - DreamClockDateComplicationComponent create(); - } - - /** - * Scoped values for {@link DreamClockDateComplicationComponent}. - */ - @Module - interface DreamClockDateComplicationModule { - String DREAM_CLOCK_DATE_COMPLICATION_VIEW = "clock_date_complication_view"; - String DREAM_CLOCK_DATE_COMPLICATION_LAYOUT_PARAMS = - "clock_date_complication_layout_params"; - // Order weight of insert into parent container - int INSERT_ORDER_WEIGHT = 2; - - /** - * Provides the complication view. - */ - @Provides - @DreamClockDateComplicationScope - @Named(DREAM_CLOCK_DATE_COMPLICATION_VIEW) - static View provideComplicationView(LayoutInflater layoutInflater) { - return Preconditions.checkNotNull( - layoutInflater.inflate(R.layout.dream_overlay_complication_clock_date, - null, false), - "R.layout.dream_overlay_complication_clock_date did not properly inflated"); - } - - /** - * Provides the layout parameters for the complication view. - */ - @Provides - @DreamClockDateComplicationScope - @Named(DREAM_CLOCK_DATE_COMPLICATION_LAYOUT_PARAMS) - static ComplicationLayoutParams provideLayoutParams() { - return new ComplicationLayoutParams(0, - ViewGroup.LayoutParams.WRAP_CONTENT, - ComplicationLayoutParams.POSITION_BOTTOM - | ComplicationLayoutParams.POSITION_START, - ComplicationLayoutParams.DIRECTION_END, - INSERT_ORDER_WEIGHT); - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockDateComplicationModule.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockDateComplicationModule.java new file mode 100644 index 000000000000..eb2fc5d1a93e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockDateComplicationModule.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.dreams.complication.dagger; + + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.android.internal.util.Preconditions; +import com.android.systemui.R; +import com.android.systemui.dreams.complication.ComplicationLayoutParams; +import com.android.systemui.dreams.complication.DreamClockDateComplication; + +import javax.inject.Named; + +import dagger.Module; +import dagger.Provides; + +/** + * Module for providing {@link DreamClockDateComplication}. + */ +@Module +public interface DreamClockDateComplicationModule { + String DREAM_CLOCK_DATE_COMPLICATION_VIEW = "clock_date_complication_view"; + String DREAM_CLOCK_DATE_COMPLICATION_LAYOUT_PARAMS = + "clock_date_complication_layout_params"; + // Order weight of insert into parent container + //TODO(b/217199227): move to a single location. + int INSERT_ORDER_WEIGHT = 2; + + /** + * Provides the complication view. + */ + @Provides + @Named(DREAM_CLOCK_DATE_COMPLICATION_VIEW) + static View provideComplicationView(LayoutInflater layoutInflater) { + return Preconditions.checkNotNull( + layoutInflater.inflate(R.layout.dream_overlay_complication_clock_date, + null, false), + "R.layout.dream_overlay_complication_clock_date did not properly inflated"); + } + + /** + * Provides the layout parameters for the complication view. + */ + @Provides + @Named(DREAM_CLOCK_DATE_COMPLICATION_LAYOUT_PARAMS) + static ComplicationLayoutParams provideLayoutParams() { + return new ComplicationLayoutParams(0, + ViewGroup.LayoutParams.WRAP_CONTENT, + ComplicationLayoutParams.POSITION_BOTTOM + | ComplicationLayoutParams.POSITION_START, + ComplicationLayoutParams.DIRECTION_END, + INSERT_ORDER_WEIGHT); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationComponent.java deleted file mode 100644 index de11b61f4759..000000000000 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationComponent.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.dreams.complication.dagger; - - -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextClock; - -import com.android.internal.util.Preconditions; -import com.android.systemui.R; -import com.android.systemui.dreams.complication.ComplicationLayoutParams; -import com.android.systemui.dreams.complication.DreamClockTimeComplication.DreamClockTimeViewHolder; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; - -import javax.inject.Named; -import javax.inject.Scope; - -import dagger.Module; -import dagger.Provides; -import dagger.Subcomponent; - -/** - * {@link DreamClockTimeComplicationComponent} is responsible for generating dependencies - * surrounding the - * Clock Time {@link com.android.systemui.dreams.complication.Complication}, such as the layout - * details. - */ -@Subcomponent(modules = { - DreamClockTimeComplicationComponent.DreamClockTimeComplicationModule.class, -}) -@DreamClockTimeComplicationComponent.DreamClockTimeComplicationScope -public interface DreamClockTimeComplicationComponent { - /** - * Creates {@link DreamClockTimeViewHolder}. - */ - DreamClockTimeViewHolder getViewHolder(); - - @Documented - @Retention(RUNTIME) - @Scope - @interface DreamClockTimeComplicationScope { - } - - /** - * Generates {@link DreamClockTimeComplicationComponent}. - */ - @Subcomponent.Factory - interface Factory { - DreamClockTimeComplicationComponent create(); - } - - /** - * Scoped values for {@link DreamClockTimeComplicationComponent}. - */ - @Module - interface DreamClockTimeComplicationModule { - String DREAM_CLOCK_TIME_COMPLICATION_VIEW = "clock_time_complication_view"; - String DREAM_CLOCK_TIME_COMPLICATION_LAYOUT_PARAMS = - "clock_time_complication_layout_params"; - // Order weight of insert into parent container - int INSERT_ORDER_WEIGHT = 0; - String TAG_WEIGHT = "'wght' "; - int WEIGHT = 200; - - /** - * Provides the complication view. - */ - @Provides - @DreamClockTimeComplicationScope - @Named(DREAM_CLOCK_TIME_COMPLICATION_VIEW) - static View provideComplicationView(LayoutInflater layoutInflater) { - final TextClock view = Preconditions.checkNotNull((TextClock) - layoutInflater.inflate(R.layout.dream_overlay_complication_clock_time, - null, false), - "R.layout.dream_overlay_complication_clock_time did not properly inflated"); - view.setFontVariationSettings(TAG_WEIGHT + WEIGHT); - return view; - } - - /** - * Provides the layout parameters for the complication view. - */ - @Provides - @DreamClockTimeComplicationScope - @Named(DREAM_CLOCK_TIME_COMPLICATION_LAYOUT_PARAMS) - static ComplicationLayoutParams provideLayoutParams() { - return new ComplicationLayoutParams(0, - ViewGroup.LayoutParams.WRAP_CONTENT, - ComplicationLayoutParams.POSITION_BOTTOM - | ComplicationLayoutParams.POSITION_START, - ComplicationLayoutParams.DIRECTION_UP, - INSERT_ORDER_WEIGHT); - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationModule.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationModule.java new file mode 100644 index 000000000000..3ad7d3ded749 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationModule.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.dreams.complication.dagger; + + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextClock; + +import com.android.internal.util.Preconditions; +import com.android.systemui.R; +import com.android.systemui.dreams.complication.ComplicationLayoutParams; +import com.android.systemui.dreams.complication.DreamClockTimeComplication; + +import javax.inject.Named; + +import dagger.Module; +import dagger.Provides; + +/** + * Module for providing {@link DreamClockTimeComplication}. + */ +@Module +public interface DreamClockTimeComplicationModule { + String DREAM_CLOCK_TIME_COMPLICATION_VIEW = "clock_time_complication_view"; + String DREAM_CLOCK_TIME_COMPLICATION_LAYOUT_PARAMS = + "clock_time_complication_layout_params"; + // Order weight of insert into parent container + //TODO(b/217199227): move to a single location. + int INSERT_ORDER_WEIGHT = 0; + String TAG_WEIGHT = "'wght' "; + int WEIGHT = 200; + + /** + * Provides the complication view. + */ + @Provides + @Named(DREAM_CLOCK_TIME_COMPLICATION_VIEW) + static View provideComplicationView(LayoutInflater layoutInflater) { + final TextClock view = Preconditions.checkNotNull((TextClock) + layoutInflater.inflate(R.layout.dream_overlay_complication_clock_time, + null, false), + "R.layout.dream_overlay_complication_clock_time did not properly inflated"); + view.setFontVariationSettings(TAG_WEIGHT + WEIGHT); + return view; + } + + /** + * Provides the layout parameters for the complication view. + */ + @Provides + @Named(DREAM_CLOCK_TIME_COMPLICATION_LAYOUT_PARAMS) + static ComplicationLayoutParams provideLayoutParams() { + return new ComplicationLayoutParams(0, + ViewGroup.LayoutParams.WRAP_CONTENT, + ComplicationLayoutParams.POSITION_BOTTOM + | ComplicationLayoutParams.POSITION_START, + ComplicationLayoutParams.DIRECTION_UP, + INSERT_ORDER_WEIGHT); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java index 8e4fb3783f4a..62a4140c6745 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java @@ -24,10 +24,12 @@ import dagger.Module; * Module for all components with corresponding dream layer complications registered in * {@link SystemUIBinder}. */ -@Module(subcomponents = { - DreamClockTimeComplicationComponent.class, - DreamClockDateComplicationComponent.class, - DreamWeatherComplicationComponent.class, -}) +@Module(includes = { + DreamClockDateComplicationModule.class, + DreamClockTimeComplicationModule.class, + }, + subcomponents = { + DreamWeatherComplicationComponent.class, + }) public interface RegisteredComplicationsModule { } diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java index 2db3de173257..61cfe925f640 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java @@ -145,7 +145,7 @@ public class Flags { /***************************************/ // 900 - media public static final BooleanFlag MEDIA_TAP_TO_TRANSFER = new BooleanFlag(900, true); - public static final BooleanFlag MEDIA_SESSION_ACTIONS = new BooleanFlag(901, true); + public static final BooleanFlag MEDIA_SESSION_ACTIONS = new BooleanFlag(901, false); public static final BooleanFlag MEDIA_SESSION_LAYOUT = new BooleanFlag(902, true); public static final BooleanFlag MEDIA_NEARBY_DEVICES = new BooleanFlag(903, true); public static final BooleanFlag MEDIA_MUTE_AWAIT = new BooleanFlag(904, true); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index acad30b4276f..bf464ecdec98 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -2347,7 +2347,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, // Hack level over 9000: To speed up wake-and-unlock sequence, force it to report // the next draw from here, so we don't have to wait for window manager to signal // this to our ViewRootImpl. - mKeyguardViewControllerLazy.get().getViewRootImpl().setReportNextDraw(); + mKeyguardViewControllerLazy.get().getViewRootImpl().setReportNextDraw( + false /* syncBuffer */); mScreenOnCoordinator.setWakeAndUnlocking(false); } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java index c69f947f5f3f..71dfa7433c32 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java @@ -16,10 +16,8 @@ package com.android.systemui.keyguard.dagger; -import android.annotation.Nullable; import android.app.trust.TrustManager; import android.content.Context; -import android.content.pm.PackageManager; import android.os.PowerManager; import com.android.internal.jank.InteractionJankMonitor; @@ -44,18 +42,14 @@ import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.navigationbar.NavigationModeController; -import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.SysuiStatusBarStateController; -import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.DozeParameters; -import com.android.systemui.statusbar.phone.KeyguardLiftController; import com.android.systemui.statusbar.phone.ScreenOffAnimationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.util.DeviceConfigProxy; -import com.android.systemui.util.sensors.AsyncSensorManager; import java.util.concurrent.Executor; @@ -133,20 +127,4 @@ public class KeyguardModule { notificationShadeWindowController, activityLaunchAnimator); } - - @SysUISingleton - @Provides - @Nullable - static KeyguardLiftController provideKeyguardLiftController( - Context context, - StatusBarStateController statusBarStateController, - AsyncSensorManager asyncSensorManager, - KeyguardUpdateMonitor keyguardUpdateMonitor, - DumpManager dumpManager) { - if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)) { - return null; - } - return new KeyguardLiftController(statusBarStateController, asyncSensorManager, - keyguardUpdateMonitor, dumpManager); - } } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index 510d15bd7b73..ffdd5376b12e 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -137,7 +137,6 @@ public class MediaControlPanel { private MediaCarouselController mMediaCarouselController; private final MediaOutputDialogFactory mMediaOutputDialogFactory; private final FalsingManager mFalsingManager; - private final MediaFlags mMediaFlags; // Used for swipe-to-dismiss logging. protected boolean mIsImpressed = false; @@ -156,7 +155,7 @@ public class MediaControlPanel { Lazy<MediaDataManager> lazyMediaDataManager, MediaOutputDialogFactory mediaOutputDialogFactory, MediaCarouselController mediaCarouselController, - FalsingManager falsingManager, MediaFlags mediaFlags, SystemClock systemClock) { + FalsingManager falsingManager, SystemClock systemClock) { mContext = context; mBackgroundExecutor = backgroundExecutor; mActivityStarter = activityStarter; @@ -167,7 +166,6 @@ public class MediaControlPanel { mMediaOutputDialogFactory = mediaOutputDialogFactory; mMediaCarouselController = mediaCarouselController; mFalsingManager = falsingManager; - mMediaFlags = mediaFlags; mSystemClock = systemClock; loadDimens(); @@ -506,9 +504,8 @@ public class MediaControlPanel { List<MediaAction> actionIcons = data.getActions(); List<Integer> actionsWhenCollapsed = data.getActionsToShowInCompact(); - // If the session actions flag is enabled, but we're still using the regular layout, use - // the session actions anyways - if (mMediaFlags.areMediaSessionActionsEnabled() && data.getSemanticActions() != null) { + // If we got session actions, use those instead + if (data.getSemanticActions() != null) { MediaButton semanticActions = data.getSemanticActions(); actionIcons = new ArrayList<MediaAction>(); diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt index 9e14fe91f21d..56d8c6486631 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt @@ -610,7 +610,8 @@ class MediaDataManager( var actionIcons: List<MediaAction> = emptyList() var actionsToShowCollapsed: List<Int> = emptyList() var semanticActions: MediaButton? = null - if (mediaFlags.areMediaSessionActionsEnabled() && mediaController.playbackState != null) { + if (mediaFlags.areMediaSessionActionsEnabled(sbn.packageName, sbn.user) && + mediaController.playbackState != null) { semanticActions = createActionsFromState(sbn.packageName, mediaController) } else { val actions = createActionsFromNotification(sbn) @@ -726,7 +727,7 @@ class MediaDataManager( } } - // Finally, assign the remaining button slots: C A play/pause B D + // Finally, assign the remaining button slots: play/pause A B C D // A = previous, else custom action (if not reserved) // B = next, else custom action (if not reserved) // C and D are always custom actions diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaFlags.kt b/packages/SystemUI/src/com/android/systemui/media/MediaFlags.kt index dd35a9a81399..59237d936d72 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaFlags.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaFlags.kt @@ -16,6 +16,8 @@ package com.android.systemui.media +import android.app.StatusBarManager +import android.os.UserHandle import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags @@ -26,16 +28,17 @@ class MediaFlags @Inject constructor(private val featureFlags: FeatureFlags) { /** * Check whether media control actions should be based on PlaybackState instead of notification */ - fun areMediaSessionActionsEnabled(): Boolean { - return featureFlags.isEnabled(Flags.MEDIA_SESSION_ACTIONS) + fun areMediaSessionActionsEnabled(packageName: String, user: UserHandle): Boolean { + val enabled = StatusBarManager.useMediaSessionActionsForApp(packageName, user) + // Allow global override with flag + return enabled || featureFlags.isEnabled(Flags.MEDIA_SESSION_ACTIONS) } /** * Check whether media controls should use the new session-based layout */ fun useMediaSessionLayout(): Boolean { - return featureFlags.isEnabled(Flags.MEDIA_SESSION_ACTIONS) && - featureFlags.isEnabled(Flags.MEDIA_SESSION_LAYOUT) + return featureFlags.isEnabled(Flags.MEDIA_SESSION_LAYOUT) } /** diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt index 7a4dee294dd6..d472aeee1073 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt @@ -812,7 +812,7 @@ class MediaHierarchyManager @Inject constructor( @TransformationType fun calculateTransformationType(): Int { if (isTransitioningToFullShade) { - if (inSplitShade) { + if (inSplitShade && areGuidedTransitionHostsVisible()) { return TRANSFORMATION_TYPE_TRANSITION } return TRANSFORMATION_TYPE_FADE @@ -829,6 +829,11 @@ class MediaHierarchyManager @Inject constructor( return TRANSFORMATION_TYPE_TRANSITION } + private fun areGuidedTransitionHostsVisible(): Boolean { + return getHost(previousLocation)?.visible == true && + getHost(desiredLocation)?.visible == true + } + /** * @return the current transformation progress if we're in a guided transformation and -1 * otherwise diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt index 3961f079748b..e4b8874ff601 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt @@ -128,19 +128,21 @@ class MediaTttCommandLineHelper @Inject constructor( as StatusBarManager val routeInfo = MediaRoute2Info.Builder("id", "Test Name") .addFeature("feature") - .setPackageName(TEST_PACKAGE_NAME) - .build() + if (args.size >= 2 && args[1] == "useAppIcon=true") { + routeInfo.setPackageName(TEST_PACKAGE_NAME) + } statusBarManager.updateMediaTapToTransferReceiverDisplay( displayState, - routeInfo, + routeInfo.build(), null, null ) } override fun help(pw: PrintWriter) { - pw.println("Usage: adb shell cmd statusbar $RECEIVER_COMMAND <chipState>") + pw.println("Usage: adb shell cmd statusbar $RECEIVER_COMMAND " + + "<chipState> useAppIcon=[true|false]") } } } diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt index 3d5b3a3f70df..54b0c1345601 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt @@ -31,6 +31,7 @@ import android.view.MotionEvent import android.view.View import android.view.ViewGroup import android.view.WindowManager +import android.widget.LinearLayout import com.android.internal.widget.CachingIconView import com.android.settingslib.Utils import com.android.systemui.R @@ -137,6 +138,11 @@ abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>( abstract fun updateChipView(chipInfo: T, currentChipView: ViewGroup) /** + * Returns the size that the icon should be, or null if no size override is needed. + */ + open fun getIconSize(isAppIcon: Boolean): Int? = null + + /** * An internal method to set the icon on the view. * * This is in the common superclass since both the sender and the receiver show an icon. @@ -151,35 +157,47 @@ abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>( appNameOverride: CharSequence? = null, ) { val appIconView = currentChipView.requireViewById<CachingIconView>(R.id.app_icon) - val appInfo = getAppInfo(appPackageName) - appIconView.contentDescription = appNameOverride ?: appInfo.appName - appIconView.setImageDrawable(appIconDrawableOverride ?: appInfo.appIcon) + val iconInfo = getIconInfo(appPackageName) + + getIconSize(iconInfo.isAppIcon)?.let { size -> + val lp = appIconView.layoutParams + lp.width = size + lp.height = size + appIconView.layoutParams = lp + } + + appIconView.contentDescription = appNameOverride ?: iconInfo.iconName + appIconView.setImageDrawable(appIconDrawableOverride ?: iconInfo.icon) } /** - * Returns the app name and icon of the app playing media, or a default name and icon if we - * can't find the app name/icon. + * Returns the information needed to display the icon. + * + * The information will either contain app name and icon of the app playing media, or a default + * name and icon if we can't find the app name/icon. */ - private fun getAppInfo(appPackageName: String?): AppInfo { + private fun getIconInfo(appPackageName: String?): IconInfo { if (appPackageName != null) { try { - return AppInfo( - appName = context.packageManager.getApplicationInfo( + return IconInfo( + iconName = context.packageManager.getApplicationInfo( appPackageName, PackageManager.ApplicationInfoFlags.of(0) ).loadLabel(context.packageManager).toString(), - appIcon = context.packageManager.getApplicationIcon(appPackageName) + icon = context.packageManager.getApplicationIcon(appPackageName), + isAppIcon = true ) } catch (e: PackageManager.NameNotFoundException) { Log.w(TAG, "Cannot find package $appPackageName", e) } } - return AppInfo( - appName = context.getString(R.string.media_output_dialog_unknown_launch_app_name), - appIcon = context.resources.getDrawable(R.drawable.ic_cast).apply { + return IconInfo( + iconName = context.getString(R.string.media_output_dialog_unknown_launch_app_name), + icon = context.resources.getDrawable(R.drawable.ic_cast).apply { this.setTint( Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary) ) - } + }, + isAppIcon = false ) } @@ -203,7 +221,9 @@ object MediaTttRemovalReason { const val REASON_SCREEN_TAP = "SCREEN_TAP" } -private data class AppInfo( - val appName: String, - val appIcon: Drawable +private data class IconInfo( + val iconName: String, + val icon: Drawable, + /** True if [icon] is the app's icon, and false if [icon] is some generic default icon. */ + val isAppIcon: Boolean ) diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt index 44965d705802..072263fcf38c 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt @@ -127,6 +127,15 @@ class MediaTttChipControllerReceiver @Inject constructor( chipInfo.appNameOverride ) } + + override fun getIconSize(isAppIcon: Boolean): Int? = + context.resources.getDimensionPixelSize( + if (isAppIcon) { + R.dimen.media_ttt_icon_size_receiver + } else { + R.dimen.media_ttt_generic_icon_size_receiver + } + ) } data class ChipReceiverInfo( diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt index 61071235db0b..8147877a8a29 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt @@ -150,24 +150,27 @@ class PrivacyDialogController( packageName: String, userId: Int, permGroupName: CharSequence, - attributionTag: CharSequence? + attributionTag: CharSequence?, + isAttributionSupported: Boolean ): Intent { lateinit var intent: Intent - if (attributionTag != null) { + if (attributionTag != null && isAttributionSupported) { intent = Intent(Intent.ACTION_MANAGE_PERMISSION_USAGE) intent.setPackage(packageName) intent.putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, permGroupName.toString()) intent.putExtra(Intent.EXTRA_ATTRIBUTION_TAGS, arrayOf(attributionTag.toString())) intent.putExtra(Intent.EXTRA_SHOWING_ATTRIBUTION, true) val resolveInfo = packageManager.resolveActivity( - intent, PackageManager.ResolveInfoFlags.of(0)) - ?: return getDefaultManageAppPermissionsIntent(packageName, userId) - intent.component = ComponentName(packageName, resolveInfo.activityInfo.name) - return intent - } else { - return getDefaultManageAppPermissionsIntent(packageName, userId) + intent, PackageManager.ResolveInfoFlags.of(0)) + if (resolveInfo != null && resolveInfo.activityInfo != null && + resolveInfo.activityInfo.permission == + android.Manifest.permission.START_VIEW_PERMISSION_USAGE) { + intent.component = ComponentName(packageName, resolveInfo.activityInfo.name) + return intent + } } + return getDefaultManageAppPermissionsIntent(packageName, userId) } fun getDefaultManageAppPermissionsIntent(packageName: String, userId: Int): Intent { @@ -226,9 +229,15 @@ class PrivacyDialogController( userInfo?.isManagedProfile ?: false, it.isPhoneCall, it.permissionGroupName, - getManagePermissionIntent(it.packageName, userId, - it.permissionGroupName, - it.attributionTag) + getManagePermissionIntent( + it.packageName, + userId, + it.permissionGroupName, + it.attributionTag, + // attributionLabel is set only when subattribution policies + // are supported and satisfied + it.attributionLabel != null + ) ) } } else { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java index 8bad2de189c5..2cc3986c6e82 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java @@ -21,6 +21,7 @@ import android.content.Intent; import android.graphics.drawable.Drawable; import android.text.Html; import android.text.TextUtils; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -158,22 +159,47 @@ public class InternetAdapter extends RecyclerView.Adapter<InternetAdapter.Intern final int security = wifiEntry.getSecurity(); updateEndIcon(connectedState, security); + mWifiListLayout.setEnabled(shouldEnabled(wifiEntry)); if (connectedState != WifiEntry.CONNECTED_STATE_DISCONNECTED) { mWifiListLayout.setOnClickListener( - v -> mInternetDialogController.launchWifiNetworkDetailsSetting( + v -> mInternetDialogController.launchWifiDetailsSetting( wifiEntry.getKey(), v)); return; } - mWifiListLayout.setOnClickListener(v -> { - if (wifiEntry.shouldEditBeforeConnect()) { - final Intent intent = WifiUtils.getWifiDialogIntent(wifiEntry.getKey(), - true /* connectForCaller */); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); - mContext.startActivity(intent); - } + mWifiListLayout.setOnClickListener(v -> onWifiClick(wifiEntry, v)); + } + + boolean shouldEnabled(@NonNull WifiEntry wifiEntry) { + if (wifiEntry.canConnect()) { + return true; + } + // If Wi-Fi is connected or saved network, leave it enabled to disconnect or configure. + if (wifiEntry.canDisconnect() || wifiEntry.isSaved()) { + return true; + } + return false; + } + + void onWifiClick(@NonNull WifiEntry wifiEntry, @NonNull View view) { + if (wifiEntry.shouldEditBeforeConnect()) { + final Intent intent = WifiUtils.getWifiDialogIntent(wifiEntry.getKey(), + true /* connectForCaller */); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); + mContext.startActivity(intent); + return; + } + + if (wifiEntry.canConnect()) { mInternetDialogController.connect(wifiEntry); - }); + return; + } + + if (wifiEntry.isSaved()) { + Log.w(TAG, "The saved Wi-Fi network does not allow to connect. SSID:" + + wifiEntry.getSsid()); + mInternetDialogController.launchWifiDetailsSetting(wifiEntry.getKey(), view); + } } void setWifiNetworkLayout(CharSequence title, CharSequence summary) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java index d1c784457c9f..8921e95fcee9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java @@ -531,8 +531,7 @@ public class InternetDialog extends SystemUIDialog implements if (mConnectedWifiEntry == null) { return; } - mInternetDialogController.launchWifiNetworkDetailsSetting(mConnectedWifiEntry.getKey(), - view); + mInternetDialogController.launchWifiDetailsSetting(mConnectedWifiEntry.getKey(), view); } void onClickSeeMoreButton(View view) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java index b322cbf6c60e..d97ce7757d8c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java @@ -635,7 +635,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi startActivity(getSettingsIntent(), view); } - void launchWifiNetworkDetailsSetting(String key, View view) { + void launchWifiDetailsSetting(String key, View view) { Intent intent = getWifiDetailsSettingsIntent(key); if (intent != null) { startActivity(intent, view); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt index ab4d0dd355f3..de3e89d6dc8c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt @@ -116,6 +116,16 @@ class LockscreenShadeTransitionController @Inject constructor( private var scrimTransitionDistance = 0 /** + * Distance that it takes in order for the notifications scrim fade in to start. + */ + private var notificationsScrimTransitionDelay = 0 + + /** + * Distance that it takes for the notifications scrim to fully fade if after it started. + */ + private var notificationsScrimTransitionDistance = 0 + + /** * Distance that the full shade transition takes in order for the notification shelf to fully * expand. */ @@ -225,6 +235,10 @@ class LockscreenShadeTransitionController @Inject constructor( R.dimen.lockscreen_shade_transition_by_tap_distance) scrimTransitionDistance = context.resources.getDimensionPixelSize( R.dimen.lockscreen_shade_scrim_transition_distance) + notificationsScrimTransitionDelay = context.resources.getDimensionPixelSize( + R.dimen.lockscreen_shade_notifications_scrim_transition_delay) + notificationsScrimTransitionDistance = context.resources.getDimensionPixelSize( + R.dimen.lockscreen_shade_notifications_scrim_transition_distance) notificationShelfTransitionDistance = context.resources.getDimensionPixelSize( R.dimen.lockscreen_shade_notif_shelf_transition_distance) qsTransitionDistance = context.resources.getDimensionPixelSize( @@ -405,6 +419,7 @@ class LockscreenShadeTransitionController @Inject constructor( false /* animate */, 0 /* delay */) mediaHierarchyManager.setTransitionToFullShadeAmount(field) + transitionToShadeAmountScrim(field) transitionToShadeAmountCommon(field) transitionToShadeAmountKeyguard(field) } @@ -417,10 +432,15 @@ class LockscreenShadeTransitionController @Inject constructor( var qSDragProgress = 0f private set - private fun transitionToShadeAmountCommon(dragDownAmount: Float) { + private fun transitionToShadeAmountScrim(dragDownAmount: Float) { val scrimProgress = MathUtils.saturate(dragDownAmount / scrimTransitionDistance) - scrimController.setTransitionToFullShadeProgress(scrimProgress) + val notificationsScrimDragAmount = dragDownAmount - notificationsScrimTransitionDelay + val notificationsScrimProgress = MathUtils.saturate( + notificationsScrimDragAmount / notificationsScrimTransitionDistance) + scrimController.setTransitionToFullShadeProgress(scrimProgress, notificationsScrimProgress) + } + private fun transitionToShadeAmountCommon(dragDownAmount: Float) { if (depthControllerTransitionDistance > 0) { val depthProgress = MathUtils.saturate(dragDownAmount / depthControllerTransitionDistance) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 3dd717d2f377..eaa66bbc15ac 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -226,10 +226,7 @@ public class NotificationShelf extends ActivatableNotificationView implements ? MathUtils.lerp(shortestWidth, getWidth(), fractionToShade) : getWidth(); ActivatableNotificationView anv = (ActivatableNotificationView) this; - NotificationBackgroundView bg = anv.getBackgroundNormal(); - if (bg != null) { - anv.getBackgroundNormal().setActualWidth((int) actualWidth); - } + anv.setBackgroundWidth((int) actualWidth); if (mShelfIcons != null) { mShelfIcons.setActualLayoutWidth((int) actualWidth); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt index 9795dcf1fcd8..b74140da99d2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt @@ -16,9 +16,12 @@ package com.android.systemui.statusbar.events +import android.animation.Animator +import android.animation.AnimatorListenerAdapter +import android.animation.AnimatorSet import android.animation.ValueAnimator import android.content.Context -import android.graphics.Point +import android.graphics.Rect import android.view.Gravity import android.view.LayoutInflater import android.view.View @@ -30,6 +33,7 @@ import com.android.systemui.R import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider import com.android.systemui.statusbar.window.StatusBarWindowController import javax.inject.Inject +import kotlin.math.roundToInt /** * Controls the view for system event animations. @@ -38,7 +42,7 @@ class SystemEventChipAnimationController @Inject constructor( private val context: Context, private val statusBarWindowController: StatusBarWindowController, private val contentInsetsProvider: StatusBarContentInsetsProvider -) : SystemStatusChipAnimationCallback { +) : SystemStatusAnimationCallback { private lateinit var animationWindowView: FrameLayout @@ -49,90 +53,169 @@ class SystemEventChipAnimationController @Inject constructor( private var chipRight = 0 private var chipLeft = 0 private var chipWidth = 0 - private var dotCenter = Point(0, 0) + private var chipMinWidth = context.resources.getDimensionPixelSize( + R.dimen.ongoing_appops_chip_min_animation_width) private var dotSize = context.resources.getDimensionPixelSize( R.dimen.ongoing_appops_dot_diameter) - // If the chip animates away to a persistent dot, then we modify the CHIP_OUT animation - private var isAnimatingToDot = false + // Use during animation so that multiple animators can update the drawing rect + private var animRect = Rect() // TODO: move to dagger private var initialized = false - override fun onChipAnimationStart( - viewCreator: ViewCreator, - @SystemAnimationState state: Int - ) { - if (!initialized) init() - - if (state == ANIMATING_IN) { - animationDirection = if (animationWindowView.isLayoutRtl) RIGHT else LEFT - - // Initialize the animated view - val insets = contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation() - currentAnimatedView = viewCreator(context).also { - animationWindowView.addView( - it.view, - layoutParamsDefault( - if (animationWindowView.isLayoutRtl) insets.first - else insets.second)) - it.view.alpha = 0f - // For some reason, the window view's measured width is always 0 here, so use the - // parent (status bar) - it.view.measure( - View.MeasureSpec.makeMeasureSpec( - (animationWindowView.parent as View).width, AT_MOST), - View.MeasureSpec.makeMeasureSpec(animationWindowView.height, AT_MOST)) - chipWidth = it.chipWidth - } + /** + * Give the chip controller a chance to inflate and configure the chip view before we start + * animating + */ + fun prepareChipAnimation(viewCreator: ViewCreator) { + if (!initialized) { + init() + } + animationDirection = if (animationWindowView.isLayoutRtl) RIGHT else LEFT - // decide which direction we're animating from, and then set some screen coordinates - val contentRect = contentInsetsProvider.getStatusBarContentAreaForCurrentRotation() - when (animationDirection) { - LEFT -> { - chipRight = contentRect.right - chipLeft = contentRect.right - chipWidth - } - else /* RIGHT */ -> { - chipLeft = contentRect.left - chipRight = contentRect.left + chipWidth - } - } + // Initialize the animated view + val insets = contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation() + currentAnimatedView = viewCreator(context).also { + animationWindowView.addView( + it.view, + layoutParamsDefault( + if (animationWindowView.isLayoutRtl) insets.first + else insets.second)) + it.view.alpha = 0f + // For some reason, the window view's measured width is always 0 here, so use the + // parent (status bar) + it.view.measure( + View.MeasureSpec.makeMeasureSpec( + (animationWindowView.parent as View).width, AT_MOST), + View.MeasureSpec.makeMeasureSpec(animationWindowView.height, AT_MOST)) + chipWidth = it.chipWidth + } - currentAnimatedView?.apply { - updateAnimatedViewBoundsForAmount(0.1f, this) + // decide which direction we're animating from, and then set some screen coordinates + val contentRect = contentInsetsProvider.getStatusBarContentAreaForCurrentRotation() + when (animationDirection) { + LEFT -> { + chipRight = contentRect.right + chipLeft = contentRect.right - chipWidth } - } else { - // We are animating away - currentAnimatedView!!.view.apply { - alpha = 1f + else /* RIGHT */ -> { + chipLeft = contentRect.left + chipRight = contentRect.left + chipWidth } } } - override fun onChipAnimationEnd(@SystemAnimationState state: Int) { - if (state == ANIMATING_IN) { - // Finished animating in - currentAnimatedView?.apply { - updateAnimatedViewBoundsForAmount(1f, this) + override fun onSystemEventAnimationBegin(): Animator { + initializeAnimRect() + + val alphaIn = ValueAnimator.ofFloat(0f, 1f).apply { + startDelay = 117 + duration = 83 + interpolator = null + addUpdateListener { currentAnimatedView?.view?.alpha = animatedValue as Float } + } + val moveIn = ValueAnimator.ofInt(chipMinWidth, chipWidth).apply { + startDelay = 117 + duration = 383 + interpolator = STATUS_BAR_X_MOVE_IN + addUpdateListener { + updateAnimatedViewBoundsWidth(animatedValue as Int) } + } + val animSet = AnimatorSet() + animSet.playTogether(alphaIn, moveIn) + return animSet + } + + override fun onSystemEventAnimationFinish(hasPersistentDot: Boolean): Animator { + initializeAnimRect() + val finish = if (hasPersistentDot) { + createMoveOutAnimationForDot() } else { - // Finished animating away - currentAnimatedView!!.view.apply { - visibility = View.INVISIBLE + createMoveOutAnimationDefault() + } + + finish.addListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator?) { + animationWindowView.removeView(currentAnimatedView!!.view) + } + }) + + return finish + } + + private fun createMoveOutAnimationForDot(): Animator { + val width1 = ValueAnimator.ofInt(chipWidth, chipMinWidth).apply { + duration = 150 + interpolator = STATUS_CHIP_WIDTH_TO_DOT_KEYFRAME_1 + addUpdateListener { + updateAnimatedViewBoundsWidth(it.animatedValue as Int) + } + } + + val width2 = ValueAnimator.ofInt(chipMinWidth, dotSize).apply { + startDelay = 150 + duration = 333 + interpolator = STATUS_CHIP_WIDTH_TO_DOT_KEYFRAME_2 + addUpdateListener { + updateAnimatedViewBoundsWidth(it.animatedValue as Int) } - animationWindowView.removeView(currentAnimatedView!!.view) } + + val keyFrame1Height = dotSize * 2 + val v = currentAnimatedView!!.view + val chipVerticalCenter = v.top + v.measuredHeight / 2 + val height1 = ValueAnimator.ofInt( + currentAnimatedView!!.view.measuredHeight, keyFrame1Height).apply { + startDelay = 133 + duration = 100 + interpolator = STATUS_CHIP_HEIGHT_TO_DOT_KEYFRAME_1 + addUpdateListener { + updateAnimatedViewBoundsHeight(it.animatedValue as Int, chipVerticalCenter) + } + } + + val height2 = ValueAnimator.ofInt(keyFrame1Height, dotSize).apply { + startDelay = 233 + duration = 250 + interpolator = STATUS_CHIP_HEIGHT_TO_DOT_KEYFRAME_2 + addUpdateListener { + updateAnimatedViewBoundsHeight(it.animatedValue as Int, chipVerticalCenter) + } + } + + // Move the chip view to overlap exactly with the privacy dot. The chip displays by default + // exactly adjacent to the dot, so we can just move over by the diameter of the dot itself + val moveOut = ValueAnimator.ofInt(0, dotSize).apply { + startDelay = 50 + duration = 183 + interpolator = STATUS_CHIP_MOVE_TO_DOT + addUpdateListener { + // If RTL, we can just invert the move + val amt = if (animationDirection == LEFT) { + animatedValue as Int + } else { + -(animatedValue as Int) + } + updateAnimatedBoundsX(amt) + } + } + + val animSet = AnimatorSet() + animSet.playTogether(width1, width2, height1, height2, moveOut) + return animSet } - override fun onChipAnimationUpdate( - animator: ValueAnimator, - @SystemAnimationState state: Int - ) { - currentAnimatedView?.apply { - val amt = (animator.animatedValue as Float).amt() - view.alpha = (animator.animatedValue as Float) - updateAnimatedViewBoundsForAmount(amt, this) + private fun createMoveOutAnimationDefault(): Animator { + val moveOut = ValueAnimator.ofInt(chipWidth, chipMinWidth).apply { + duration = 383 + addUpdateListener { + currentAnimatedView?.apply { + updateAnimatedViewBoundsWidth(it.animatedValue as Int) + } + } } + return moveOut } private fun init() { @@ -144,7 +227,6 @@ class SystemEventChipAnimationController @Inject constructor( statusBarWindowController.addViewToWindow(animationWindowView, lp) animationWindowView.clipToPadding = false animationWindowView.clipChildren = false - animationWindowView.measureAllChildren = true } private fun layoutParamsDefault(marginEnd: Int): FrameLayout.LayoutParams = @@ -153,29 +235,55 @@ class SystemEventChipAnimationController @Inject constructor( it.marginEnd = marginEnd } - private fun updateAnimatedViewBoundsForAmount(amt: Float, chip: BackgroundAnimatableView) { + private fun initializeAnimRect() = animRect.set( + chipLeft, + currentAnimatedView!!.view.top, + chipRight, + currentAnimatedView!!.view.bottom) + + /** + * To be called during an animation, sets the width and updates the current animated chip view + */ + private fun updateAnimatedViewBoundsWidth(width: Int) { when (animationDirection) { LEFT -> { - chip.setBoundsForAnimation( - (chipRight - (chipWidth * amt)).toInt(), - chip.view.top, - chipRight, - chip.view.bottom) - } - else /* RIGHT */ -> { - chip.setBoundsForAnimation( - chipLeft, - chip.view.top, - (chipLeft + (chipWidth * amt)).toInt(), - chip.view.bottom) + animRect.set((chipRight - width), animRect.top, chipRight, animRect.bottom) + } else /* RIGHT */ -> { + animRect.set(chipLeft, animRect.top, (chipLeft + width), animRect.bottom) } } + + updateCurrentAnimatedView() } - private fun start() = if (animationWindowView.isLayoutRtl) right() else left() - private fun right() = contentInsetsProvider.getStatusBarContentAreaForCurrentRotation().right - private fun left() = contentInsetsProvider.getStatusBarContentAreaForCurrentRotation().left - private fun Float.amt() = 0.01f.coerceAtLeast(this) + /** + * To be called during an animation, updates the animation rect and sends the update to the chip + */ + private fun updateAnimatedViewBoundsHeight(height: Int, verticalCenter: Int) { + animRect.set( + animRect.left, + verticalCenter - (height.toFloat() / 2).roundToInt(), + animRect.right, + verticalCenter + (height.toFloat() / 2).roundToInt()) + + updateCurrentAnimatedView() + } + + /** + * To be called during an animation, updates the animation rect offset and updates the chip + */ + private fun updateAnimatedBoundsX(translation: Int) { + currentAnimatedView?.view?.translationX = translation.toFloat() + } + + /** + * To be called during an animation. Sets the chip rect to animRect + */ + private fun updateCurrentAnimatedView() { + currentAnimatedView?.setBoundsForAnimation( + animRect.left, animRect.top, animRect.right, animRect.bottom + ) + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt index 947f3eb2ee12..36233e4e3eab 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt @@ -19,14 +19,12 @@ package com.android.systemui.statusbar.events import android.animation.Animator import android.animation.AnimatorListenerAdapter import android.animation.AnimatorSet -import android.animation.ValueAnimator import android.annotation.IntDef import android.os.Process import android.provider.DeviceConfig import android.util.Log +import android.view.animation.PathInterpolator import com.android.systemui.Dumpable -import com.android.systemui.animation.Interpolators.STANDARD_ACCELERATE -import com.android.systemui.animation.Interpolators.STANDARD_DECELERATE import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main @@ -38,6 +36,7 @@ import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.util.time.SystemClock import java.io.FileDescriptor import java.io.PrintWriter +import java.lang.IllegalStateException import javax.inject.Inject @@ -116,7 +115,10 @@ class SystemStatusAnimationScheduler @Inject constructor( scheduledEvent?.updateFromEvent(event) if (event.forceVisible) { hasPersistentDot = true - notifyTransitionToPersistentDot() + // If we missed the chance to show the persistent dot, do it now + if (animationState == IDLE) { + notifyTransitionToPersistentDot() + } } } else { if (DEBUG) { @@ -162,68 +164,90 @@ class SystemStatusAnimationScheduler @Inject constructor( return } - // Schedule the animation to start after a debounce period - cancelExecutionRunnable = executor.executeDelayed({ - cancelExecutionRunnable = null - animationState = ANIMATING_IN - statusBarWindowController.setForceStatusBarVisible(true) - - val entranceAnimator = ValueAnimator.ofFloat(1f, 0f) - entranceAnimator.duration = ENTRANCE_ANIM_LENGTH - entranceAnimator.addListener(systemAnimatorAdapter) - entranceAnimator.addUpdateListener(systemUpdateListener) - entranceAnimator.interpolator = STANDARD_ACCELERATE - - val chipAnimator = ValueAnimator.ofFloat(0f, 1f) - chipAnimator.duration = CHIP_ANIM_LENGTH - chipAnimator.addListener( - ChipAnimatorAdapter(RUNNING_CHIP_ANIM, scheduledEvent!!.viewCreator)) - chipAnimator.addUpdateListener(chipUpdateListener) - chipAnimator.interpolator = STANDARD_DECELERATE - - val aSet2 = AnimatorSet() - aSet2.playSequentially(entranceAnimator, chipAnimator) - aSet2.start() - - executor.executeDelayed({ - animationState = ANIMATING_OUT - - val systemAnimator = ValueAnimator.ofFloat(0f, 1f) - systemAnimator.duration = ENTRANCE_ANIM_LENGTH - systemAnimator.addListener(systemAnimatorAdapter) - systemAnimator.addUpdateListener(systemUpdateListener) - systemAnimator.interpolator = STANDARD_DECELERATE - systemAnimator.addListener(object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator?) { - statusBarWindowController.setForceStatusBarVisible(false) + chipAnimationController.prepareChipAnimation(scheduledEvent!!.viewCreator) + animationState = ANIMATION_QUEUED + executor.executeDelayed({ + runChipAnimation() + }, DEBOUNCE_DELAY) + } + + /** + * 1. Define a total budget for the chip animation (1500ms) + * 2. Send out callbacks to listeners so that they can generate animations locally + * 3. Update the scheduler state so that clients know where we are + * 4. Maybe: provide scaffolding such as: dot location, margins, etc + * 5. Maybe: define a maximum animation length and enforce it. Probably only doable if we + * collect all of the animators and run them together. + */ + private fun runChipAnimation() { + statusBarWindowController.setForceStatusBarVisible(true) + animationState = ANIMATING_IN + + val animSet = collectStartAnimations() + if (animSet.totalDuration > 500) { + throw IllegalStateException("System animation total length exceeds budget. " + + "Expected: 500, actual: ${animSet.totalDuration}") + } + animSet.addListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator?) { + animationState = RUNNING_CHIP_ANIM + } + }) + animSet.start() + + executor.executeDelayed({ + val animSet2 = collectFinishAnimations() + animationState = ANIMATING_OUT + animSet2.addListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator?) { + animationState = if (hasPersistentDot) { + SHOWING_PERSISTENT_DOT + } else { + IDLE } - }) - - val chipAnimator = ValueAnimator.ofFloat(1f, 0f) - chipAnimator.duration = CHIP_ANIM_LENGTH - val endState = if (hasPersistentDot) { - SHOWING_PERSISTENT_DOT - } else { - IDLE + + statusBarWindowController.setForceStatusBarVisible(false) } - chipAnimator.addListener( - ChipAnimatorAdapter(endState, scheduledEvent!!.viewCreator)) - chipAnimator.addUpdateListener(chipUpdateListener) - chipAnimator.interpolator = STANDARD_ACCELERATE + }) + animSet2.start() + scheduledEvent = null + }, DISPLAY_LENGTH) + } - val aSet2 = AnimatorSet() + private fun collectStartAnimations(): AnimatorSet { + val animators = mutableListOf<Animator>() + listeners.forEach { listener -> + listener.onSystemEventAnimationBegin()?.let { anim -> + animators.add(anim) + } + } + animators.add(chipAnimationController.onSystemEventAnimationBegin()) + val animSet = AnimatorSet().also { + it.playTogether(animators) + } - aSet2.play(chipAnimator).before(systemAnimator) - if (hasPersistentDot) { - val dotAnim = notifyTransitionToPersistentDot() - if (dotAnim != null) aSet2.playTogether(systemAnimator, dotAnim) - } + return animSet + } - aSet2.start() + private fun collectFinishAnimations(): AnimatorSet { + val animators = mutableListOf<Animator>() + listeners.forEach { listener -> + listener.onSystemEventAnimationFinish(hasPersistentDot)?.let { anim -> + animators.add(anim) + } + } + animators.add(chipAnimationController.onSystemEventAnimationFinish(hasPersistentDot)) + if (hasPersistentDot) { + val dotAnim = notifyTransitionToPersistentDot() + if (dotAnim != null) { + animators.add(dotAnim) + } + } + val animSet = AnimatorSet().also { + it.playTogether(animators) + } - scheduledEvent = null - }, DISPLAY_LENGTH) - }, DELAY) + return animSet } private fun notifyTransitionToPersistentDot(): Animator? { @@ -257,18 +281,6 @@ class SystemStatusAnimationScheduler @Inject constructor( return null } - private fun notifySystemStart() { - listeners.forEach { it.onSystemChromeAnimationStart() } - } - - private fun notifySystemFinish() { - listeners.forEach { it.onSystemChromeAnimationEnd() } - } - - private fun notifySystemAnimationUpdate(anim: ValueAnimator) { - listeners.forEach { it.onSystemChromeAnimationUpdate(anim) } - } - override fun addCallback(listener: SystemStatusAnimationCallback) { Assert.isMainThread() @@ -287,24 +299,6 @@ class SystemStatusAnimationScheduler @Inject constructor( } } - private val systemUpdateListener = ValueAnimator.AnimatorUpdateListener { - anim -> notifySystemAnimationUpdate(anim) - } - - private val systemAnimatorAdapter = object : AnimatorListenerAdapter() { - override fun onAnimationEnd(p0: Animator?) { - notifySystemFinish() - } - - override fun onAnimationStart(p0: Animator?) { - notifySystemStart() - } - } - - private val chipUpdateListener = ValueAnimator.AnimatorUpdateListener { - anim -> chipAnimationController.onChipAnimationUpdate(anim, animationState) - } - override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) { pw.println("Scheduled event: $scheduledEvent") pw.println("Has persistent privacy dot: $hasPersistentDot") @@ -318,24 +312,6 @@ class SystemStatusAnimationScheduler @Inject constructor( } } } - - inner class ChipAnimatorAdapter( - @SystemAnimationState val endState: Int, - val viewCreator: ViewCreator - ) : AnimatorListenerAdapter() { - override fun onAnimationEnd(p0: Animator?) { - chipAnimationController.onChipAnimationEnd(animationState) - animationState = if (endState == SHOWING_PERSISTENT_DOT && !hasPersistentDot) { - IDLE - } else { - endState - } - } - - override fun onAnimationStart(p0: Animator?) { - chipAnimationController.onChipAnimationStart(viewCreator, animationState) - } - } } /** @@ -344,16 +320,14 @@ class SystemStatusAnimationScheduler @Inject constructor( * create space for the chip animation to display. This means hiding the system elements in the * status bar and keyguard. * - * TODO: the chip animation really only has one client, we can probably remove it from this - * interface - * * The value animators themselves are simple animators from 0.0 to 1.0. Listeners can apply any * interpolation they choose but realistically these are most likely to be simple alpha transitions */ interface SystemStatusAnimationCallback { - @JvmDefault fun onSystemChromeAnimationUpdate(animator: ValueAnimator) {} - @JvmDefault fun onSystemChromeAnimationStart() {} - @JvmDefault fun onSystemChromeAnimationEnd() {} + /** Implement this method to return an [Animator] or [AnimatorSet] that presents the chip */ + fun onSystemEventAnimationBegin(): Animator? { return null } + /** Implement this method to return an [Animator] or [AnimatorSet] that hides the chip */ + fun onSystemEventAnimationFinish(hasPersistentDot: Boolean): Animator? { return null } // Best method name, change my mind @JvmDefault @@ -363,50 +337,61 @@ interface SystemStatusAnimationCallback { @JvmDefault fun onHidePersistentDot(): Animator? { return null } } -interface SystemStatusChipAnimationCallback { - fun onChipAnimationUpdate(animator: ValueAnimator, @SystemAnimationState state: Int) {} - - fun onChipAnimationStart( - viewCreator: ViewCreator, - @SystemAnimationState state: Int - ) {} - - fun onChipAnimationEnd(@SystemAnimationState state: Int) {} -} - /** + * Animation state IntDef */ @Retention(AnnotationRetention.SOURCE) @IntDef( value = [ - IDLE, ANIMATING_IN, RUNNING_CHIP_ANIM, ANIMATING_OUT, SHOWING_PERSISTENT_DOT + IDLE, + ANIMATION_QUEUED, + ANIMATING_IN, + RUNNING_CHIP_ANIM, + ANIMATING_OUT, + SHOWING_PERSISTENT_DOT ] ) annotation class SystemAnimationState /** No animation is in progress */ const val IDLE = 0 +/** An animation is queued, and awaiting the debounce period */ +const val ANIMATION_QUEUED = 1 /** System is animating out, and chip is animating in */ -const val ANIMATING_IN = 1 +const val ANIMATING_IN = 2 /** Chip has animated in and is awaiting exit animation, and optionally playing its own animation */ -const val RUNNING_CHIP_ANIM = 2 +const val RUNNING_CHIP_ANIM = 3 /** Chip is animating away and system is animating back */ -const val ANIMATING_OUT = 3 +const val ANIMATING_OUT = 4 /** Chip has animated away, and the persistent dot is showing */ -const val SHOWING_PERSISTENT_DOT = 4 +const val SHOWING_PERSISTENT_DOT = 5 + +/** Commonly-needed interpolators can go here */ +@JvmField val STATUS_BAR_X_MOVE_OUT = PathInterpolator(0.33f, 0f, 0f, 1f) +@JvmField val STATUS_BAR_X_MOVE_IN = PathInterpolator(0f, 0f, 0f, 1f) +/** + * Status chip animation to dot have multiple stages of motion, the _1 and _2 interpolators should + * be used in succession + */ +val STATUS_CHIP_WIDTH_TO_DOT_KEYFRAME_1 = PathInterpolator(0.44f, 0f, 0.25f, 1f) +val STATUS_CHIP_WIDTH_TO_DOT_KEYFRAME_2 = PathInterpolator(0.3f, 0f, 0.26f, 1f) +val STATUS_CHIP_HEIGHT_TO_DOT_KEYFRAME_1 = PathInterpolator(0.4f, 0f, 0.17f, 1f) +val STATUS_CHIP_HEIGHT_TO_DOT_KEYFRAME_2 = PathInterpolator(0.3f, 0f, 0f, 1f) +val STATUS_CHIP_MOVE_TO_DOT = PathInterpolator(0f, 0f, 0.05f, 1f) private const val TAG = "SystemStatusAnimationScheduler" -private const val DELAY = 0L +private const val DEBOUNCE_DELAY = 100L /** - * The total time spent animation should be 1500ms. The entrance animation is how much time - * we give to the system to animate system elements out of the way. Total chip animation length - * will be equivalent to 2*chip_anim_length + display_length + * The total time spent on the chip animation is 1500ms, broken up into 3 sections: + * - 500ms to animate the chip in (including animating system icons away) + * - 500ms holding the chip on screen + * - 500ms to animate the chip away (and system icons back) + * + * So DISPLAY_LENGTH should be the sum of the first 2 phases, while the final 500ms accounts for + * the actual animation */ -private const val ENTRANCE_ANIM_LENGTH = 250L -private const val CHIP_ANIM_LENGTH = 250L -// 1s + entrance time + chip anim_length -private const val DISPLAY_LENGTH = 1500L +private const val DISPLAY_LENGTH = 1000L private const val MIN_UPTIME: Long = 5 * 1000 diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt index 7fbb0f1182c0..02aa1f2fd585 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt @@ -25,7 +25,7 @@ class NotificationLaunchAnimatorControllerProvider @Inject constructor( @JvmOverloads fun getAnimatorController( notification: ExpandableNotificationRow, - onFinishAnimationCallback: Runnable = Runnable {} + onFinishAnimationCallback: Runnable? = null ): NotificationLaunchAnimatorController { return NotificationLaunchAnimatorController( notificationShadeWindowViewController, @@ -49,7 +49,7 @@ class NotificationLaunchAnimatorController( private val headsUpManager: HeadsUpManagerPhone, private val notification: ExpandableNotificationRow, private val jankMonitor: InteractionJankMonitor, - private val onFinishAnimationCallback: Runnable + private val onFinishAnimationCallback: Runnable? ) : ActivityLaunchAnimator.Controller { companion object { @@ -123,7 +123,7 @@ class NotificationLaunchAnimatorController( if (!willAnimate) { removeHun(animate = true) - onFinishAnimationCallback.run() + onFinishAnimationCallback?.run() } } @@ -142,7 +142,7 @@ class NotificationLaunchAnimatorController( notificationShadeWindowViewController.setExpandAnimationRunning(false) notificationEntry.isExpandAnimationRunning = false removeHun(animate = true) - onFinishAnimationCallback.run() + onFinishAnimationCallback?.run() } override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) { @@ -162,7 +162,7 @@ class NotificationLaunchAnimatorController( notificationListContainer.setExpandingNotification(null) applyParams(null) removeHun(animate = false) - onFinishAnimationCallback.run() + onFinishAnimationCallback?.run() } private fun applyParams(params: ExpandAnimationParameters?) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java index fca2aa167e37..577d536262b2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java @@ -163,10 +163,13 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView } /** - * @return The background of this view. + * @param width The actual width to apply to the background view. */ - public NotificationBackgroundView getBackgroundNormal() { - return mBackgroundNormal; + public void setBackgroundWidth(int width) { + if (mBackgroundNormal == null) { + return; + } + mBackgroundNormal.setActualWidth(width); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index c237e1deeae3..e479509247d3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -79,7 +79,6 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.ContrastColorUtil; import com.android.internal.widget.CachingIconView; import com.android.internal.widget.CallLayout; -import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.animation.Interpolators; import com.android.systemui.classifier.FalsingCollector; @@ -90,6 +89,7 @@ import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.RemoteInputController; +import com.android.systemui.statusbar.SmartReplyController; import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.notification.AboveShelfChangedListener; import com.android.systemui.statusbar.notification.ExpandAnimationParameters; @@ -111,10 +111,11 @@ import com.android.systemui.statusbar.notification.stack.ExpandableViewState; import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.SwipeableView; -import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.CentralSurfaces; +import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.InflatedSmartReplyState; +import com.android.systemui.statusbar.policy.SmartReplyConstants; import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent; import com.android.systemui.util.Compile; import com.android.systemui.util.DumpUtilsKt; @@ -169,6 +170,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private RowContentBindStage mRowContentBindStage; private PeopleNotificationIdentifier mPeopleNotificationIdentifier; private Optional<BubblesManager> mBubblesManagerOptional; + private MetricsLogger mMetricsLogger; private int mIconTransformContentShift; private int mMaxHeadsUpHeightBeforeN; private int mMaxHeadsUpHeightBeforeP; @@ -304,8 +306,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView final boolean wasExpanded = mGroupExpansionManager.isGroupExpanded(mEntry); boolean nowExpanded = mGroupExpansionManager.toggleGroupExpansion(mEntry); mOnExpandClickListener.onExpandClicked(mEntry, v, nowExpanded); - MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTIFICATION_GROUP_EXPANDER, - nowExpanded); + mMetricsLogger.action(MetricsEvent.ACTION_NOTIFICATION_GROUP_EXPANDER, nowExpanded); onExpansionChanged(true /* userAction */, wasExpanded); } else if (mEnableNonGroupedNotificationExpand) { if (v.isAccessibilityFocused()) { @@ -327,8 +328,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } notifyHeightChanged(true); mOnExpandClickListener.onExpandClicked(mEntry, v, nowExpanded); - MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTIFICATION_EXPANDER, - nowExpanded); + mMetricsLogger.action(MetricsEvent.ACTION_NOTIFICATION_EXPANDER, nowExpanded); } } }; @@ -1271,7 +1271,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView addView(mMenuRow.getMenuView(), menuIndex); } for (NotificationContentView l : mLayouts) { - l.initView(); + l.reinflate(); l.reInflateViews(); } mEntry.getSbn().clearPackageContext(); @@ -1464,7 +1464,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView * @param fromAccessibility whether this dismiss is coming from an accessibility action */ public void performDismiss(boolean fromAccessibility) { - Dependency.get(MetricsLogger.class).count(NotificationCounters.NOTIFICATION_DISMISSED, 1); + mMetricsLogger.count(NotificationCounters.NOTIFICATION_DISMISSED, 1); dismiss(fromAccessibility); if (mEntry.isDismissable()) { if (mOnUserInteractionCallback != null) { @@ -1600,7 +1600,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView PeopleNotificationIdentifier peopleNotificationIdentifier, OnUserInteractionCallback onUserInteractionCallback, Optional<BubblesManager> bubblesManagerOptional, - NotificationGutsManager gutsManager) { + NotificationGutsManager gutsManager, + MetricsLogger metricsLogger, + SmartReplyConstants smartReplyConstants, + SmartReplyController smartReplyController) { mEntry = entry; mAppName = appName; if (mMenuRow == null) { @@ -1623,15 +1626,18 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mFalsingManager = falsingManager; mFalsingCollector = falsingCollector; mStatusBarStateController = statusBarStateController; - mPeopleNotificationIdentifier = peopleNotificationIdentifier; for (NotificationContentView l : mLayouts) { - l.setPeopleNotificationIdentifier(mPeopleNotificationIdentifier); - l.setRemoteInputViewSubcomponentFactory(rivSubcomponentFactory); + l.initialize( + mPeopleNotificationIdentifier, + rivSubcomponentFactory, + smartReplyConstants, + smartReplyController); } mOnUserInteractionCallback = onUserInteractionCallback; mBubblesManagerOptional = bubblesManagerOptional; mNotificationGutsManager = gutsManager; + mMetricsLogger = metricsLogger; cacheIsSystemNotification(); } @@ -3127,7 +3133,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView if (mGroupMembershipManager.isGroupSummary(mEntry)) { event = MetricsEvent.ACTION_NOTIFICATION_GROUP_GESTURE_EXPANDER; } - MetricsLogger.action(mContext, event, userExpanded); + mMetricsLogger.action(event, userExpanded); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java index c4beb5bf4d7b..599039d46556 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java @@ -27,6 +27,7 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.internal.logging.MetricsLogger; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; @@ -35,6 +36,7 @@ import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.NotificationMediaManager; +import com.android.systemui.statusbar.SmartReplyController; import com.android.systemui.statusbar.notification.FeedbackIcon; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager; @@ -49,6 +51,7 @@ import com.android.systemui.statusbar.notification.row.dagger.NotificationRowSco import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.policy.SmartReplyConstants; import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent; import com.android.systemui.util.time.SystemClock; import com.android.systemui.wmshell.BubblesManager; @@ -82,6 +85,7 @@ public class ExpandableNotificationRowController implements NotifViewController private final HeadsUpManager mHeadsUpManager; private final ExpandableNotificationRow.OnExpandClickListener mOnExpandClickListener; private final StatusBarStateController mStatusBarStateController; + private final MetricsLogger mMetricsLogger; private final ExpandableNotificationRow.ExpansionLogger mExpansionLogger = this::logNotificationExpansion; @@ -94,16 +98,21 @@ public class ExpandableNotificationRowController implements NotifViewController private final boolean mAllowLongPress; private final PeopleNotificationIdentifier mPeopleNotificationIdentifier; private final Optional<BubblesManager> mBubblesManagerOptional; + private final SmartReplyConstants mSmartReplyConstants; + private final SmartReplyController mSmartReplyController; private final ExpandableNotificationRowDragController mDragController; @Inject public ExpandableNotificationRowController( ExpandableNotificationRow view, - NotificationListContainer listContainer, - RemoteInputViewSubcomponent.Factory rivSubcomponentFactory, ActivatableNotificationViewController activatableNotificationViewController, + RemoteInputViewSubcomponent.Factory rivSubcomponentFactory, + MetricsLogger metricsLogger, + NotificationListContainer listContainer, NotificationMediaManager mediaManager, + SmartReplyConstants smartReplyConstants, + SmartReplyController smartReplyController, PluginManager pluginManager, SystemClock clock, @AppName String appName, @@ -152,6 +161,9 @@ public class ExpandableNotificationRowController implements NotifViewController mPeopleNotificationIdentifier = peopleNotificationIdentifier; mBubblesManagerOptional = bubblesManagerOptional; mDragController = dragController; + mMetricsLogger = metricsLogger; + mSmartReplyConstants = smartReplyConstants; + mSmartReplyController = smartReplyController; } /** @@ -179,7 +191,10 @@ public class ExpandableNotificationRowController implements NotifViewController mPeopleNotificationIdentifier, mOnUserInteractionCallback, mBubblesManagerOptional, - mNotificationGutsManager + mNotificationGutsManager, + mMetricsLogger, + mSmartReplyConstants, + mSmartReplyController ); mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); if (mAllowLongPress) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index adb4ce68c031..b9c7113206df 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -39,7 +39,6 @@ import android.widget.ImageView; import android.widget.LinearLayout; import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.statusbar.RemoteInputController; @@ -94,7 +93,6 @@ public class NotificationContentView extends FrameLayout implements Notification private final Rect mClipBounds = new Rect(); private int mMinContractedHeight; - private int mNotificationContentMarginEnd; private View mContractedChild; private View mExpandedChild; private View mHeadsUpChild; @@ -116,7 +114,7 @@ public class NotificationContentView extends FrameLayout implements Notification private NotificationViewWrapper mContractedWrapper; private NotificationViewWrapper mExpandedWrapper; private NotificationViewWrapper mHeadsUpWrapper; - private HybridGroupManager mHybridGroupManager; + private final HybridGroupManager mHybridGroupManager; private int mClipTopAmount; private int mContentHeight; private int mVisibleType = VISIBLE_TYPE_NONE; @@ -128,7 +126,6 @@ public class NotificationContentView extends FrameLayout implements Notification private int mHeadsUpHeight; private int mNotificationMaxHeight; private NotificationEntry mNotificationEntry; - private GroupMembershipManager mGroupMembershipManager; private RemoteInputController mRemoteInputController; private Runnable mExpandedVisibleListener; private PeopleNotificationIdentifier mPeopleIdentifier; @@ -184,7 +181,6 @@ public class NotificationContentView extends FrameLayout implements Notification private boolean mFocusOnVisibilityChange; private boolean mHeadsUpAnimatingAway; private int mClipBottomAmount; - private boolean mIsLowPriority; private boolean mIsContentExpandable; private boolean mRemoteInputVisible; private int mUnrestrictedContentHeight; @@ -192,16 +188,23 @@ public class NotificationContentView extends FrameLayout implements Notification public NotificationContentView(Context context, AttributeSet attrs) { super(context, attrs); mHybridGroupManager = new HybridGroupManager(getContext()); - mSmartReplyConstants = Dependency.get(SmartReplyConstants.class); - mSmartReplyController = Dependency.get(SmartReplyController.class); - initView(); + reinflate(); } - public void initView() { + public void initialize( + PeopleNotificationIdentifier peopleNotificationIdentifier, + RemoteInputViewSubcomponent.Factory rivSubcomponentFactory, + SmartReplyConstants smartReplyConstants, + SmartReplyController smartReplyController) { + mPeopleIdentifier = peopleNotificationIdentifier; + mRemoteInputSubcomponentFactory = rivSubcomponentFactory; + mSmartReplyConstants = smartReplyConstants; + mSmartReplyController = smartReplyController; + } + + public void reinflate() { mMinContractedHeight = getResources().getDimensionPixelSize( R.dimen.min_notification_layout_height); - mNotificationContentMarginEnd = getResources().getDimensionPixelSize( - com.android.internal.R.dimen.notification_content_margin_end); } public void setHeights(int smallHeight, int headsUpMaxHeight, int maxHeight) { @@ -1606,7 +1609,6 @@ public class NotificationContentView extends FrameLayout implements Notification } public void setGroupMembershipManager(GroupMembershipManager groupMembershipManager) { - mGroupMembershipManager = groupMembershipManager; } public void setRemoteInputController(RemoteInputController r) { @@ -1694,10 +1696,6 @@ public class NotificationContentView extends FrameLayout implements Notification mContainingNotification = containingNotification; } - public void setPeopleNotificationIdentifier(PeopleNotificationIdentifier peopleIdentifier) { - mPeopleIdentifier = peopleIdentifier; - } - public void requestSelectLayout(boolean needsAnimation) { selectLayout(needsAnimation, false); } @@ -1865,7 +1863,6 @@ public class NotificationContentView extends FrameLayout implements Notification } public void setIsLowPriority(boolean isLowPriority) { - mIsLowPriority = isLowPriority; } public boolean isDimmable() { @@ -2090,10 +2087,6 @@ public class NotificationContentView extends FrameLayout implements Notification return false; } - public void setRemoteInputViewSubcomponentFactory(RemoteInputViewSubcomponent.Factory factory) { - mRemoteInputSubcomponentFactory = factory; - } - private static class RemoteInputViewData { @Nullable RemoteInputView mView; @Nullable RemoteInputViewController mController; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index ade95a8cbfe7..c89f4d797819 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -217,6 +217,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private HashSet<View> mFromMoreCardAdditions = new HashSet<>(); private ArrayList<AnimationEvent> mAnimationEvents = new ArrayList<>(); private ArrayList<View> mSwipedOutViews = new ArrayList<>(); + private NotificationStackSizeCalculator mNotificationStackSizeCalculator; private final StackStateAnimator mStateAnimator = new StackStateAnimator(this); private boolean mAnimationsEnabled; private boolean mChangePositionInProgress; @@ -415,6 +416,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private NotificationShelf mShelf; private int mMaxDisplayedNotifications = -1; private float mKeyguardBottomPadding = -1; + private float mKeyguardNotificationAvailableSpace = -1; @VisibleForTesting int mStatusBarHeight; private int mMinInteractionHeight; private final Rect mClipRect = new Rect(); @@ -757,31 +759,34 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } else { mDebugTextUsedYPositions.clear(); } - int y = mTopPadding; - drawDebugInfo(canvas, y, Color.RED, /* label= */ "mTopPadding = "+y); + int y = 0; + drawDebugInfo(canvas, y, Color.RED, /* label= */ "y = " + y); + + y = mTopPadding; + drawDebugInfo(canvas, y, Color.RED, /* label= */ "mTopPadding = " + y); y = getLayoutHeight(); - drawDebugInfo(canvas, y, Color.YELLOW, /* label= */ "getLayoutHeight() = "+y); + drawDebugInfo(canvas, y, Color.YELLOW, /* label= */ "getLayoutHeight() = " + y); y = (int) mMaxLayoutHeight; - drawDebugInfo(canvas, y, Color.MAGENTA, /* label= */ "mMaxLayoutHeight = "+y); + drawDebugInfo(canvas, y, Color.MAGENTA, /* label= */ "mMaxLayoutHeight = " + y); if (mKeyguardBottomPadding >= 0) { y = getHeight() - (int) mKeyguardBottomPadding; drawDebugInfo(canvas, y, Color.GRAY, - /* label= */ "getHeight() - mKeyguardBottomPadding = "+y); + /* label= */ "getHeight() - mKeyguardBottomPadding = " + y); } y = getHeight() - getEmptyBottomMargin(); drawDebugInfo(canvas, y, Color.GREEN, - /* label= */ "getHeight() - getEmptyBottomMargin() = "+y); + /* label= */ "getHeight() - getEmptyBottomMargin() = " + y); y = (int) (mAmbientState.getStackY()); - drawDebugInfo(canvas, y, Color.CYAN, /* label= */ "mAmbientState.getStackY() = "+y); + drawDebugInfo(canvas, y, Color.CYAN, /* label= */ "mAmbientState.getStackY() = " + y); y = (int) (mAmbientState.getStackY() + mAmbientState.getStackHeight()); drawDebugInfo(canvas, y, Color.BLUE, - /* label= */ "mAmbientState.getStackY() + mAmbientState.getStackHeight() = "+y); + /* label= */ "mAmbientState.getStackY() + mAmbientState.getStackHeight() = " + y); y = (int) mAmbientState.getStackY() + mContentHeight; drawDebugInfo(canvas, y, Color.MAGENTA, @@ -790,6 +795,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable y = (int) mAmbientState.getStackY() + mIntrinsicContentHeight; drawDebugInfo(canvas, y, Color.YELLOW, /* label= */ "mAmbientState.getStackY() + mIntrinsicContentHeight = " + y); + + y = (int) (mAmbientState.getStackY() + mKeyguardNotificationAvailableSpace); + drawDebugInfo(canvas, y, Color.RED, /* label= */ + "mAmbientState.getStackY() + mKeyguardNotificationAvailableSpace = " + y); } private void drawDebugInfo(Canvas canvas, int y, int color, String label) { @@ -956,13 +965,15 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } private void reinitView() { - initView(getContext(), mSwipeHelper); + initView(getContext(), mSwipeHelper, mNotificationStackSizeCalculator); } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - void initView(Context context, NotificationSwipeHelper swipeHelper) { + void initView(Context context, NotificationSwipeHelper swipeHelper, + NotificationStackSizeCalculator notificationStackSizeCalculator) { mScroller = new OverScroller(getContext()); mSwipeHelper = swipeHelper; + mNotificationStackSizeCalculator = notificationStackSizeCalculator; setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); setClipChildren(false); @@ -2248,48 +2259,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) private void updateContentHeight() { final float scrimTopPadding = mAmbientState.isOnKeyguard() ? 0 : mMinimumPaddings; - int height = (int) scrimTopPadding; - float previousPaddingRequest = mPaddingBetweenElements; - int numShownItems = 0; - int numShownNotifs = 0; - boolean finish = false; - int maxDisplayedNotifications = mMaxDisplayedNotifications; - ExpandableView previousView = null; - - for (int i = 0; i < getChildCount(); i++) { - ExpandableView expandableView = (ExpandableView) getChildAt(i); - boolean footerViewOnLockScreen = expandableView == mFooterView && onKeyguard(); - - if (expandableView.getVisibility() != View.GONE - && !expandableView.hasNoContentHeight() && !footerViewOnLockScreen) { - - boolean limitReached = maxDisplayedNotifications != -1 - && numShownNotifs >= maxDisplayedNotifications; - final float viewHeight; - if (limitReached) { - viewHeight = mShelf.getIntrinsicHeight(); - finish = true; - } else { - viewHeight = expandableView.getIntrinsicHeight(); - } - if (height != 0) { - height += mPaddingBetweenElements; - } - float gapHeight = calculateGapHeight(previousView, expandableView, numShownNotifs); - height += gapHeight; - height += viewHeight; - - numShownItems++; - if (viewHeight > 0 || !(expandableView instanceof MediaContainerView)) { - // Only count the media as a notification if it has a positive height. - numShownNotifs++; - } - previousView = expandableView; - if (finish) { - break; - } - } - } + final int height = + (int) scrimTopPadding + (int) mNotificationStackSizeCalculator.computeHeight( + /* notificationStackScrollLayout= */ this, mMaxDisplayedNotifications, + mShelf != null ? mShelf.getIntrinsicHeight() : 0); mIntrinsicContentHeight = height; // The topPadding can be bigger than the regular padding when qs is expanded, in that @@ -4932,6 +4905,15 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mKeyguardBottomPadding = keyguardBottomPadding; } + /** + * For debugging only. Enables to draw a line related to the available size for notifications in + * keyguard. + */ + public void setKeyguardAvailableSpaceForDebug(float keyguardNotificationAvailableSpace) { + mKeyguardNotificationAvailableSpace = keyguardNotificationAvailableSpace; + } + + @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void setShouldShowShelfOnly(boolean shouldShowShelfOnly) { mShouldShowShelfOnly = shouldShowShelfOnly; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 6bbecc8438bc..d98f8a77cc8d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -181,6 +181,7 @@ public class NotificationStackScrollLayoutController { private final SectionHeaderController mSilentHeaderController; private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; private final InteractionJankMonitor mJankMonitor; + private final NotificationStackSizeCalculator mNotificationStackSizeCalculator; private final StackStateLogger mStackStateLogger; private final NotificationStackScrollLogger mLogger; @@ -307,6 +308,7 @@ public class NotificationStackScrollLayoutController { R.dimen.lockscreen_shade_notification_movement); mTotalDistanceForFullShadeTransition = mResources.getDimensionPixelSize( R.dimen.lockscreen_shade_qs_transition_distance); + mNotificationStackSizeCalculator.updateResources(); } private final StatusBarStateController.StateListener mStateListener = @@ -662,7 +664,8 @@ public class NotificationStackScrollLayoutController { ShadeController shadeController, InteractionJankMonitor jankMonitor, StackStateLogger stackLogger, - NotificationStackScrollLogger logger) { + NotificationStackScrollLogger logger, + NotificationStackSizeCalculator notificationStackSizeCalculator) { mStackStateLogger = stackLogger; mLogger = logger; mAllowLongPress = allowLongPress; @@ -688,6 +691,7 @@ public class NotificationStackScrollLayoutController { mCentralSurfaces = centralSurfaces; mScrimController = scrimController; mJankMonitor = jankMonitor; + mNotificationStackSizeCalculator = notificationStackSizeCalculator; groupManager.registerGroupExpansionChangeListener( (changedRow, expanded) -> mView.onGroupExpandChanged(changedRow, expanded)); legacyGroupManager.registerGroupChangeListener(new OnGroupChangeListener() { @@ -758,7 +762,7 @@ public class NotificationStackScrollLayoutController { }); } - mView.initView(mView.getContext(), mSwipeHelper); + mView.initView(mView.getContext(), mSwipeHelper, mNotificationStackSizeCalculator); mView.setKeyguardBypassEnabled(mKeyguardBypassController.getBypassEnabled()); mKeyguardBypassController .registerOnBypassStateChangedListener(mView::setKeyguardBypassEnabled); @@ -907,6 +911,13 @@ public class NotificationStackScrollLayoutController { return mView.getTop(); } + /** + * @return the bottom of the view. + */ + public int getBottom() { + return mView.getBottom(); + } + public float getTranslationX() { return mView.getTranslationX(); } @@ -1296,10 +1307,16 @@ public class NotificationStackScrollLayoutController { * appear on the keyguard. * Setting a negative number will disable rendering this line. */ - public void setKeyguardBottomPadding(float keyguardBottomPadding) { + public void setKeyguardBottomPaddingForDebug(float keyguardBottomPadding) { mView.setKeyguardBottomPadding(keyguardBottomPadding); } + /** For debugging only. */ + public void mKeyguardNotificationAvailableSpaceForDebug( + float keyguardNotificationAvailableSpace) { + mView.setKeyguardAvailableSpaceForDebug(keyguardNotificationAvailableSpace); + } + public RemoteInputController.Delegate createDelegate() { return new RemoteInputController.Delegate() { public void setRemoteInputActive(NotificationEntry entry, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt new file mode 100644 index 000000000000..3f97155242b4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.stack + +import android.content.res.Resources +import android.util.Log +import android.view.View.GONE +import com.android.systemui.R +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.statusbar.NotificationLockscreenUserManager +import com.android.systemui.statusbar.StatusBarState.KEYGUARD +import com.android.systemui.statusbar.SysuiStatusBarStateController +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow +import com.android.systemui.statusbar.notification.row.ExpandableView +import com.android.systemui.util.children +import javax.inject.Inject +import kotlin.math.max +import kotlin.properties.Delegates.notNull + +private const val TAG = "NotificationStackSizeCalculator" +private const val DEBUG = false + +/** Calculates number of notifications to display and the height of the notification stack. */ +@SysUISingleton +class NotificationStackSizeCalculator +@Inject +constructor( + private val groupManager: NotificationGroupManagerLegacy, + private val lockscreenUserManager: NotificationLockscreenUserManager, + private val statusBarStateController: SysuiStatusBarStateController, + @Main private val resources: Resources +) { + + /** + * Maximum # notifications to show on Keyguard; extras will be collapsed in an overflow shelf. + * If there are exactly 1 + mMaxKeyguardNotifications, and they fit in the available space + * (considering the overflow shelf is not displayed in this case), then all notifications are + * shown. + */ + private var maxKeyguardNotifications by notNull<Int>() + + /** + * Minimum space between two notifications. There might be more space, see [calculateGapHeight]. + */ + private var notificationPadding by notNull<Int>() + + init { + updateResources() + } + + /** + * Given the [availableSpace] constraint, calculates how many notification to show. + * + * This number is only valid in keyguard. + * + * @param availableSpace space for notifications. This doesn't include the space for the shelf. + */ + fun computeMaxKeyguardNotifications( + stack: NotificationStackScrollLayout, + availableSpace: Float, + shelfHeight: Float + ): Int { + log { + "computeMaxKeyguardNotifications(" + + "availableSpace=$availableSpace shelfHeight=$shelfHeight)" + } + + val children: Sequence<ExpandableView> = stack.childrenSequence + var remainingSpace: Float = availableSpace + var count = 0 + var previous: ExpandableView? = null + val onLockscreen = true + val showableRows = children.filter { it.isShowable(onLockscreen) } + val showableRowsCount = showableRows.count() + showableRows.forEachIndexed { i, current -> + val spaceNeeded = current.spaceNeeded(count, previous, stack, onLockscreen) + previous = current + log { "\ti=$i spaceNeeded=$spaceNeeded remainingSpace=$remainingSpace" } + + if (remainingSpace - spaceNeeded >= 0 && count < maxKeyguardNotifications) { + count += 1 + remainingSpace -= spaceNeeded + } else if (remainingSpace - spaceNeeded > -shelfHeight && i == showableRowsCount - 1) { + log { "Showing all notifications. Shelf is not be needed." } + // If this is the last one, and it fits using the space shelf would use, then we can + // display it, as the shelf will not be needed (as all notifications are shown). + return count + 1 + } else { + log { + "No more fit. Returning $count. Space used: ${availableSpace - remainingSpace}" + } + return count + } + } + log { "All fit. Returning $count" } + return count + } + + /** + * Given the [maxNotifications] constraint, calculates the height of the + * [NotificationStackScrollLayout]. This might or might not be in keyguard. + * + * @param stack stack containing notifications as children. + * @param maxNotifications Maximum number of notifications. When reached, the others will go + * into the shelf. + * @param shelfHeight height of the shelf. It might be zero. + * + * @return height of the stack, including shelf height, if needed. + */ + fun computeHeight( + stack: NotificationStackScrollLayout, + maxNotifications: Int, + shelfHeight: Float + ): Float { + val children: Sequence<ExpandableView> = stack.childrenSequence + val maxNotificationsArg = infiniteIfNegative(maxNotifications) + var height = 0f + var previous: ExpandableView? = null + var count = 0 + val onLockscreen = onLockscreen() + + log { "computeHeight(maxNotification=$maxNotifications, shelf=$shelfHeight" } + children.filter { it.isShowable(onLockscreen) }.forEach { current -> + if (count < maxNotificationsArg) { + val spaceNeeded = current.spaceNeeded(count, previous, stack, onLockscreen) + log { "\ti=$count spaceNeeded=$spaceNeeded" } + height += spaceNeeded + count += 1 + } else { + height += shelfHeight + log { "returning height with shelf -> $height" } + return height + } + previous = current + } + log { "Returning height without shelf -> $height" } + return height + } + + fun updateResources() { + maxKeyguardNotifications = + infiniteIfNegative(resources.getInteger(R.integer.keyguard_max_notification_count)) + + notificationPadding = + max(1, resources.getDimensionPixelSize(R.dimen.notification_divider_height)) + } + + private val NotificationStackScrollLayout.childrenSequence: Sequence<ExpandableView> + get() = children.map { it as ExpandableView } + + private fun onLockscreen() = statusBarStateController.state == KEYGUARD + + private fun ExpandableView.spaceNeeded( + visibleIndex: Int, + previousView: ExpandableView?, + stack: NotificationStackScrollLayout, + onLockscreen: Boolean + ): Float { + assert(isShowable(onLockscreen)) + var size = + if (onLockscreen) { + getMinHeight(/* ignoreTemporaryStates= */ true).toFloat() + } else { + intrinsicHeight.toFloat() + } + if (visibleIndex != 0) { + size += notificationPadding + } + size += calculateGapHeight(stack, previousView, visibleIndex) + return size + } + + private fun ExpandableView.isShowable(onLockscreen: Boolean): Boolean { + if (visibility == GONE || hasNoContentHeight()) return false + if (onLockscreen) { + when (this) { + is ExpandableNotificationRow -> { + if (isSummaryOfSuppressedGroup() || !canShowViewOnLockscreen() || isRemoved) { + return false + } + } + is MediaContainerView -> if (intrinsicHeight == 0) return false + else -> return false + } + } + return true + } + + private fun ExpandableView.calculateGapHeight( + stack: NotificationStackScrollLayout, + previous: ExpandableView?, + visibleIndex: Int + ) = stack.calculateGapHeight(previous, /* current= */ this, visibleIndex) + + private fun ExpandableNotificationRow.isSummaryOfSuppressedGroup() = + groupManager.isSummaryOfSuppressedGroup(entry.sbn) + + /** + * Can a view be shown on the lockscreen when calculating the number of allowed notifications to + * show? + * + * @return `true` if it can be shown. + */ + private fun ExpandableView.canShowViewOnLockscreen(): Boolean { + if (hasNoContentHeight()) { + return false + } + if (this is ExpandableNotificationRow && !canShowRowOnLockscreen()) { + return false + } else if (visibility == GONE) { + return false + } + return true + } + + /** + * Can a row be shown on the lockscreen when calculating the number of allowed notifications to + * show? + * + * @return true if it can be shown + */ + private fun ExpandableNotificationRow.canShowRowOnLockscreen(): Boolean { + if (isSummaryOfSuppressedGroup()) { + return false + } + if (!lockscreenUserManager.shouldShowOnKeyguard(entry)) { + return false + } + return !isRemoved + } + + private fun log(s: () -> String) { + if (DEBUG) { + Log.d(TAG, s()) + } + } + + /** Returns infinite when [v] is negative. Useful in case a resource doesn't limit when -1. */ + private fun infiniteIfNegative(v: Int): Int = + if (v < 0) { + Int.MAX_VALUE + } else { + v + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java index 732e5f0343a2..602d075b167d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java @@ -23,6 +23,7 @@ import static com.android.systemui.statusbar.notification.NotificationUtils.inte import android.content.res.Resources; import android.util.MathUtils; +import com.android.keyguard.BouncerPanelExpansionCalculator; import com.android.keyguard.KeyguardStatusView; import com.android.systemui.R; import com.android.systemui.animation.Interpolators; @@ -169,7 +170,8 @@ public class KeyguardClockPositionAlgorithm { boolean isSplitShade, float udfpsTop, float clockBottom, boolean isClockTopAligned) { mMinTopMargin = keyguardStatusBarHeaderHeight + Math.max(mContainerTopPadding, userSwitchHeight); - mPanelExpansion = panelExpansion; + mPanelExpansion = BouncerPanelExpansionCalculator + .getKeyguardClockScaledExpansion(panelExpansion); mKeyguardStatusHeight = keyguardStatusHeight + mStatusViewBottomMargin; mUserSwitchHeight = userSwitchHeight; mUserSwitchPreferredY = userSwitchPreferredY; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt index 571c10b3800f..64b0b4e2909f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt @@ -16,34 +16,52 @@ package com.android.systemui.statusbar.phone +import android.content.Context +import android.content.pm.PackageManager import android.hardware.Sensor import android.hardware.TriggerEvent import android.hardware.TriggerEventListener import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback +import com.android.systemui.CoreStartable import com.android.systemui.Dumpable +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.util.Assert import com.android.systemui.util.sensors.AsyncSensorManager import java.io.FileDescriptor import java.io.PrintWriter +import javax.inject.Inject -class KeyguardLiftController constructor( +/** + * Triggers face auth on lift when the device is showing the lock screen. Only initialized + * if face auth is supported on the device. Not to be confused with the lift to wake gesture + * which is handled by {@link com.android.server.policy.PhoneWindowManager}. + */ +@SysUISingleton +class KeyguardLiftController @Inject constructor( + private val context: Context, private val statusBarStateController: StatusBarStateController, private val asyncSensorManager: AsyncSensorManager, private val keyguardUpdateMonitor: KeyguardUpdateMonitor, - dumpManager: DumpManager -) : StatusBarStateController.StateListener, Dumpable, KeyguardUpdateMonitorCallback() { + private val dumpManager: DumpManager +) : Dumpable, CoreStartable(context) { private val pickupSensor = asyncSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE) private var isListening = false private var bouncerVisible = false - init { + override fun start() { + if (context.packageManager.hasSystemFeature(PackageManager.FEATURE_FACE)) { + init() + } + } + + private fun init() { dumpManager.registerDumpable(javaClass.name, this) - statusBarStateController.addCallback(this) - keyguardUpdateMonitor.registerCallback(this) + statusBarStateController.addCallback(statusBarStateListener) + keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback) updateListeningState() } @@ -58,17 +76,21 @@ class KeyguardLiftController constructor( } } - override fun onDozingChanged(isDozing: Boolean) { - updateListeningState() - } + private val keyguardUpdateMonitorCallback = object : KeyguardUpdateMonitorCallback() { + override fun onKeyguardBouncerChanged(bouncer: Boolean) { + bouncerVisible = bouncer + updateListeningState() + } - override fun onKeyguardBouncerChanged(bouncer: Boolean) { - bouncerVisible = bouncer - updateListeningState() + override fun onKeyguardVisibilityChanged(showing: Boolean) { + updateListeningState() + } } - override fun onKeyguardVisibilityChanged(showing: Boolean) { - updateListeningState() + private val statusBarStateListener = object : StatusBarStateController.StateListener { + override fun onDozingChanged(isDozing: Boolean) { + updateListeningState() + } } override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) { 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 65173a230871..cb332bdf59b1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java @@ -174,7 +174,7 @@ public class KeyguardStatusBarView extends RelativeLayout { } private void updateKeyguardStatusBarHeight() { - MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams(); + MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams(); lp.height = getStatusBarHeaderHeightKeyguard(mContext); setLayoutParams(lp); } @@ -510,28 +510,6 @@ public class KeyguardStatusBarView extends RelativeLayout { } } - void onSystemChromeAnimationStart(boolean isAnimatingOut) { - if (isAnimatingOut) { - mSystemIconsContainer.setVisibility(View.VISIBLE); - mSystemIconsContainer.setAlpha(0f); - } - } - - void onSystemChromeAnimationEnd(boolean isAnimatingIn) { - // Make sure the system icons are out of the way - if (isAnimatingIn) { - mSystemIconsContainer.setVisibility(View.INVISIBLE); - mSystemIconsContainer.setAlpha(0f); - } else { - mSystemIconsContainer.setAlpha(1f); - mSystemIconsContainer.setVisibility(View.VISIBLE); - } - } - - void onSystemChromeAnimationUpdate(float animatedValue) { - mSystemIconsContainer.setAlpha(animatedValue); - } - @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java index 1df1aff38593..a70ba8236e9a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java @@ -17,8 +17,6 @@ package com.android.systemui.statusbar.phone; import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; -import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_IN; -import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_OUT; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -47,6 +45,7 @@ import com.android.systemui.statusbar.notification.AnimatableProperty; import com.android.systemui.statusbar.notification.PropertyAnimator; import com.android.systemui.statusbar.notification.stack.AnimationProperties; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; +import com.android.systemui.statusbar.phone.fragment.StatusBarSystemEventAnimator; import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserInfoTracker; import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherController; import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherFeatureController; @@ -59,7 +58,6 @@ import com.android.systemui.util.ViewController; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.Arrays; -import java.util.Collections; import java.util.List; import javax.inject.Inject; @@ -107,6 +105,8 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat @Override public void onDensityOrFontScaleChanged() { mView.loadDimens(); + // The animator is dependent on resources for offsets + mSystemEventAnimator = new StatusBarSystemEventAnimator(mView, getResources()); } @Override @@ -123,21 +123,16 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat private final SystemStatusAnimationCallback mAnimationCallback = new SystemStatusAnimationCallback() { + @NonNull @Override - public void onSystemChromeAnimationStart() { - mView.onSystemChromeAnimationStart( - mAnimationScheduler.getAnimationState() == ANIMATING_OUT); + public Animator onSystemEventAnimationFinish(boolean hasPersistentDot) { + return mSystemEventAnimator.onSystemEventAnimationFinish(hasPersistentDot); } + @NonNull @Override - public void onSystemChromeAnimationEnd() { - mView.onSystemChromeAnimationEnd( - mAnimationScheduler.getAnimationState() == ANIMATING_IN); - } - - @Override - public void onSystemChromeAnimationUpdate(@NonNull ValueAnimator anim) { - mView.onSystemChromeAnimationUpdate((float) anim.getAnimatedValue()); + public Animator onSystemEventAnimationBegin() { + return mSystemEventAnimator.onSystemEventAnimationBegin(); } }; @@ -232,6 +227,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat private int mStatusBarState; private boolean mDozing; private boolean mShowingKeyguardHeadsUp; + private StatusBarSystemEventAnimator mSystemEventAnimator; @Inject public KeyguardStatusBarViewController( @@ -292,16 +288,15 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat ); Resources r = getResources(); - mBlockedIcons = Collections.unmodifiableList(Arrays.asList( - r.getString(com.android.internal.R.string.status_bar_volume), - r.getString(com.android.internal.R.string.status_bar_alarm_clock), - r.getString(com.android.internal.R.string.status_bar_call_strength))); + mBlockedIcons = Arrays.asList(r.getStringArray( + R.array.config_keyguard_statusbar_icon_blocklist)); mNotificationsHeaderCollideDistance = r.getDimensionPixelSize( R.dimen.header_notifications_collide_distance); mView.setKeyguardUserAvatarEnabled( !mFeatureController.isStatusBarUserSwitcherFeatureEnabled()); mFeatureController.addCallback(enabled -> mView.setKeyguardUserAvatarEnabled(!enabled)); + mSystemEventAnimator = new StatusBarSystemEventAnimator(mView, r); } @Override 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 1891ab017ef8..5746ffb6debe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -17,7 +17,6 @@ package com.android.systemui.statusbar.phone; import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; -import static android.view.View.GONE; import static androidx.constraintlayout.widget.ConstraintSet.END; import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID; @@ -144,7 +143,6 @@ import com.android.systemui.statusbar.GestureRecorder; import com.android.systemui.statusbar.KeyguardAffordanceView; import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.LockscreenShadeTransitionController; -import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeWindowController; @@ -165,17 +163,16 @@ import com.android.systemui.statusbar.notification.PropertyAnimator; import com.android.systemui.statusbar.notification.ViewGroupFadeHelper; import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.collection.render.ShadeViewManager; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.stack.AmbientState; import com.android.systemui.statusbar.notification.stack.AnimationProperties; -import com.android.systemui.statusbar.notification.stack.MediaContainerView; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; +import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent; import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent; @@ -312,9 +309,6 @@ public class NotificationPanelViewController extends PanelViewController { private final ControlsComponent mControlsComponent; private final NotificationRemoteInputManager mRemoteInputManager; - // Maximum # notifications to show on Keyguard; extras will be collapsed in an overflow card. - // If there are exactly 1 + mMaxKeyguardNotifications, then still shows all notifications - private final int mMaxKeyguardNotifications; private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; private final TapAgainViewController mTapAgainViewController; private final SplitShadeHeaderController mSplitShadeHeaderController; @@ -323,6 +317,8 @@ public class NotificationPanelViewController extends PanelViewController { private boolean mShouldUseSplitNotificationShade; // The bottom padding reserved for elements of the keyguard measuring notifications private float mKeyguardNotificationBottomPadding; + // Space available for notifications. + private float mKeyguardNotificationAvailableSpace; // Current max allowed keyguard notifications determined by measuring the panel private int mMaxAllowedKeyguardNotifications; @@ -446,8 +442,6 @@ public class NotificationPanelViewController extends PanelViewController { setHeadsUpAnimatingAway(false); updatePanelExpansionAndVisibility(); }; - // TODO (b/162832756): once migrated to the new pipeline, delete legacy group manager - private NotificationGroupManagerLegacy mGroupManager; private boolean mShowIconsWhenExpanded; private int mIndicationBottomPadding; private int mAmbientIndicationBottomPadding; @@ -509,7 +503,6 @@ public class NotificationPanelViewController extends PanelViewController { private final NotificationEntryManager mEntryManager; private final CommandQueue mCommandQueue; - private final NotificationLockscreenUserManager mLockscreenUserManager; private final UserManager mUserManager; private final MediaDataManager mMediaDataManager; private final SysUiState mSysUiState; @@ -651,6 +644,7 @@ public class NotificationPanelViewController extends PanelViewController { mNotificationPanelUnfoldAnimationController; private final NotificationListContainer mNotificationListContainer; + private final NotificationStackSizeCalculator mNotificationStackSizeCalculator; private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() { @Override @@ -695,7 +689,6 @@ public class NotificationPanelViewController extends PanelViewController { DynamicPrivacyController dynamicPrivacyController, KeyguardBypassController bypassController, FalsingManager falsingManager, FalsingCollector falsingCollector, - NotificationLockscreenUserManager notificationLockscreenUserManager, NotificationEntryManager notificationEntryManager, KeyguardStateController keyguardStateController, StatusBarStateController statusBarStateController, @@ -721,7 +714,6 @@ public class NotificationPanelViewController extends PanelViewController { KeyguardUserSwitcherComponent.Factory keyguardUserSwitcherComponentFactory, KeyguardStatusBarViewComponent.Factory keyguardStatusBarViewComponentFactory, LockscreenShadeTransitionController lockscreenShadeTransitionController, - NotificationGroupManagerLegacy groupManager, NotificationIconAreaController notificationIconAreaController, AuthController authController, ScrimController scrimController, @@ -753,7 +745,8 @@ public class NotificationPanelViewController extends PanelViewController { SysUiState sysUiState, KeyguardUnlockAnimationController keyguardUnlockAnimationController, NotificationListContainer notificationListContainer, - PanelEventsEmitter panelEventsEmitter) { + PanelEventsEmitter panelEventsEmitter, + NotificationStackSizeCalculator notificationStackSizeCalculator) { super(view, falsingManager, dozeLog, @@ -785,9 +778,9 @@ public class NotificationPanelViewController extends PanelViewController { mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; mNotificationsQSContainerController = notificationsQSContainerController; mNotificationListContainer = notificationListContainer; + mNotificationStackSizeCalculator = notificationStackSizeCalculator; mNotificationsQSContainerController.init(); mNotificationStackScrollLayoutController = notificationStackScrollLayoutController; - mGroupManager = groupManager; mNotificationIconAreaController = notificationIconAreaController; mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory; mKeyguardStatusBarViewComponentFactory = keyguardStatusBarViewComponentFactory; @@ -849,7 +842,6 @@ public class NotificationPanelViewController extends PanelViewController { }); mBottomAreaShadeAlphaAnimator.setDuration(160); mBottomAreaShadeAlphaAnimator.setInterpolator(Interpolators.ALPHA_OUT); - mLockscreenUserManager = notificationLockscreenUserManager; mEntryManager = notificationEntryManager; mConversationNotificationManager = conversationNotificationManager; mAuthController = authController; @@ -874,7 +866,6 @@ public class NotificationPanelViewController extends PanelViewController { mView.getOverlay().add(new DebugDrawable()); } - mMaxKeyguardNotifications = resources.getInteger(R.integer.keyguard_max_notification_count); mKeyguardUnfoldTransition = unfoldComponent.map(c -> c.getKeyguardUnfoldTransition()); mNotificationPanelUnfoldAnimationController = unfoldComponent.map( SysUIUnfoldComponent::getNotificationPanelUnfoldAnimationController); @@ -1238,12 +1229,14 @@ public class NotificationPanelViewController extends PanelViewController { if (mKeyguardShowing && !mKeyguardBypassController.getBypassEnabled()) { mNotificationStackScrollLayoutController.setMaxDisplayedNotifications( mMaxAllowedKeyguardNotifications); - mNotificationStackScrollLayoutController.setKeyguardBottomPadding( + mNotificationStackScrollLayoutController.setKeyguardBottomPaddingForDebug( mKeyguardNotificationBottomPadding); + mNotificationStackScrollLayoutController.mKeyguardNotificationAvailableSpaceForDebug( + mKeyguardNotificationAvailableSpace); } else { // no max when not on the keyguard mNotificationStackScrollLayoutController.setMaxDisplayedNotifications(-1); - mNotificationStackScrollLayoutController.setKeyguardBottomPadding(-1f); + mNotificationStackScrollLayoutController.setKeyguardBottomPaddingForDebug(-1f); } } @@ -1454,127 +1447,38 @@ public class NotificationPanelViewController extends PanelViewController { * @return the maximum keyguard notifications that can fit on the screen */ private int computeMaxKeyguardNotifications() { - float minPadding = mClockPositionAlgorithm.getMinStackScrollerPadding(); int notificationPadding = Math.max( 1, mResources.getDimensionPixelSize(R.dimen.notification_divider_height)); - float shelfSize = + float topPadding = mNotificationStackScrollLayoutController.getTopPadding(); + float shelfHeight = mNotificationShelfController.getVisibility() == View.GONE ? 0 : mNotificationShelfController.getIntrinsicHeight() + notificationPadding; + // Padding to add to the bottom of the stack to keep a minimum distance from the top of + // the lock icon. float lockIconPadding = 0; if (mLockIconViewController.getTop() != 0) { - lockIconPadding = mCentralSurfaces.getDisplayHeight() - mLockIconViewController.getTop() - + mResources.getDimensionPixelSize(R.dimen.min_lock_icon_padding); + final float lockIconTopWithPadding = mLockIconViewController.getTop() + - mResources.getDimensionPixelSize(R.dimen.min_lock_icon_padding); + lockIconPadding = mNotificationStackScrollLayoutController.getBottom() + - lockIconTopWithPadding; } - float bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding); - bottomPadding = Math.max(lockIconPadding, bottomPadding); + float bottomPadding = Math.max(lockIconPadding, + Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding)); mKeyguardNotificationBottomPadding = bottomPadding; float availableSpace = mNotificationStackScrollLayoutController.getHeight() - - minPadding - - shelfSize + - topPadding + - shelfHeight - bottomPadding; + mKeyguardNotificationAvailableSpace = availableSpace; - int count = 0; - ExpandableView previousView = null; - for (int i = 0; i < mNotificationStackScrollLayoutController.getChildCount(); i++) { - ExpandableView child = mNotificationStackScrollLayoutController.getChildAt(i); - if (child instanceof ExpandableNotificationRow) { - ExpandableNotificationRow row = (ExpandableNotificationRow) child; - boolean suppressedSummary = mGroupManager != null - && mGroupManager.isSummaryOfSuppressedGroup(row.getEntry().getSbn()); - if (suppressedSummary) { - continue; - } - if (!canShowViewOnLockscreen(child)) { - continue; - } - if (row.isRemoved()) { - continue; - } - } else if (child instanceof MediaContainerView) { - if (child.getVisibility() == GONE) { - continue; - } - if (child.getIntrinsicHeight() == 0) { - continue; - } - } else { - continue; - } - availableSpace -= child.getMinHeight(true /* ignoreTemporaryStates */); - availableSpace -= count == 0 ? 0 : notificationPadding; - availableSpace -= mNotificationStackScrollLayoutController - .calculateGapHeight(previousView, child, count); - previousView = child; - if (availableSpace >= 0 - && (mMaxKeyguardNotifications == -1 || count < mMaxKeyguardNotifications)) { - count++; - } else if (availableSpace > -shelfSize) { - // if we are exactly the last view, then we can show us still! - int childCount = mNotificationStackScrollLayoutController.getChildCount(); - for (int j = i + 1; j < childCount; j++) { - ExpandableView view = mNotificationStackScrollLayoutController.getChildAt(j); - if (view instanceof ExpandableNotificationRow - && canShowViewOnLockscreen(view)) { - return count; - } - } - count++; - return count; - } else { - return count; - } - } - return count; - } - - /** - * Can a view be shown on the lockscreen when calculating the number of allowed notifications - * to show? - * - * @param child the view in question - * @return true if it can be shown - */ - private boolean canShowViewOnLockscreen(ExpandableView child) { - if (child.hasNoContentHeight()) { - return false; - } - if (child instanceof ExpandableNotificationRow && - !canShowRowOnLockscreen((ExpandableNotificationRow) child)) { - return false; - } else if (child.getVisibility() == GONE) { - // ENRs can be gone and count because their visibility is only set after - // this calculation, but all other views should be up to date - return false; - } - return true; - } - - /** - * Can a row be shown on the lockscreen when calculating the number of allowed notifications - * to show? - * - * @param row the row in question - * @return true if it can be shown - */ - private boolean canShowRowOnLockscreen(ExpandableNotificationRow row) { - boolean suppressedSummary = - mGroupManager != null && mGroupManager.isSummaryOfSuppressedGroup( - row.getEntry().getSbn()); - if (suppressedSummary) { - return false; - } - if (!mLockscreenUserManager.shouldShowOnKeyguard(row.getEntry())) { - return false; - } - if (row.isRemoved()) { - return false; - } - return true; + return mNotificationStackSizeCalculator.computeMaxKeyguardNotifications( + mNotificationStackScrollLayoutController.getView(), availableSpace, + shelfHeight); } private void updateClock() { @@ -4883,6 +4787,8 @@ public class NotificationPanelViewController extends PanelViewController { "calculateNotificationsTopPadding()"); drawDebugInfo(canvas, mClockPositionResult.clockY, Color.GRAY, "mClockPositionResult.clockY"); + drawDebugInfo(canvas, (int) mLockIconViewController.getTop(), Color.GRAY, + "mLockIconViewController.getTop()"); mDebugPaint.setColor(Color.CYAN); canvas.drawLine(0, mClockPositionResult.stackScrollerPadding, mView.getWidth(), 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 24f5ff86a794..78edc07c8544 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java @@ -445,7 +445,7 @@ public abstract class PanelViewController { mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_UNLOCK); } @Classifier.InteractionType int interactionType = vel == 0 ? GENERIC - : vel > 0 ? QUICK_SETTINGS + : y - mInitialTouchY > 0 ? QUICK_SETTINGS : (mKeyguardStateController.canDismissLockScreen() ? UNLOCK : BOUNCER_UNLOCK); @@ -532,7 +532,7 @@ public abstract class PanelViewController { return true; } - @Classifier.InteractionType int interactionType = vel > 0 + @Classifier.InteractionType int interactionType = y - mInitialTouchY > 0 ? QUICK_SETTINGS : ( mKeyguardStateController.canDismissLockScreen() ? UNLOCK : BOUNCER_UNLOCK); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 419661b766d6..029a7a5fcdd5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -41,6 +41,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.colorextraction.ColorExtractor.GradientColors; import com.android.internal.graphics.ColorUtils; import com.android.internal.util.function.TriConsumer; +import com.android.keyguard.BouncerPanelExpansionCalculator; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.settingslib.Utils; @@ -116,6 +117,15 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump private float mTransitionToFullShadeProgress; /** + * Same as {@link #mTransitionToFullShadeProgress}, but specifically for the notifications scrim + * on the lock screen. + * + * On split shade lock screen we want the different scrims to fade in at different times and + * rates. + */ + private float mTransitionToLockScreenFullShadeNotificationsProgress; + + /** * If we're currently transitioning to the full shade. */ private boolean mTransitioningToFullShade; @@ -574,11 +584,17 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump * Set the amount of progress we are currently in if we're transitioning to the full shade. * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full * shade. + * + * @param progress the progress for all scrims. + * @param lockScreenNotificationsProgress the progress specifically for the notifications scrim. */ - public void setTransitionToFullShadeProgress(float progress) { - if (progress != mTransitionToFullShadeProgress) { + public void setTransitionToFullShadeProgress(float progress, + float lockScreenNotificationsProgress) { + if (progress != mTransitionToFullShadeProgress || lockScreenNotificationsProgress + != mTransitionToLockScreenFullShadeNotificationsProgress) { mTransitionToFullShadeProgress = progress; - setTransitionToFullShade(progress > 0.0f); + mTransitionToLockScreenFullShadeNotificationsProgress = lockScreenNotificationsProgress; + setTransitionToFullShade(progress > 0.0f || lockScreenNotificationsProgress > 0.0f); applyAndDispatchState(); } } @@ -754,12 +770,13 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump } else { mNotificationsAlpha = Math.max(1.0f - getInterpolatedFraction(), mQsExpansion); } - if (mState == ScrimState.KEYGUARD && mTransitionToFullShadeProgress > 0.0f) { + if (mState == ScrimState.KEYGUARD + && mTransitionToLockScreenFullShadeNotificationsProgress > 0.0f) { // Interpolate the notification alpha when transitioning! mNotificationsAlpha = MathUtils.lerp( mNotificationsAlpha, getInterpolatedFraction(), - mTransitionToFullShadeProgress); + mTransitionToLockScreenFullShadeNotificationsProgress); } mNotificationsTint = mState.getNotifTint(); mBehindTint = behindTint; @@ -792,7 +809,15 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump private Pair<Integer, Float> calculateBackStateForState(ScrimState state) { // Either darken of make the scrim transparent when you // pull down the shade - float interpolatedFract = getInterpolatedFraction(); + float interpolatedFract; + + if (state == ScrimState.KEYGUARD) { + interpolatedFract = BouncerPanelExpansionCalculator + .getBackScrimScaledExpansion(mPanelExpansionFraction); + } else { + interpolatedFract = getInterpolatedFraction(); + } + float stateBehind = mClipsQsScrim ? state.getNotifAlpha() : state.getBehindAlpha(); float behindAlpha; int behindTint; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index 637e4bee8948..6fe92fafc075 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -397,15 +397,25 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte mMainThreadHandler.post(() -> { final Runnable removeNotification = () -> { mOnUserInteractionCallback.onDismiss(entry, REASON_CLICK, summaryToRemove); + if (!animate) { + // If we're animating, this would be invoked after the activity launch + // animation completes. Since we're not animating, the launch already + // happened synchronously, so we notify the launch is complete here after + // onDismiss. + mLaunchEventsEmitter.notifyFinishLaunchNotifActivity(entry); + } }; if (mPresenter.isCollapsing()) { - // To avoid lags we're only performing the remove - // after the shade is collapsed + // To avoid lags we're only performing the remove after the shade is collapsed mShadeController.addPostCollapseAction(removeNotification); } else { removeNotification.run(); } }); + } else if (!canBubble && !animate) { + // Not animating, this is the end of the launch flow (see above comment for more info). + mMainThreadHandler.post( + () -> mLaunchEventsEmitter.notifyFinishLaunchNotifActivity(entry)); } mIsCollapsingToShowActivityOverLockscreen = false; @@ -481,8 +491,9 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte boolean isActivityIntent) { mLogger.logStartNotificationIntent(entry.getKey(), intent); try { - Runnable onFinishAnimationCallback = - () -> mLaunchEventsEmitter.notifyFinishLaunchNotifActivity(entry); + Runnable onFinishAnimationCallback = animate + ? () -> mLaunchEventsEmitter.notifyFinishLaunchNotifActivity(entry) + : null; ActivityLaunchAnimator.Controller animationController = new StatusBarLaunchAnimatorController( mNotificationAnimationProvider diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java index 2c84219dbfd0..8194957c52fc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java @@ -20,12 +20,10 @@ import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS; import static android.app.StatusBarManager.DISABLE_ONGOING_CALL_CHIP; import static android.app.StatusBarManager.DISABLE_SYSTEM_INFO; -import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_IN; -import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_OUT; import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.IDLE; import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.SHOWING_PERSISTENT_DOT; -import android.animation.ValueAnimator; +import android.animation.Animator; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; @@ -74,6 +72,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.settings.SecureSettings; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.concurrent.Executor; @@ -136,6 +135,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } }; private OperatorNameViewController mOperatorNameViewController; + private StatusBarSystemEventAnimator mSystemEventAnimator; @SuppressLint("ValidFragment") public CollapsedStatusBarFragment( @@ -210,18 +210,31 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue initEmergencyCryptkeeperText(); initOperatorName(); initNotificationIconArea(); - mAnimationScheduler.addCallback(this); + mSystemEventAnimator = + new StatusBarSystemEventAnimator(mSystemIconArea, getResources()); } @VisibleForTesting void updateBlockedIcons() { mBlockedIcons.clear(); - if (mSecureSettings.getInt(Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON, 0) == 0) { - mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_volume)); + // Reload the blocklist from res + List<String> blockList = Arrays.asList(getResources().getStringArray( + R.array.config_collapsed_statusbar_icon_blocklist)); + String vibrateIconSlot = getString(com.android.internal.R.string.status_bar_volume); + boolean showVibrateIcon = + mSecureSettings.getInt(Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON, 0) == 0; + + // Filter out vibrate icon from the blocklist if the setting is on + for (int i = 0; i < blockList.size(); i++) { + if (blockList.get(i).equals(vibrateIconSlot)) { + if (showVibrateIcon) { + mBlockedIcons.add(blockList.get(i)); + } + } else { + mBlockedIcons.add(blockList.get(i)); + } } - mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_alarm_clock)); - mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_call_strength)); mMainExecutor.execute(() -> mDarkIconManager.setBlockList(mBlockedIcons)); } @@ -245,6 +258,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue mCommandQueue.addCallback(this); mStatusBarStateController.addCallback(this); initOngoingCallChip(); + mAnimationScheduler.addCallback(this); mSecureSettings.registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON), @@ -258,6 +272,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue mCommandQueue.removeCallback(this); mStatusBarStateController.removeCallback(this); mOngoingCallController.removeCallback(mOngoingCallListener); + mAnimationScheduler.removeCallback(this); mSecureSettings.unregisterContentObserver(mVolumeSettingObserver); } @@ -265,7 +280,6 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue public void onDestroyView() { super.onDestroyView(); mStatusBarIconController.removeIconGroup(mDarkIconManager); - mAnimationScheduler.removeCallback(this); if (mNetworkController.hasEmergencyCryptKeeperText()) { mNetworkController.removeCallback(mSignalCallback); } @@ -576,35 +590,16 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue disable(getContext().getDisplayId(), mDisabled1, mDisabled2, false /* animate */); } + @Nullable @Override - public void onSystemChromeAnimationStart() { - if (mAnimationScheduler.getAnimationState() == ANIMATING_OUT - && !isSystemIconAreaDisabled()) { - mSystemIconArea.setVisibility(View.VISIBLE); - mSystemIconArea.setAlpha(0f); - } - } - - @Override - public void onSystemChromeAnimationEnd() { - // Make sure the system icons are out of the way - if (mAnimationScheduler.getAnimationState() == ANIMATING_IN) { - mSystemIconArea.setVisibility(View.INVISIBLE); - mSystemIconArea.setAlpha(0f); - } else { - if (isSystemIconAreaDisabled()) { - // don't unhide - return; - } - - mSystemIconArea.setAlpha(1f); - mSystemIconArea.setVisibility(View.VISIBLE); - } + public Animator onSystemEventAnimationBegin() { + return mSystemEventAnimator.onSystemEventAnimationBegin(); } + @Nullable @Override - public void onSystemChromeAnimationUpdate(@NonNull ValueAnimator animator) { - mSystemIconArea.setAlpha((float) animator.getAnimatedValue()); + public Animator onSystemEventAnimationFinish(boolean hasPersistentDot) { + return mSystemEventAnimator.onSystemEventAnimationFinish(hasPersistentDot); } private boolean isSystemIconAreaDisabled() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt new file mode 100644 index 000000000000..f530ec83aec5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone.fragment + +import android.animation.Animator +import android.animation.AnimatorSet +import android.animation.ValueAnimator +import android.content.res.Resources +import android.view.View +import com.android.systemui.R +import com.android.systemui.statusbar.events.STATUS_BAR_X_MOVE_IN +import com.android.systemui.statusbar.events.STATUS_BAR_X_MOVE_OUT +import com.android.systemui.statusbar.events.SystemStatusAnimationCallback + +/** + * Tied directly to [SystemStatusAnimationScheduler]. Any StatusBar-like thing (keyguard, collapsed + * status bar fragment), can just feed this an animatable view to get the default system status + * animation. + * + * This animator relies on resources, and should be recreated whenever resources are updated. While + * this class could be used directly as the animation callback, it's probably best to forward calls + * to it so that it can be recreated at any moment without needing to remove/add callback. + */ +class StatusBarSystemEventAnimator( + val animatedView: View, + resources: Resources +) : SystemStatusAnimationCallback { + private val translationXIn: Int = resources.getDimensionPixelSize( + R.dimen.ongoing_appops_chip_animation_in_status_bar_translation_x) + private val translationXOut: Int = resources.getDimensionPixelSize( + R.dimen.ongoing_appops_chip_animation_out_status_bar_translation_x) + + override fun onSystemEventAnimationBegin(): Animator { + val moveOut = ValueAnimator.ofFloat(0f, 1f).setDuration(383) + moveOut.interpolator = STATUS_BAR_X_MOVE_OUT + moveOut.addUpdateListener { animation: ValueAnimator -> + animatedView.translationX = -(translationXIn * animation.animatedValue as Float) + } + val alphaOut = ValueAnimator.ofFloat(1f, 0f).setDuration(133) + alphaOut.interpolator = null + alphaOut.addUpdateListener { animation: ValueAnimator -> + animatedView.alpha = animation.animatedValue as Float + } + + val animSet = AnimatorSet() + animSet.playTogether(moveOut, alphaOut) + return animSet + } + + override fun onSystemEventAnimationFinish(hasPersistentDot: Boolean): Animator { + animatedView.translationX = translationXOut.toFloat() + val moveIn = ValueAnimator.ofFloat(1f, 0f).setDuration(467) + moveIn.startDelay = 33 + moveIn.interpolator = STATUS_BAR_X_MOVE_IN + moveIn.addUpdateListener { animation: ValueAnimator -> + animatedView.translationX = translationXOut * animation.animatedValue as Float + } + val alphaIn = ValueAnimator.ofFloat(0f, 1f).setDuration(167) + alphaIn.startDelay = 67 + alphaIn.interpolator = null + alphaIn.addUpdateListener { animation: ValueAnimator -> + animatedView.alpha = animation.animatedValue as Float + } + + val animatorSet = AnimatorSet() + animatorSet.playTogether(moveIn, alphaIn) + + return animatorSet + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt index c0d7925cf2bb..9e9b74616d29 100644 --- a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt @@ -23,7 +23,6 @@ import android.content.IntentFilter import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable import android.graphics.drawable.GradientDrawable -import android.graphics.drawable.InsetDrawable import android.graphics.drawable.LayerDrawable import android.os.Bundle import android.os.UserManager @@ -149,8 +148,8 @@ class UserSwitcherActivity @Inject constructor( } private fun getDrawable(item: UserRecord): Drawable { - var drawable = if (item.isCurrent && item.isGuest) { - getDrawable(R.drawable.ic_avatar_guest_user) + var drawable = if (item.isGuest) { + getDrawable(R.drawable.ic_account_circle) } else { findUserIcon(item) } @@ -168,7 +167,7 @@ class UserSwitcherActivity @Inject constructor( val ld = getDrawable(R.drawable.user_switcher_icon_large).mutate() as LayerDrawable if (item == userSwitcherController.getCurrentUserRecord()) { - (ld.getDrawable(1) as GradientDrawable).apply { + (ld.findDrawableByLayerId(R.id.ring) as GradientDrawable).apply { val stroke = resources .getDimensionPixelSize(R.dimen.user_switcher_icon_selected_width) val color = Utils.getColorAttrDefaultColor( @@ -180,15 +179,7 @@ class UserSwitcherActivity @Inject constructor( } } - ld.addLayer( - InsetDrawable( - drawable, - resources.getDimensionPixelSize( - R.dimen.user_switcher_icon_large_margin - ) - ) - ) - + ld.setDrawableByLayerId(R.id.user_avatar, drawable) return ld } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/BouncerPanelExpansionCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/BouncerPanelExpansionCalculatorTest.kt new file mode 100644 index 000000000000..6266bf146f5b --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/BouncerPanelExpansionCalculatorTest.kt @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.keyguard + +import android.test.suitebuilder.annotation.SmallTest +import android.testing.AndroidTestingRunner +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class BouncerPanelExpansionCalculatorTest : SysuiTestCase() { + @Test + fun testGetHostViewScaledExpansion() { + assertThat(BouncerPanelExpansionCalculator.getHostViewScaledExpansion(1f)) + .isEqualTo(1f) + assertThat(BouncerPanelExpansionCalculator.getHostViewScaledExpansion(0.9f)) + .isEqualTo(1f) + assertThat(BouncerPanelExpansionCalculator.getHostViewScaledExpansion(0.59f)) + .isEqualTo(0f) + assertThat(BouncerPanelExpansionCalculator.getHostViewScaledExpansion(0f)) + .isEqualTo(0f) + assertEquals(BouncerPanelExpansionCalculator + .getHostViewScaledExpansion(0.8f), 2f / 3f, 0.01f) + } + + @Test + fun testGetBackScrimScaledExpansion() { + assertThat(BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(1f)) + .isEqualTo(1f) + assertEquals(BouncerPanelExpansionCalculator + .getBackScrimScaledExpansion(0.95f), 1f / 2f, 0.01f) + assertThat(BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(0.9f)) + .isEqualTo(0f) + assertThat(BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(0.5f)) + .isEqualTo(0f) + assertThat(BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(0f)) + .isEqualTo(0f) + } + + @Test + fun testGetKeyguardClockScaledExpansion() { + assertThat(BouncerPanelExpansionCalculator.getKeyguardClockScaledExpansion(1f)) + .isEqualTo(1f) + assertEquals(BouncerPanelExpansionCalculator + .getKeyguardClockScaledExpansion(0.8f), 1f / 3f, 0.01f) + assertThat(BouncerPanelExpansionCalculator.getKeyguardClockScaledExpansion(0.7f)) + .isEqualTo(0f) + assertThat(BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(0.5f)) + .isEqualTo(0f) + assertThat(BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(0f)) + .isEqualTo(0f) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java index 7a0db1fd975c..8c7927782d2d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java +++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java @@ -208,6 +208,11 @@ public abstract class SysuiTestCase { } } + /** Delegates to {@link android.testing.TestableResources#addOverride(int, Object)}. */ + protected void overrideResource(int resourceId, Object value) { + mContext.getOrCreateTestableResources().addOverride(resourceId, value); + } + public static final class EmptyRunnable implements Runnable { public void run() { } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt index ec2c1de49b4c..a95da6295350 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt @@ -114,12 +114,13 @@ class AuthRippleControllerTest : SysuiTestCase() { } @Test - fun testFingerprintTrigger_Ripple() { + fun testFingerprintTrigger_KeyguardVisible_Ripple() { // GIVEN fp exists, keyguard is visible, user doesn't need strong auth val fpsLocation = PointF(5f, 5f) `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation) controller.onViewAttached() `when`(keyguardUpdateMonitor.isKeyguardVisible).thenReturn(true) + `when`(keyguardUpdateMonitor.isDreaming).thenReturn(false) `when`(keyguardUpdateMonitor.userNeedsStrongAuth()).thenReturn(false) // WHEN fingerprint authenticated @@ -136,7 +137,30 @@ class AuthRippleControllerTest : SysuiTestCase() { } @Test - fun testFingerprintTrigger_KeyguardNotVisible_NoRipple() { + fun testFingerprintTrigger_Dreaming_Ripple() { + // GIVEN fp exists, keyguard is visible, user doesn't need strong auth + val fpsLocation = PointF(5f, 5f) + `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation) + controller.onViewAttached() + `when`(keyguardUpdateMonitor.isKeyguardVisible).thenReturn(false) + `when`(keyguardUpdateMonitor.isDreaming).thenReturn(true) + `when`(keyguardUpdateMonitor.userNeedsStrongAuth()).thenReturn(false) + + // WHEN fingerprint authenticated + val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java) + verify(keyguardUpdateMonitor).registerCallback(captor.capture()) + captor.value.onBiometricAuthenticated( + 0 /* userId */, + BiometricSourceType.FINGERPRINT /* type */, + false /* isStrongBiometric */) + + // THEN update sensor location and show ripple + verify(rippleView).setFingerprintSensorLocation(fpsLocation, -1f) + verify(rippleView).startUnlockedRipple(any()) + } + + @Test + fun testFingerprintTrigger_KeyguardNotVisible_NotDreaming_NoRipple() { // GIVEN fp exists & user doesn't need strong auth val fpsLocation = PointF(5f, 5f) `when`(authController.udfpsSensorLocation).thenReturn(fpsLocation) @@ -145,6 +169,7 @@ class AuthRippleControllerTest : SysuiTestCase() { // WHEN keyguard is NOT visible & fingerprint authenticated `when`(keyguardUpdateMonitor.isKeyguardVisible).thenReturn(false) + `when`(keyguardUpdateMonitor.isDreaming).thenReturn(false) val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java) verify(keyguardUpdateMonitor).registerCallback(captor.capture()) captor.value.onBiometricAuthenticated( diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt index ef82c3ec3322..fd49766dafef 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt @@ -61,6 +61,8 @@ import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit import org.mockito.Mockito.`when` as whenever +private const val REQUEST_ID = 2L + @SmallTest @RunWith(AndroidTestingRunner::class) @RunWithLooper(setAsMainLooper = true) @@ -119,7 +121,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { statusBarStateController, panelExpansionStateManager, statusBarKeyguardViewManager, keyguardUpdateMonitor, dialogManager, dumpManager, transitionController, configurationController, systemClock, keyguardStateController, - unlockedScreenOffAnimationController, sensorProps, hbmProvider, reason, + unlockedScreenOffAnimationController, sensorProps, hbmProvider, REQUEST_ID, reason, controllerCallback, onTouch, activityLaunchAnimator) block() } @@ -263,6 +265,12 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { controllerOverlay.hide() verify(udfpsView).stopIllumination() } + + @Test + fun matchesRequestIds() = withReason(REASON_AUTH_BP) { + assertThat(controllerOverlay.matchesRequestId(REQUEST_ID)).isTrue() + assertThat(controllerOverlay.matchesRequestId(REQUEST_ID + 1)).isFalse() + } } private class EnrollListener( diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index 613931f1341f..406ed5c17b0c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -27,6 +27,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.inOrder; 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; @@ -102,6 +103,7 @@ public class UdfpsControllerTest extends SysuiTestCase { // Use this for inputs going into SystemUI. Use UdfpsController.mUdfpsSensorId for things // leaving SystemUI. private static final int TEST_UDFPS_SENSOR_ID = 1; + private static final long TEST_REQUEST_ID = 70; @Rule public MockitoRule rule = MockitoJUnit.rule(); @@ -278,7 +280,7 @@ public class UdfpsControllerTest extends SysuiTestCase { @Test public void dozeTimeTick() throws RemoteException { - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); mUdfpsController.dozeTimeTick(); @@ -293,7 +295,7 @@ public class UdfpsControllerTest extends SysuiTestCase { when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController); // GIVEN that the overlay is showing - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); @@ -316,7 +318,7 @@ public class UdfpsControllerTest extends SysuiTestCase { when(mStatusBarStateController.isDozing()).thenReturn(true); // GIVEN that the overlay is showing - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); @@ -339,7 +341,7 @@ public class UdfpsControllerTest extends SysuiTestCase { when(mStatusBarStateController.isDozing()).thenReturn(false); // GIVEN that the overlay is showing - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); @@ -362,7 +364,7 @@ public class UdfpsControllerTest extends SysuiTestCase { (UdfpsAnimationViewController) mock(UdfpsEnrollViewController.class)); // GIVEN that the overlay is showing - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, BiometricOverlayConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); @@ -377,25 +379,42 @@ public class UdfpsControllerTest extends SysuiTestCase { } @Test - public void onActionMoveTouch_whenCanDismissLockScreen_entersDevice() throws RemoteException { + public void onActionMoveTouch_whenCanDismissLockScreen_entersDevice() + throws RemoteException { + onActionMoveTouch_whenCanDismissLockScreen_entersDevice(false /* stale */); + } + + @Test + public void onActionMoveTouch_whenCanDismissLockScreen_entersDevice_ignoreStale() + throws RemoteException { + onActionMoveTouch_whenCanDismissLockScreen_entersDevice(true /* stale */); + } + + public void onActionMoveTouch_whenCanDismissLockScreen_entersDevice(boolean stale) + throws RemoteException { // GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true); when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController); // GIVEN that the overlay is showing - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); // WHEN ACTION_MOVE is received verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0); + if (stale) { + mOverlayController.hideUdfpsOverlay(TEST_UDFPS_SENSOR_ID); + mFgExecutor.runAllReady(); + } mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); moveEvent.recycle(); // THEN notify keyguard authenticate to dismiss the keyguard - verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(anyBoolean()); + verify(mStatusBarKeyguardViewManager, stale ? never() : times(1)) + .notifyKeyguardAuthenticated(anyBoolean()); } @Test @@ -406,7 +425,7 @@ public class UdfpsControllerTest extends SysuiTestCase { when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController); // GIVEN that the overlay is showing - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); @@ -427,7 +446,7 @@ public class UdfpsControllerTest extends SysuiTestCase { @Test public void hideUdfpsOverlay_resetsAltAuthBouncerWhenShowing() throws RemoteException { // GIVEN overlay was showing and the udfps bouncer is showing - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); when(mStatusBarKeyguardViewManager.isShowingAlternateAuth()).thenReturn(true); @@ -441,7 +460,7 @@ public class UdfpsControllerTest extends SysuiTestCase { @Test public void testSubscribesToOrientationChangesWhenShowingOverlay() throws Exception { - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); @@ -460,7 +479,7 @@ public class UdfpsControllerTest extends SysuiTestCase { when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); // GIVEN that the overlay is showing - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); // WHEN ACTION_DOWN is received @@ -472,8 +491,9 @@ public class UdfpsControllerTest extends SysuiTestCase { mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); moveEvent.recycle(); // THEN FingerprintManager is notified about onPointerDown - verify(mFingerprintManager).onPointerDown(eq(mUdfpsController.mSensorProps.sensorId), eq(0), - eq(0), eq(0f), eq(0f)); + verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID), + eq(mUdfpsController.mSensorProps.sensorId), + eq(0), eq(0), eq(0f), eq(0f)); verify(mLatencyTracker).onActionStart(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE)); // AND illumination begins verify(mUdfpsView).startIllumination(mOnIlluminatedRunnableCaptor.capture()); @@ -481,14 +501,15 @@ public class UdfpsControllerTest extends SysuiTestCase { // AND onIlluminatedRunnable notifies FingerprintManager about onUiReady mOnIlluminatedRunnableCaptor.getValue().run(); InOrder inOrder = inOrder(mFingerprintManager, mLatencyTracker); - inOrder.verify(mFingerprintManager).onUiReady(eq(mUdfpsController.mSensorProps.sensorId)); + inOrder.verify(mFingerprintManager).onUiReady( + eq(TEST_REQUEST_ID), eq(mUdfpsController.mSensorProps.sensorId)); inOrder.verify(mLatencyTracker).onActionEnd(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE)); } @Test public void aodInterrupt() throws RemoteException { // GIVEN that the overlay is showing and screen is on and fp is running - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mScreenObserver.onScreenTurnedOn(); mFgExecutor.runAllReady(); @@ -499,14 +520,15 @@ public class UdfpsControllerTest extends SysuiTestCase { // AND onIlluminatedRunnable that notifies FingerprintManager is set verify(mUdfpsView).startIllumination(mOnIlluminatedRunnableCaptor.capture()); mOnIlluminatedRunnableCaptor.getValue().run(); - verify(mFingerprintManager).onPointerDown(eq(mUdfpsController.mSensorProps.sensorId), eq(0), - eq(0), eq(3f) /* minor */, eq(2f) /* major */); + verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID), + eq(mUdfpsController.mSensorProps.sensorId), + eq(0), eq(0), eq(3f) /* minor */, eq(2f) /* major */); } @Test public void cancelAodInterrupt() throws RemoteException { // GIVEN AOD interrupt - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mScreenObserver.onScreenTurnedOn(); mFgExecutor.runAllReady(); @@ -522,7 +544,7 @@ public class UdfpsControllerTest extends SysuiTestCase { @Test public void aodInterruptTimeout() throws RemoteException { // GIVEN AOD interrupt - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mScreenObserver.onScreenTurnedOn(); mFgExecutor.runAllReady(); @@ -539,7 +561,7 @@ public class UdfpsControllerTest extends SysuiTestCase { @Test public void aodInterruptScreenOff() throws RemoteException { // GIVEN screen off - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mScreenObserver.onScreenTurnedOff(); mFgExecutor.runAllReady(); @@ -555,7 +577,7 @@ public class UdfpsControllerTest extends SysuiTestCase { @Test public void aodInterrupt_fingerprintNotRunning() throws RemoteException { // GIVEN showing overlay - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mScreenObserver.onScreenTurnedOn(); @@ -577,7 +599,7 @@ public class UdfpsControllerTest extends SysuiTestCase { // GIVEN that the overlay is showing and a11y touch exploration enabled when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true); - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); @@ -612,7 +634,7 @@ public class UdfpsControllerTest extends SysuiTestCase { // GIVEN that the overlay is showing and a11y touch exploration NOT enabled when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false); - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java index a32ff801e824..a6921b441f17 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java @@ -53,6 +53,8 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.concurrent.Executor; + @SmallTest @RunWith(AndroidTestingRunner.class) public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { @@ -90,6 +92,8 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { @Mock ZenModeController mZenModeController; + private final Executor mMainExecutor = Runnable::run; + DreamOverlayStatusBarViewController mController; @Before @@ -102,6 +106,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { mController = new DreamOverlayStatusBarViewController( mView, mResources, + mMainExecutor, mConnectivityManager, mTouchSession, mAlarmManager, @@ -134,7 +139,8 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { .thenReturn(false); when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities); mController.onViewAttached(); - verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, true); + verify(mView).showIcon( + DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, true, null); } @Test @@ -143,13 +149,16 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { .thenReturn(true); when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities); mController.onViewAttached(); - verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, false); + verify(mView).showIcon( + DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, false, null); } @Test public void testOnViewAttachedShowsWifiIconWhenNetworkCapabilitiesUnavailable() { when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(null); mController.onViewAttached(); + verify(mView).showIcon( + DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, true, null); } @Test @@ -176,7 +185,8 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA)) .thenReturn(true); mController.onViewAttached(); - verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true); + verify(mView).showIcon( + DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true, null); } @Test @@ -186,7 +196,8 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA)) .thenReturn(false); mController.onViewAttached(); - verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, false); + verify(mView).showIcon( + DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, false, null); } @Test @@ -211,7 +222,8 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { when(mZenModeController.getZen()).thenReturn( Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS); mController.onViewAttached(); - verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, true); + verify(mView).showIcon( + DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, true, null); } @Test @@ -219,7 +231,8 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { when(mZenModeController.getZen()).thenReturn( Settings.Global.ZEN_MODE_OFF); mController.onViewAttached(); - verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, false); + verify(mView).showIcon( + DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, false, null); } @Test @@ -250,7 +263,9 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class); verify(mConnectivityManager).registerNetworkCallback(any(), callbackCapture.capture()); callbackCapture.getValue().onAvailable(mNetwork); - verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, false); + + verify(mView).showIcon( + DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, false, null); } @Test @@ -266,7 +281,9 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class); verify(mConnectivityManager).registerNetworkCallback(any(), callbackCapture.capture()); callbackCapture.getValue().onLost(mNetwork); - verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, true); + + verify(mView).showIcon( + DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, true, null); } @Test @@ -283,7 +300,9 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) .thenReturn(true); callbackCapture.getValue().onCapabilitiesChanged(mNetwork, mNetworkCapabilities); - verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, false); + + verify(mView).showIcon( + DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, false, null); } @Test @@ -333,7 +352,8 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { callbackCapture.getValue().onSensorBlockedChanged( SensorPrivacyManager.Sensors.MICROPHONE, true); - verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true); + verify(mView).showIcon( + DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true, null); } @Test @@ -350,7 +370,8 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { callbackCapture.getValue().onSensorBlockedChanged( SensorPrivacyManager.Sensors.MICROPHONE, false); - verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, false); + verify(mView).showIcon( + DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, false, null); } @Test @@ -364,7 +385,8 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { verify(mZenModeController).addCallback(callbackCapture.capture()); callbackCapture.getValue().onZenChanged(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS); - verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, true); + verify(mView).showIcon( + DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, true, null); } @Test @@ -373,12 +395,12 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { .thenReturn(Settings.Global.ZEN_MODE_OFF); mController.onViewAttached(); - final ArgumentCaptor<ZenModeController.Callback> callbackCapture = ArgumentCaptor.forClass(ZenModeController.Callback.class); verify(mZenModeController).addCallback(callbackCapture.capture()); callbackCapture.getValue().onZenChanged(Settings.Global.ZEN_MODE_OFF); - verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, false); + verify(mView).showIcon( + DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, false, null); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockDateComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockDateComplicationTest.java index b02c506be8be..86aa14d7a877 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockDateComplicationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockDateComplicationTest.java @@ -15,11 +15,16 @@ */ package com.android.systemui.dreams.complication; +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.content.Context; import android.testing.AndroidTestingRunner; +import android.view.View; import androidx.test.filters.SmallTest; @@ -32,6 +37,8 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import javax.inject.Provider; + @SmallTest @RunWith(AndroidTestingRunner.class) public class DreamClockDateComplicationTest extends SysuiTestCase { @@ -45,9 +52,28 @@ public class DreamClockDateComplicationTest extends SysuiTestCase { @Mock private DreamClockDateComplication mComplication; + @Mock + private Provider<DreamClockDateComplication.DreamClockDateViewHolder> + mDreamClockDateViewHolderProvider; + + @Mock + private DreamClockDateComplication.DreamClockDateViewHolder + mDreamClockDateViewHolder; + + @Mock + private ComplicationViewModel mComplicationViewModel; + + @Mock + private View mView; + + @Mock + private ComplicationLayoutParams mLayoutParams; + @Before public void setup() { MockitoAnnotations.initMocks(this); + when(mDreamClockDateViewHolderProvider.get()).thenReturn(mDreamClockDateViewHolder); + } /** @@ -63,4 +89,40 @@ public class DreamClockDateComplicationTest extends SysuiTestCase { registrant.start(); verify(mDreamOverlayStateController).addComplication(eq(mComplication)); } + + /** + * Verifies {@link DreamClockDateComplication} has the required type. + */ + @Test + public void testComplicationRequiredTypeAvailability() { + final DreamClockDateComplication complication = + new DreamClockDateComplication(mDreamClockDateViewHolderProvider); + assertEquals(Complication.COMPLICATION_TYPE_DATE, + complication.getRequiredTypeAvailability()); + } + + /** + * Verifies {@link DreamClockDateComplication.DreamClockDateViewHolder} is obtainable from its + * provider when the complication creates view. + */ + @Test + public void testComplicationViewHolderProviderOnCreateView() { + final DreamClockDateComplication complication = + new DreamClockDateComplication(mDreamClockDateViewHolderProvider); + final Complication.ViewHolder viewHolder = complication.createView(mComplicationViewModel); + verify(mDreamClockDateViewHolderProvider).get(); + assertThat(viewHolder).isEqualTo(mDreamClockDateViewHolder); + } + + /** + * Verifies {@link DreamClockDateComplication.DreamClockDateViewHolder} has the intended view + * and layout parameters from constructor. + */ + @Test + public void testComplicationViewHolderContentAccessors() { + final DreamClockDateComplication.DreamClockDateViewHolder viewHolder = + new DreamClockDateComplication.DreamClockDateViewHolder(mView, mLayoutParams); + assertThat(viewHolder.getView()).isEqualTo(mView); + assertThat(viewHolder.getLayoutParams()).isEqualTo(mLayoutParams); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java index 088b4d5136ff..314a30b2d14a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java @@ -15,11 +15,16 @@ */ package com.android.systemui.dreams.complication; +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.content.Context; import android.testing.AndroidTestingRunner; +import android.view.View; import androidx.test.filters.SmallTest; @@ -32,6 +37,8 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import javax.inject.Provider; + @SmallTest @RunWith(AndroidTestingRunner.class) public class DreamClockTimeComplicationTest extends SysuiTestCase { @@ -45,9 +52,27 @@ public class DreamClockTimeComplicationTest extends SysuiTestCase { @Mock private DreamClockTimeComplication mComplication; + @Mock + private Provider<DreamClockTimeComplication.DreamClockTimeViewHolder> + mDreamClockTimeViewHolderProvider; + + @Mock + private DreamClockTimeComplication.DreamClockTimeViewHolder + mDreamClockTimeViewHolder; + + @Mock + private ComplicationViewModel mComplicationViewModel; + + @Mock + private View mView; + + @Mock + private ComplicationLayoutParams mLayoutParams; + @Before public void setup() { MockitoAnnotations.initMocks(this); + when(mDreamClockTimeViewHolderProvider.get()).thenReturn(mDreamClockTimeViewHolder); } /** @@ -63,4 +88,40 @@ public class DreamClockTimeComplicationTest extends SysuiTestCase { registrant.start(); verify(mDreamOverlayStateController).addComplication(eq(mComplication)); } + + /** + * Verifies {@link DreamClockTimeComplication} has the required type. + */ + @Test + public void testComplicationRequiredTypeAvailability() { + final DreamClockTimeComplication complication = + new DreamClockTimeComplication(mDreamClockTimeViewHolderProvider); + assertEquals(Complication.COMPLICATION_TYPE_TIME, + complication.getRequiredTypeAvailability()); + } + + /** + * Verifies {@link DreamClockTimeComplication.DreamClockTimeViewHolder} is obtainable from its + * provider when the complication creates view. + */ + @Test + public void testComplicationViewHolderProviderOnCreateView() { + final DreamClockTimeComplication complication = + new DreamClockTimeComplication(mDreamClockTimeViewHolderProvider); + final Complication.ViewHolder viewHolder = complication.createView(mComplicationViewModel); + verify(mDreamClockTimeViewHolderProvider).get(); + assertThat(viewHolder).isEqualTo(mDreamClockTimeViewHolder); + } + + /** + * Verifies {@link DreamClockTimeComplication.DreamClockTimeViewHolder} has the intended view + * and layout parameters from constructor. + */ + @Test + public void testComplicationViewHolderContentAccessors() { + final DreamClockTimeComplication.DreamClockTimeViewHolder viewHolder = + new DreamClockTimeComplication.DreamClockTimeViewHolder(mView, mLayoutParams); + assertThat(viewHolder.getView()).isEqualTo(mView); + assertThat(viewHolder.getLayoutParams()).isEqualTo(mLayoutParams); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt index dcbe0ab96dac..daf81bdc6e82 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt @@ -37,7 +37,6 @@ import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.MockitoAnnotations import javax.inject.Provider -import org.mockito.Mockito.`when` as whenever private val DATA = MediaData( userId = -1, @@ -83,7 +82,6 @@ class MediaCarouselControllerTest : SysuiTestCase() { @Before fun setup() { MockitoAnnotations.initMocks(this) - whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true) mediaCarouselController = MediaCarouselController( context, mediaControlPanelFactory, diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt index cb68d81287df..90eff1ae9804 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt @@ -101,7 +101,6 @@ public class MediaControlPanelTest : SysuiTestCase() { @Mock private lateinit var mediaOutputDialogFactory: MediaOutputDialogFactory @Mock private lateinit var mediaCarouselController: MediaCarouselController @Mock private lateinit var falsingManager: FalsingManager - @Mock private lateinit var mediaFlags: MediaFlags private lateinit var appIcon: ImageView private lateinit var albumView: ImageView private lateinit var titleText: TextView @@ -147,7 +146,7 @@ public class MediaControlPanelTest : SysuiTestCase() { player = MediaControlPanel(context, bgExecutor, activityStarter, broadcastSender, mediaViewController, seekBarViewModel, Lazy { mediaDataManager }, - mediaOutputDialogFactory, mediaCarouselController, falsingManager, mediaFlags, clock) + mediaOutputDialogFactory, mediaCarouselController, falsingManager, clock) whenever(seekBarViewModel.progress).thenReturn(seekBarData) // Set up mock views for the players @@ -215,9 +214,6 @@ public class MediaControlPanelTest : SysuiTestCase() { device = device, active = true, resumeAction = null) - - whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(false) - whenever(mediaFlags.useMediaSessionLayout()).thenReturn(false) } /** @@ -295,9 +291,6 @@ public class MediaControlPanelTest : SysuiTestCase() { @Test fun bindSemanticActionsOldLayout() { - whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true) - whenever(mediaFlags.useMediaSessionLayout()).thenReturn(false) - val icon = Icon.createWithResource(context, android.R.drawable.ic_media_play) val semanticActions = MediaButton( playOrPause = MediaAction(icon, Runnable {}, "play"), @@ -332,9 +325,6 @@ public class MediaControlPanelTest : SysuiTestCase() { @Test fun bindSemanticActionsNewLayout() { - whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true) - whenever(mediaFlags.useMediaSessionLayout()).thenReturn(true) - val icon = Icon.createWithResource(context, android.R.drawable.ic_media_play) val semanticActions = MediaButton( playOrPause = MediaAction(icon, Runnable {}, "play"), @@ -381,9 +371,6 @@ public class MediaControlPanelTest : SysuiTestCase() { @Test fun bindNotificationActionsNewLayout() { - whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true) - whenever(mediaFlags.useMediaSessionLayout()).thenReturn(true) - val icon = Icon.createWithResource(context, android.R.drawable.ic_media_play) val actions = listOf( MediaAction(icon, Runnable {}, "previous"), diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt index 925ae30e8773..066f49a16f19 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt @@ -26,6 +26,7 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.statusbar.SbnBuilder import com.android.systemui.tuner.TunerService import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.eq import com.android.systemui.util.time.FakeSystemClock @@ -167,7 +168,7 @@ class MediaDataManagerTest : SysuiTestCase() { whenever(mediaSmartspaceTarget.featureType).thenReturn(SmartspaceTarget.FEATURE_MEDIA) whenever(mediaSmartspaceTarget.iconGrid).thenReturn(listOf(mediaRecommendationItem)) whenever(mediaSmartspaceTarget.creationTimeMillis).thenReturn(1234L) - whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(false) + whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(false) } @After @@ -594,7 +595,7 @@ class MediaDataManagerTest : SysuiTestCase() { @Test fun testPlaybackActions_noState_usesNotification() { val desc = "Notification Action" - whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true) + whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) whenever(controller.playbackState).thenReturn(null) val notifWithAction = SbnBuilder().run { @@ -621,7 +622,7 @@ class MediaDataManagerTest : SysuiTestCase() { @Test fun testPlaybackActions_hasPrevNext() { val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4") - whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true) + whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) val stateActions = PlaybackState.ACTION_PLAY or PlaybackState.ACTION_SKIP_TO_PREVIOUS or PlaybackState.ACTION_SKIP_TO_NEXT @@ -669,7 +670,7 @@ class MediaDataManagerTest : SysuiTestCase() { @Test fun testPlaybackActions_noPrevNext_usesCustom() { val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4", "custom 5") - whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true) + whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) val stateActions = PlaybackState.ACTION_PLAY val stateBuilder = PlaybackState.Builder() .setActions(stateActions) @@ -707,7 +708,7 @@ class MediaDataManagerTest : SysuiTestCase() { @Test fun testPlaybackActions_reservedSpace() { val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4") - whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true) + whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) val stateActions = PlaybackState.ACTION_PLAY val stateBuilder = PlaybackState.Builder() .setActions(stateActions) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt index 8e201b5a3e87..203eb47165e0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt @@ -223,6 +223,18 @@ class MediaHierarchyManagerTest : SysuiTestCase() { } @Test + fun calculateTransformationType_onLockSplitShade_goingToFullShade_mediaInvisible_returnsFade() { + enableSplitShade() + goToLockscreen() + expandQS() + whenever(lockHost.visible).thenReturn(false) + mediaHiearchyManager.setTransitionToFullShadeAmount(10000f) + + val transformType = mediaHiearchyManager.calculateTransformationType() + assertThat(transformType).isEqualTo(MediaHierarchyManager.TRANSFORMATION_TYPE_FADE) + } + + @Test fun calculateTransformationType_onLockShade_inSplitShade_notExpanding_returnsFade() { enableSplitShade() goToLockscreen() diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt index 962d78c129f9..b9a69bb8641a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt @@ -192,9 +192,9 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { verify(windowManager, never()).removeView(any()) } - + @Test - fun displayChip_nullAppIconDrawableAndNullPackageName_stillHasIcon() { + fun setIcon_nullAppIconDrawableAndNullPackageName_stillHasIcon() { controllerCommon.displayChip(getState()) val chipView = getChipView() @@ -204,7 +204,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { } @Test - fun displayChip_nullAppIconDrawableAndInvalidPackageName_stillHasIcon() { + fun setIcon_nullAppIconDrawableAndInvalidPackageName_stillHasIcon() { controllerCommon.displayChip(getState()) val chipView = getChipView() @@ -226,7 +226,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { } @Test - fun displayChip_hasAppIconDrawable_iconIsDrawable() { + fun setIcon_hasAppIconDrawable_iconIsDrawable() { controllerCommon.displayChip(getState()) val chipView = getChipView() @@ -237,7 +237,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { } @Test - fun displayChip_nullAppNameAndNullPackageName_stillHasContentDescription() { + fun setIcon_nullAppNameAndNullPackageName_stillHasContentDescription() { controllerCommon.displayChip(getState()) val chipView = getChipView() @@ -247,7 +247,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { } @Test - fun displayChip_nullAppNameAndInvalidPackageName_stillHasContentDescription() { + fun setIcon_nullAppNameAndInvalidPackageName_stillHasContentDescription() { controllerCommon.displayChip(getState()) val chipView = getChipView() @@ -259,7 +259,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { } @Test - fun displayChip_nullAppName_iconContentDescriptionIsFromPackageName() { + fun setIcon_nullAppName_iconContentDescriptionIsFromPackageName() { controllerCommon.displayChip(getState()) val chipView = getChipView() @@ -269,7 +269,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { } @Test - fun displayChip_hasAppName_iconContentDescriptionIsAppNameOverride() { + fun setIcon_hasAppName_iconContentDescriptionIsAppNameOverride() { controllerCommon.displayChip(getState()) val chipView = getChipView() @@ -280,6 +280,21 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { } @Test + fun setIcon_iconSizeMatchesGetIconSize() { + controllerCommon.displayChip(getState()) + val chipView = getChipView() + + controllerCommon.setIcon(chipView, PACKAGE_NAME) + chipView.measure( + View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), + View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) + ) + + assertThat(chipView.getAppIconView().measuredWidth).isEqualTo(ICON_SIZE) + assertThat(chipView.getAppIconView().measuredHeight).isEqualTo(ICON_SIZE) + } + + @Test fun tapGestureDetected_outsideViewBounds_viewHidden() { controllerCommon.displayChip(getState()) whenever(viewUtil.touchIsWithinView(any(), any(), any())).thenReturn(false) @@ -344,6 +359,8 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { override fun updateChipView(chipInfo: ChipInfo, currentChipView: ViewGroup) { } + + override fun getIconSize(isAppIcon: Boolean): Int? = ICON_SIZE } inner class ChipInfo : ChipInfoCommon { @@ -354,3 +371,4 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { private const val PACKAGE_NAME = "com.android.systemui" private const val APP_NAME = "Fake App Name" private const val TIMEOUT_MS = 10000L +private const val ICON_SIZE = 47
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt index 355d3fe4b8d1..067607f9b8ae 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt @@ -174,12 +174,47 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() { verify(logger).logStateChange(any(), any()) } + @Test + fun setIcon_isAppIcon_usesAppIconSize() { + controllerReceiver.displayChip(getChipReceiverInfo()) + val chipView = getChipView() + + controllerReceiver.setIcon(chipView, PACKAGE_NAME) + chipView.measure( + View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), + View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) + ) + + val expectedSize = controllerReceiver.getIconSize(isAppIcon = true) + assertThat(chipView.getAppIconView().measuredWidth).isEqualTo(expectedSize) + assertThat(chipView.getAppIconView().measuredHeight).isEqualTo(expectedSize) + } + + @Test + fun setIcon_notAppIcon_usesGenericIconSize() { + controllerReceiver.displayChip(getChipReceiverInfo()) + val chipView = getChipView() + + controllerReceiver.setIcon(chipView, appPackageName = null) + chipView.measure( + View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), + View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) + ) + + val expectedSize = controllerReceiver.getIconSize(isAppIcon = false) + assertThat(chipView.getAppIconView().measuredWidth).isEqualTo(expectedSize) + assertThat(chipView.getAppIconView().measuredHeight).isEqualTo(expectedSize) + } + private fun getChipView(): ViewGroup { val viewCaptor = ArgumentCaptor.forClass(View::class.java) verify(windowManager).addView(viewCaptor.capture(), any()) return viewCaptor.value as ViewGroup } + private fun getChipReceiverInfo(): ChipReceiverInfo = + ChipReceiverInfo(routeInfo, null, null) + private fun ViewGroup.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt index 350822691121..38b448fb362c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt @@ -19,8 +19,11 @@ package com.android.systemui.privacy import android.app.ActivityManager import android.content.Context import android.content.Intent +import android.content.pm.ActivityInfo import android.content.pm.ApplicationInfo import android.content.pm.PackageManager +import android.content.pm.PackageManager.ResolveInfoFlags +import android.content.pm.ResolveInfo import android.content.pm.UserInfo import android.os.Process.SYSTEM_UID import android.os.UserHandle @@ -648,6 +651,77 @@ class PrivacyDialogControllerTest : SysuiTestCase() { } } + @Test + fun testCorrectIntentSubAttribution() { + val usage = createMockPermGroupUsage( + attributionTag = TEST_ATTRIBUTION_TAG, + attributionLabel = "TEST_LABEL" + ) + + val activityInfo = createMockActivityInfo() + val resolveInfo = createMockResolveInfo(activityInfo) + `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage)) + `when`(packageManager.resolveActivity(any(), any<ResolveInfoFlags>())) + .thenAnswer { resolveInfo } + controller.showDialog(context) + exhaustExecutors() + + dialogProvider.list?.let { list -> + val navigationIntent = list.get(0).navigationIntent!! + assertThat(navigationIntent.action).isEqualTo(Intent.ACTION_MANAGE_PERMISSION_USAGE) + assertThat(navigationIntent.getStringExtra(Intent.EXTRA_PERMISSION_GROUP_NAME)) + .isEqualTo(PERM_CAMERA) + assertThat(navigationIntent.getStringArrayExtra(Intent.EXTRA_ATTRIBUTION_TAGS)) + .isEqualTo(arrayOf(TEST_ATTRIBUTION_TAG.toString())) + assertThat(navigationIntent.getBooleanExtra(Intent.EXTRA_SHOWING_ATTRIBUTION, false)) + .isTrue() + } + } + + @Test + fun testDefaultIntentOnMissingAttributionLabel() { + val usage = createMockPermGroupUsage( + attributionTag = TEST_ATTRIBUTION_TAG + ) + + val activityInfo = createMockActivityInfo() + val resolveInfo = createMockResolveInfo(activityInfo) + `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage)) + `when`(packageManager.resolveActivity(any(), any<ResolveInfoFlags>())) + .thenAnswer { resolveInfo } + controller.showDialog(context) + exhaustExecutors() + + dialogProvider.list?.let { list -> + assertThat(isIntentEqual(list.get(0).navigationIntent!!, + controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID))) + .isTrue() + } + } + + @Test + fun testDefaultIntentOnIncorrectPermission() { + val usage = createMockPermGroupUsage( + attributionTag = TEST_ATTRIBUTION_TAG + ) + + val activityInfo = createMockActivityInfo( + permission = "INCORRECT_PERMISSION" + ) + val resolveInfo = createMockResolveInfo(activityInfo) + `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage)) + `when`(packageManager.resolveActivity(any(), any<ResolveInfoFlags>())) + .thenAnswer { resolveInfo } + controller.showDialog(context) + exhaustExecutors() + + dialogProvider.list?.let { list -> + assertThat(isIntentEqual(list.get(0).navigationIntent!!, + controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID))) + .isTrue() + } + } + private fun exhaustExecutors() { FakeExecutor.exhaustExecutors(backgroundExecutor, uiExecutor) } @@ -680,6 +754,24 @@ class PrivacyDialogControllerTest : SysuiTestCase() { return user * UserHandle.PER_USER_RANGE + nextUid++ } + private fun createMockResolveInfo( + activityInfo: ActivityInfo? = null + ): ResolveInfo { + val resolveInfo = mock(ResolveInfo::class.java) + resolveInfo.activityInfo = activityInfo + return resolveInfo + } + + private fun createMockActivityInfo( + permission: String = android.Manifest.permission.START_VIEW_PERMISSION_USAGE, + className: String = "TEST_CLASS_NAME" + ): ActivityInfo { + val activityInfo = mock(ActivityInfo::class.java) + activityInfo.permission = permission + activityInfo.name = className + return activityInfo + } + private fun createMockPermGroupUsage( packageName: String = TEST_PACKAGE_NAME, uid: Int = generateUidForUser(USER_ID), diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java index d3bb241baad4..f306fd601136 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java @@ -4,14 +4,19 @@ import static com.android.systemui.qs.tiles.dialog.InternetDialogController.MAX_ import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.content.Context; import android.graphics.drawable.Drawable; import android.testing.AndroidTestingRunner; import android.testing.TestableResources; @@ -30,6 +35,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.Spy; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -40,6 +46,7 @@ import java.util.List; @RunWith(AndroidTestingRunner.class) public class InternetAdapterTest extends SysuiTestCase { + private static final String WIFI_KEY = "Wi-Fi_Key"; private static final String WIFI_TITLE = "Wi-Fi Title"; private static final String WIFI_SUMMARY = "Wi-Fi Summary"; private static final int GEAR_ICON_RES_ID = R.drawable.ic_settings_24dp; @@ -47,6 +54,8 @@ public class InternetAdapterTest extends SysuiTestCase { @Rule public MockitoRule mRule = MockitoJUnit.rule(); + @Spy + private Context mSpyContext = mContext; @Mock private WifiEntry mInternetWifiEntry; @@ -74,6 +83,7 @@ public class InternetAdapterTest extends SysuiTestCase { when(mInternetWifiEntry.getSummary(false)).thenReturn(WIFI_SUMMARY); when(mInternetWifiEntry.isDefaultNetwork()).thenReturn(true); when(mInternetWifiEntry.hasInternetAccess()).thenReturn(true); + when(mWifiEntry.getKey()).thenReturn(WIFI_KEY); when(mWifiEntry.getTitle()).thenReturn(WIFI_TITLE); when(mWifiEntry.getSummary(false)).thenReturn(WIFI_SUMMARY); @@ -197,6 +207,66 @@ public class InternetAdapterTest extends SysuiTestCase { } @Test + public void viewHolderShouldEnabled_wifiCanConnect_returnTrue() { + when(mWifiEntry.canConnect()).thenReturn(true); + + assertThat(mViewHolder.shouldEnabled(mWifiEntry)).isTrue(); + } + + @Test + public void viewHolderShouldEnabled_wifiCanNotConnect_returnFalse() { + when(mWifiEntry.canConnect()).thenReturn(false); + + assertThat(mViewHolder.shouldEnabled(mWifiEntry)).isFalse(); + } + + @Test + public void viewHolderShouldEnabled_wifiCanNotConnectButCanDisconnect_returnTrue() { + when(mWifiEntry.canConnect()).thenReturn(false); + when(mWifiEntry.canConnect()).thenReturn(true); + + assertThat(mViewHolder.shouldEnabled(mWifiEntry)).isTrue(); + } + + @Test + public void viewHolderShouldEnabled_wifiCanNotConnectButIsSaved_returnTrue() { + when(mWifiEntry.canConnect()).thenReturn(false); + when(mWifiEntry.isSaved()).thenReturn(true); + + assertThat(mViewHolder.shouldEnabled(mWifiEntry)).isTrue(); + } + + @Test + public void viewHolderOnWifiClick_wifiShouldEditBeforeConnect_startActivity() { + when(mWifiEntry.shouldEditBeforeConnect()).thenReturn(true); + mViewHolder = mInternetAdapter.onCreateViewHolder(new LinearLayout(mSpyContext), 0); + doNothing().when(mSpyContext).startActivity(any()); + + mViewHolder.onWifiClick(mWifiEntry, mock(View.class)); + + verify(mSpyContext).startActivity(any()); + } + + @Test + public void viewHolderOnWifiClick_wifiCanConnect_connectWifi() { + when(mWifiEntry.canConnect()).thenReturn(true); + + mViewHolder.onWifiClick(mWifiEntry, mock(View.class)); + + verify(mInternetDialogController).connect(mWifiEntry); + } + + @Test + public void viewHolderOnWifiClick_wifiCanNotConnectButIsSaved_launchWifiDetailsSetting() { + when(mWifiEntry.canConnect()).thenReturn(false); + when(mWifiEntry.isSaved()).thenReturn(true); + + mViewHolder.onWifiClick(mWifiEntry, mock(View.class)); + + verify(mInternetDialogController).launchWifiDetailsSetting(anyString(), any()); + } + + @Test public void viewHolderUpdateEndIcon_wifiConnected_updateGearIcon() { mTestableResources.addOverride(GEAR_ICON_RES_ID, mGearIcon); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java index a2959e2fb917..633a9c3a03d8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java @@ -385,18 +385,16 @@ public class InternetDialogControllerTest extends SysuiTestCase { } @Test - public void launchWifiNetworkDetailsSetting_withNoWifiEntryKey_doNothing() { - mInternetDialogController.launchWifiNetworkDetailsSetting(null /* key */, - mDialogLaunchView); + public void launchWifiDetailsSetting_withNoWifiEntryKey_doNothing() { + mInternetDialogController.launchWifiDetailsSetting(null /* key */, mDialogLaunchView); verify(mActivityStarter, never()) .postStartActivityDismissingKeyguard(any(Intent.class), anyInt()); } @Test - public void launchWifiNetworkDetailsSetting_withWifiEntryKey_startActivity() { - mInternetDialogController.launchWifiNetworkDetailsSetting("wifi_entry_key", - mDialogLaunchView); + public void launchWifiDetailsSetting_withWifiEntryKey_startActivity() { + mInternetDialogController.launchWifiDetailsSetting("wifi_entry_key", mDialogLaunchView); verify(mActivityStarter).postStartActivityDismissingKeyguard(any(Intent.class), anyInt(), any()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt index 64a0a2342a44..1d2a0ca3777a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt @@ -1,5 +1,6 @@ package com.android.systemui.statusbar +import org.mockito.Mockito.`when` as whenever import android.test.suitebuilder.annotation.SmallTest import android.testing.AndroidTestingRunner import android.testing.TestableLooper @@ -18,11 +19,11 @@ import com.android.systemui.statusbar.notification.row.NotificationTestHelper import com.android.systemui.statusbar.notification.stack.AmbientState import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController +import com.android.systemui.statusbar.phone.CentralSurfaces import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.phone.LSShadeTransitionLogger import com.android.systemui.statusbar.phone.NotificationPanelViewController import com.android.systemui.statusbar.phone.ScrimController -import com.android.systemui.statusbar.phone.CentralSurfaces import com.android.systemui.statusbar.policy.FakeConfigurationController import org.junit.After import org.junit.Assert.assertFalse @@ -37,14 +38,13 @@ import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.ArgumentMatchers.anyFloat import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.anyLong +import org.mockito.ArgumentMatchers.eq import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit -import org.mockito.Mockito.`when` as whenever -import org.mockito.ArgumentMatchers.eq private fun <T> anyObject(): T { return Mockito.anyObject<T>() @@ -231,7 +231,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { transitionController.dragDownAmount = 10f verify(nsslController, never()).setTransitionToFullShadeAmount(anyFloat(), anyFloat()) verify(mediaHierarchyManager, never()).setTransitionToFullShadeAmount(anyFloat()) - verify(scrimController, never()).setTransitionToFullShadeProgress(anyFloat()) + verify(scrimController, never()).setTransitionToFullShadeProgress(anyFloat(), anyFloat()) verify(notificationPanelController, never()).setTransitionToFullShadeAmount(anyFloat(), anyBoolean(), anyLong()) verify(qS, never()).setTransitionToFullShadeAmount(anyFloat(), anyFloat()) @@ -242,7 +242,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { transitionController.dragDownAmount = 10f verify(nsslController).setTransitionToFullShadeAmount(anyFloat(), anyFloat()) verify(mediaHierarchyManager).setTransitionToFullShadeAmount(anyFloat()) - verify(scrimController).setTransitionToFullShadeProgress(anyFloat()) + verify(scrimController).setTransitionToFullShadeProgress(anyFloat(), anyFloat()) verify(notificationPanelController).setTransitionToFullShadeAmount(anyFloat(), anyBoolean(), anyLong()) verify(qS).setTransitionToFullShadeAmount(anyFloat(), anyFloat()) @@ -311,6 +311,75 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { } @Test + fun setDragAmount_setsScrimProgressBasedOnScrimDistance() { + val distance = 10 + context.orCreateTestableResources + .addOverride(R.dimen.lockscreen_shade_scrim_transition_distance, distance) + configurationController.notifyConfigurationChanged() + + transitionController.dragDownAmount = 5f + + verify(scrimController).transitionToFullShadeProgress( + progress = eq(0.5f), + lockScreenNotificationsProgress = anyFloat() + ) + } + + @Test + fun setDragAmount_setsNotificationsScrimProgressBasedOnNotificationsScrimDistanceAndDelay() { + val distance = 100 + val delay = 10 + context.orCreateTestableResources.addOverride( + R.dimen.lockscreen_shade_notifications_scrim_transition_distance, distance) + context.orCreateTestableResources.addOverride( + R.dimen.lockscreen_shade_notifications_scrim_transition_delay, delay) + configurationController.notifyConfigurationChanged() + + transitionController.dragDownAmount = 20f + + verify(scrimController).transitionToFullShadeProgress( + progress = anyFloat(), + lockScreenNotificationsProgress = eq(0.1f) + ) + } + + @Test + fun setDragAmount_dragAmountLessThanNotifDelayDistance_setsNotificationsScrimProgressToZero() { + val distance = 100 + val delay = 50 + context.orCreateTestableResources.addOverride( + R.dimen.lockscreen_shade_notifications_scrim_transition_distance, distance) + context.orCreateTestableResources.addOverride( + R.dimen.lockscreen_shade_notifications_scrim_transition_delay, delay) + configurationController.notifyConfigurationChanged() + + transitionController.dragDownAmount = 20f + + verify(scrimController).transitionToFullShadeProgress( + progress = anyFloat(), + lockScreenNotificationsProgress = eq(0f) + ) + } + + @Test + fun setDragAmount_dragAmountMoreThanTotalDistance_setsNotificationsScrimProgressToOne() { + val distance = 100 + val delay = 50 + context.orCreateTestableResources.addOverride( + R.dimen.lockscreen_shade_notifications_scrim_transition_distance, distance) + context.orCreateTestableResources.addOverride( + R.dimen.lockscreen_shade_notifications_scrim_transition_delay, delay) + configurationController.notifyConfigurationChanged() + + transitionController.dragDownAmount = 999999f + + verify(scrimController).transitionToFullShadeProgress( + progress = anyFloat(), + lockScreenNotificationsProgress = eq(1f) + ) + } + + @Test fun setDragDownAmount_inSplitShade_setsValueOnMediaHierarchyManager() { enableSplitShade() @@ -328,9 +397,21 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { } private fun setSplitShadeEnabled(enabled: Boolean) { - context.getOrCreateTestableResources().addOverride( - R.bool.config_use_split_notification_shade, enabled - ) + overrideResource(R.bool.config_use_split_notification_shade, enabled) configurationController.notifyConfigurationChanged() } + + /** + * Wrapper around [ScrimController.transitionToFullShadeProgress] that has named parameters for + * clarify and easier refactoring of parameter names. + */ + private fun ScrimController.transitionToFullShadeProgress( + progress: Float, + lockScreenNotificationsProgress: Float + ) { + scrimController.setTransitionToFullShadeProgress( + progress, + lockScreenNotificationsProgress + ) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java index 7fc5ece670d4..251ac7d250fe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java @@ -41,6 +41,7 @@ import android.testing.TestableLooper; import androidx.asynclayoutinflater.view.AsyncLayoutInflater; import androidx.test.filters.SmallTest; +import com.android.internal.logging.MetricsLogger; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.NotificationMessagingUtil; import com.android.systemui.R; @@ -87,6 +88,7 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.InflatedSmartReplyState; import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder; +import com.android.systemui.statusbar.policy.SmartReplyConstants; import com.android.systemui.statusbar.policy.SmartReplyStateInflater; import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent; import com.android.systemui.util.concurrency.FakeExecutor; @@ -252,13 +254,17 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { .thenAnswer((Answer<ExpandableNotificationRowController>) invocation -> new ExpandableNotificationRowController( viewCaptor.getValue(), - mListContainer, - mock(RemoteInputViewSubcomponent.Factory.class), mock(ActivatableNotificationViewController.class), + mock(RemoteInputViewSubcomponent.Factory.class), + mock(MetricsLogger.class), + mListContainer, mNotificationMediaManager, + mock(SmartReplyConstants.class), + mock(SmartReplyController.class), mock(PluginManager.class), new FakeSystemClock(), - "FOOBAR", "FOOBAR", + "FOOBAR", + "FOOBAR", mKeyguardBypassController, mGroupMembershipManager, mGroupExpansionManager, @@ -275,8 +281,7 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { mock(FeatureFlags.class), mPeopleNotificationIdentifier, Optional.of(mock(BubblesManager.class)), - mock(ExpandableNotificationRowDragController.class) - )); + mock(ExpandableNotificationRowDragController.class))); when(mNotificationRowComponentBuilder.activatableNotificationView(any())) .thenReturn(mNotificationRowComponentBuilder); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index 72f8f70058fc..1ecb09bc8514 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -44,6 +44,7 @@ import android.text.TextUtils; import android.view.LayoutInflater; import android.widget.RemoteViews; +import com.android.internal.logging.MetricsLogger; import com.android.systemui.TestableDependency; import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.FalsingManagerFake; @@ -54,6 +55,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeWindowController; +import com.android.systemui.statusbar.SmartReplyController; import com.android.systemui.statusbar.notification.ConversationNotificationProcessor; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; @@ -73,6 +75,7 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.policy.HeadsUpManagerLogger; import com.android.systemui.statusbar.policy.InflatedSmartReplyState; import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder; +import com.android.systemui.statusbar.policy.SmartReplyConstants; import com.android.systemui.statusbar.policy.SmartReplyStateInflater; import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent; import com.android.systemui.tests.R; @@ -505,7 +508,10 @@ public class NotificationTestHelper { mPeopleNotificationIdentifier, mOnUserInteractionCallback, Optional.of(mock(BubblesManager.class)), - mock(NotificationGutsManager.class)); + mock(NotificationGutsManager.class), + mock(MetricsLogger.class), + mock(SmartReplyConstants.class), + mock(SmartReplyController.class)); row.setAboveShelfChangedListener(aboveShelf -> { }); mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java index bf16e0ab39c6..c9de60806b66 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java @@ -134,6 +134,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { @Mock private InteractionJankMonitor mJankMonitor; @Mock private StackStateLogger mStackLogger; @Mock private NotificationStackScrollLogger mLogger; + @Mock private NotificationStackSizeCalculator mNotificationStackSizeCalculator; @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerArgumentCaptor; @@ -186,7 +187,8 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { mShadeController, mJankMonitor, mStackLogger, - mLogger + mLogger, + mNotificationStackSizeCalculator ); when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index 56541f3ceb6f..7a92b96f40db 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -103,6 +103,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Mock private NotificationStackScrollLayoutController mStackScrollLayoutController; @Mock private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; @Mock private NotificationShelf mNotificationShelf; + @Mock private NotificationStackSizeCalculator mNotificationStackSizeCalculator; @Before @UiThreadTest @@ -138,7 +139,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { // holds a copy of the CUT's instances of these KeyguardBypassController, so they still // refer to the CUT's member variables, not the spy's member variables. mStackScrollerInternal = new NotificationStackScrollLayout(getContext(), null); - mStackScrollerInternal.initView(getContext(), mNotificationSwipeHelper); + mStackScrollerInternal.initView(getContext(), mNotificationSwipeHelper, + mNotificationStackSizeCalculator); mStackScroller = spy(mStackScrollerInternal); mStackScroller.setShelfController(notificationShelfController); mStackScroller.setCentralSurfaces(mCentralSurfaces); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt new file mode 100644 index 000000000000..d1848e38ca06 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.stack + +import android.service.notification.StatusBarNotification +import android.testing.AndroidTestingRunner +import android.view.View.VISIBLE +import androidx.test.filters.SmallTest +import com.android.systemui.R +import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.NotificationLockscreenUserManager +import com.android.systemui.statusbar.StatusBarState.KEYGUARD +import com.android.systemui.statusbar.StatusBarState.SHADE +import com.android.systemui.statusbar.SysuiStatusBarStateController +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow +import com.android.systemui.statusbar.notification.row.ExpandableView +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.nullable +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.mock +import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class NotificationStackSizeCalculatorTest : SysuiTestCase() { + + @Mock private lateinit var groupManager: NotificationGroupManagerLegacy + + @Mock private lateinit var notificationLockscreenUserManager: NotificationLockscreenUserManager + + @Mock private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController + + @Mock private lateinit var stackLayout: NotificationStackScrollLayout + + private val testableResources = mContext.getOrCreateTestableResources() + + private lateinit var sizeCalculator: NotificationStackSizeCalculator + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + whenever(stackLayout.calculateGapHeight(nullable(), nullable(), any())) + .thenReturn(GAP_HEIGHT) + whenever(groupManager.isSummaryOfSuppressedGroup(any())).thenReturn(false) + with(testableResources) { + addOverride(R.integer.keyguard_max_notification_count, -1) + addOverride(R.dimen.notification_divider_height, NOTIFICATION_PADDING.toInt()) + } + + sizeCalculator = + NotificationStackSizeCalculator( + groupManager = groupManager, + lockscreenUserManager = notificationLockscreenUserManager, + statusBarStateController = sysuiStatusBarStateController, + testableResources.resources) + } + + @Test + fun computeMaxKeyguardNotifications_zeroSpace_returnZero() { + val rows = listOf(createMockRow(height = ROW_HEIGHT, visibleOnLockscreen = true)) + + val maxNotifications = + computeMaxKeyguardNotifications(rows, availableSpace = 0f, shelfHeight = 0f) + + assertThat(maxNotifications).isEqualTo(0) + } + + @Test + fun computeMaxKeyguardNotifications_infiniteSpace_returnsAll() { + val numberOfRows = 30 + val rows = createLockscreenRows(numberOfRows) + + val maxNotifications = computeMaxKeyguardNotifications(rows, Float.MAX_VALUE) + + assertThat(maxNotifications).isEqualTo(numberOfRows) + } + + @Test + fun computeMaxKeyguardNotifications_spaceForOne_returnsOne() { + val rowHeight = ROW_HEIGHT + val totalSpaceForEachRow = GAP_HEIGHT + rowHeight + val shelfHeight = + totalSpaceForEachRow / 2 // In this way shelf absence will not leave room for another. + val spaceForOne = totalSpaceForEachRow + val rows = + listOf( + createMockRow(rowHeight, visibleOnLockscreen = true), + createMockRow(rowHeight, visibleOnLockscreen = true)) + + val maxNotifications = + computeMaxKeyguardNotifications( + rows, availableSpace = spaceForOne, shelfHeight = shelfHeight) + + assertThat(maxNotifications).isEqualTo(1) + } + + @Test + fun computeMaxKeyguardNotifications_spaceForOne_shelfUsableForLastNotification_returnsTwo() { + val rowHeight = ROW_HEIGHT + val totalSpaceForEachRow = GAP_HEIGHT + rowHeight + val shelfHeight = totalSpaceForEachRow + NOTIFICATION_PADDING + val spaceForOne = totalSpaceForEachRow + val rows = + listOf( + createMockRow(rowHeight, visibleOnLockscreen = true), + createMockRow(rowHeight, visibleOnLockscreen = true)) + + val maxNotifications = + computeMaxKeyguardNotifications( + rows, availableSpace = spaceForOne, shelfHeight = shelfHeight) + + assertThat(maxNotifications).isEqualTo(1) + } + + @Test + fun computeMaxKeyguardNotifications_invisibleOnLockscreen_returnsZero() { + val rows = listOf(createMockRow(visibleOnLockscreen = false)) + + val maxNotifications = computeMaxKeyguardNotifications(rows, Float.MAX_VALUE) + + assertThat(maxNotifications).isEqualTo(0) + } + + @Test + fun computeMaxKeyguardNotifications_spaceForTwo_returnsTwo() { + val rowHeight = ROW_HEIGHT + val totalSpaceForEachRow = GAP_HEIGHT + rowHeight + val spaceForTwo = totalSpaceForEachRow * 2 + NOTIFICATION_PADDING + val rows = + listOf( + createMockRow(rowHeight, visibleOnLockscreen = true), + createMockRow(rowHeight, visibleOnLockscreen = true), + createMockRow(rowHeight, visibleOnLockscreen = true)) + + val maxNotifications = computeMaxKeyguardNotifications(rows, spaceForTwo, shelfHeight = 0f) + + assertThat(maxNotifications).isEqualTo(2) + } + + @Test + fun computeHeight_returnsLessThanAvailableSpaceUsedToCalculateMaxNotifications() { + val rowHeight = ROW_HEIGHT + val shelfHeight = SHELF_HEIGHT + val totalSpaceForEachRow = GAP_HEIGHT + rowHeight + NOTIFICATION_PADDING + val availableSpace = totalSpaceForEachRow * 2 + val rows = + listOf( + createMockRow(rowHeight, visibleOnLockscreen = true), + createMockRow(rowHeight, visibleOnLockscreen = true), + createMockRow(rowHeight, visibleOnLockscreen = true)) + + val maxNotifications = computeMaxKeyguardNotifications(rows, availableSpace, shelfHeight) + assertThat(maxNotifications).isEqualTo(2) + + val height = sizeCalculator.computeHeight(stackLayout, maxNotifications, SHELF_HEIGHT) + assertThat(height).isAtMost(availableSpace + SHELF_HEIGHT) + } + + @Test + fun computeHeight_allInvisibleToLockscreen_NotInLockscreen_returnsHigherThanZero() { + setOnLockscreen(false) + val rowHeight = 10f + setupChildren(listOf(createMockRow(rowHeight, visibleOnLockscreen = false))) + + val height = + sizeCalculator.computeHeight( + stackLayout, maxNotifications = Int.MAX_VALUE, SHELF_HEIGHT) + + assertThat(height).isGreaterThan(rowHeight) + } + + @Test + fun computeHeight_allInvisibleToLockscreen_onLockscreen_returnsZero() { + setOnLockscreen(true) + setupChildren(listOf(createMockRow(visibleOnLockscreen = false))) + + val height = + sizeCalculator.computeHeight( + stackLayout, maxNotifications = Int.MAX_VALUE, SHELF_HEIGHT) + + assertThat(height).isEqualTo(0) + } + + private fun computeMaxKeyguardNotifications( + rows: List<ExpandableView>, + availableSpace: Float, + shelfHeight: Float = SHELF_HEIGHT + ): Int { + setupChildren(rows) + return sizeCalculator.computeMaxKeyguardNotifications( + stackLayout, availableSpace, shelfHeight) + } + + private fun setupChildren(children: List<ExpandableView>) { + whenever(stackLayout.getChildAt(any())).thenAnswer { invocation -> + val inx = invocation.getArgument<Int>(0) + return@thenAnswer children[inx] + } + whenever(stackLayout.childCount).thenReturn(children.size) + } + + private fun createLockscreenRows(number: Int): List<ExpandableNotificationRow> = + (1..number).map { createMockRow(visibleOnLockscreen = true) }.toList() + + private fun createMockRow( + height: Float = ROW_HEIGHT, + visibleOnLockscreen: Boolean = true, + isRemoved: Boolean = false, + visibility: Int = VISIBLE, + summaryOfSuppressed: Boolean = false + ): ExpandableNotificationRow { + val row = mock(ExpandableNotificationRow::class.java) + val entry = mock(NotificationEntry::class.java) + val sbn = mock(StatusBarNotification::class.java) + whenever(entry.sbn).thenReturn(sbn) + whenever(row.entry).thenReturn(entry) + whenever(row.isRemoved).thenReturn(isRemoved) + whenever(row.visibility).thenReturn(visibility) + whenever(notificationLockscreenUserManager.shouldShowOnKeyguard(entry)) + .thenReturn(visibleOnLockscreen) + whenever(groupManager.isSummaryOfSuppressedGroup(sbn)).thenReturn(summaryOfSuppressed) + whenever(row.getMinHeight(any())).thenReturn(height.toInt()) + whenever(row.intrinsicHeight).thenReturn(height.toInt()) + return row + } + + private fun setOnLockscreen(onLockscreen: Boolean) { + whenever(sysuiStatusBarStateController.state) + .thenReturn( + if (onLockscreen) { + KEYGUARD + } else { + SHADE + }) + } + + /** Default dimensions for tests that don't overwrite them. */ + companion object { + const val GAP_HEIGHT = 12f + const val NOTIFICATION_PADDING = 3f + const val SHELF_HEIGHT = 14f + const val ROW_HEIGHT = SHELF_HEIGHT * 3 + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java index 4bac08ea536b..06b20380c0ab 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java @@ -104,7 +104,6 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.KeyguardAffordanceView; import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.LockscreenShadeTransitionController; -import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeWindowController; @@ -119,12 +118,12 @@ import com.android.systemui.statusbar.notification.ConversationNotificationManag import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; -import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.stack.AmbientState; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; +import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator; import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController; @@ -174,8 +173,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { @Mock private NotificationShelfController mNotificationShelfController; @Mock - private NotificationGroupManagerLegacy mGroupManager; - @Mock private KeyguardStatusBarView mKeyguardStatusBar; @Mock private KeyguardUserSwitcherView mUserSwitcherView; @@ -200,10 +197,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { @Mock private DynamicPrivacyController mDynamicPrivacyController; @Mock - private ShadeController mShadeController; - @Mock - private NotificationLockscreenUserManager mNotificationLockscreenUserManager; - @Mock private NotificationEntryManager mNotificationEntryManager; @Mock private StatusBarTouchableRegionManager mStatusBarTouchableRegionManager; @@ -336,6 +329,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { private SysUiState mSysUiState; @Mock private NotificationListContainer mNotificationListContainer; + @Mock + private NotificationStackSizeCalculator mNotificationStackSizeCalculator; private NotificationPanelViewController.PanelEventsEmitter mPanelEventsEmitter; private Optional<SysUIUnfoldComponent> mSysUIUnfoldComponent = Optional.empty(); private SysuiStatusBarStateController mStatusBarStateController; @@ -466,7 +461,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { mFeatureFlags, coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController, mFalsingManager, new FalsingCollectorFake(), - mNotificationLockscreenUserManager, mNotificationEntryManager, + mNotificationEntryManager, mKeyguardStateController, mStatusBarStateController, mStatusBarWindowStateController, @@ -484,7 +479,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { mKeyguardUserSwitcherComponentFactory, mKeyguardStatusBarViewComponentFactory, mLockscreenShadeTransitionController, - mGroupManager, mNotificationAreaController, mAuthController, mScrimController, @@ -516,7 +510,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { mSysUiState, mKeyguardUnlockAnimationController, mNotificationListContainer, - mPanelEventsEmitter); + mPanelEventsEmitter, + mNotificationStackSizeCalculator); mNotificationPanelViewController.initDependencies( mCentralSurfaces, () -> {}, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt index 83eabb667997..6526fabefe49 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt @@ -6,7 +6,6 @@ import android.view.View import android.view.ViewGroup import android.view.WindowInsets import android.view.WindowManagerPolicyConstants -import androidx.annotation.AnyRes import androidx.annotation.IdRes import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet @@ -104,10 +103,6 @@ class NotificationQSContainerControllerTest : SysuiTestCase() { windowInsetsCallback = windowInsetsCallbackCaptor.value } - private fun overrideResource(@AnyRes id: Int, value: Any) { - mContext.orCreateTestableResources.addOverride(id, value) - } - @Test fun testTaskbarVisibleInSplitShade() { enableSplitShade() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index 0b25467d20d8..786a8586ea39 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.phone; -import static com.android.systemui.statusbar.phone.ScrimController.KEYGUARD_SCRIM_ALPHA; import static com.android.systemui.statusbar.phone.ScrimController.OPAQUE; import static com.android.systemui.statusbar.phone.ScrimController.SEMI_TRANSPARENT; import static com.android.systemui.statusbar.phone.ScrimController.TRANSPARENT; @@ -1263,19 +1262,36 @@ public class ScrimControllerTest extends SysuiTestCase { mScrimController.setClipsQsScrim(true); float progress = 0.5f; - mScrimController.setTransitionToFullShadeProgress(progress); + float lsNotifProgress = 0.3f; + mScrimController.setTransitionToFullShadeProgress(progress, lsNotifProgress); assertEquals(MathUtils.lerp(keyguardAlpha, shadeLockedAlpha, progress), mNotificationsScrim.getViewAlpha(), 0.2); progress = 0.0f; - mScrimController.setTransitionToFullShadeProgress(progress); + mScrimController.setTransitionToFullShadeProgress(progress, lsNotifProgress); assertEquals(MathUtils.lerp(keyguardAlpha, shadeLockedAlpha, progress), mNotificationsScrim.getViewAlpha(), 0.2); progress = 1.0f; - mScrimController.setTransitionToFullShadeProgress(progress); + mScrimController.setTransitionToFullShadeProgress(progress, lsNotifProgress); assertEquals(MathUtils.lerp(keyguardAlpha, shadeLockedAlpha, progress), mNotificationsScrim.getViewAlpha(), 0.2); } + @Test + public void notificationTransparency_followsNotificationScrimProgress() { + mScrimController.transitionTo(ScrimState.SHADE_LOCKED); + mScrimController.setRawPanelExpansionFraction(1.0f); + finishAnimationsImmediately(); + mScrimController.transitionTo(ScrimState.KEYGUARD); + mScrimController.setRawPanelExpansionFraction(1.0f); + finishAnimationsImmediately(); + + float progress = 0.5f; + float notifProgress = 0.3f; + mScrimController.setTransitionToFullShadeProgress(progress, notifProgress); + + assertThat(mNotificationsScrim.getViewAlpha()).isEqualTo(notifProgress); + } + private void assertAlphaAfterExpansion(ScrimView scrim, float expectedAlpha, float expansion) { mScrimController.setRawPanelExpansionFraction(expansion); finishAnimationsImmediately(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java index d48ce8c6803e..fa867e2796f7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java @@ -447,4 +447,15 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { controllerCaptor.getValue().onIntentStarted(false); verify(listener).onFinishLaunchNotifActivity(mNotificationRow.getEntry()); } + + @Test + public void testNotifActivityStarterEventSourceFinishEvent_postPanelCollapse_noAnimate() { + NotifActivityLaunchEvents.Listener listener = + mock(NotifActivityLaunchEvents.Listener.class); + mLaunchEventsEmitter.registerListener(listener); + when(mCentralSurfaces.shouldAnimateLaunch(anyBoolean())).thenReturn(false); + mNotificationActivityStarter + .onNotificationClicked(mNotificationRow.getEntry().getSbn(), mNotificationRow); + verify(listener).onFinishLaunchNotifActivity(mNotificationRow.getEntry()); + } } diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto index 3f712dd1492f..3801c2473c11 100644 --- a/proto/src/metrics_constants/metrics_constants.proto +++ b/proto/src/metrics_constants/metrics_constants.proto @@ -2260,10 +2260,12 @@ message MetricsEvent { ACCOUNTS_WORK_PROFILE_SETTINGS = 401; // Settings -> Dev options -> Convert to file encryption - CONVERT_FBE = 402; + // DEPRECATED: this setting was removed in Android T. + CONVERT_FBE = 402 [deprecated=true]; // Settings -> Dev options -> Convert to file encryption -> WIPE AND CONVERT... - CONVERT_FBE_CONFIRM = 403; + // DEPRECATED: this setting was removed in Android T. + CONVERT_FBE_CONFIRM = 403 [deprecated=true]; // Settings -> Dev options -> Running services RUNNING_SERVICES = 404; diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 249ee16afaae..61e3da8aae51 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -322,28 +322,28 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public void setImeSessionEnabled(SparseArray<IInputMethodSession> sessions, boolean enabled) { - mService.setImeSessionEnabled(sessions, enabled); + mService.scheduleSetImeSessionEnabled(sessions, enabled); } @Override public void unbindInput() { - mService.unbindInput(); + mService.scheduleUnbindInput(); } @Override public void bindInput(InputBinding binding) { - mService.bindInput(binding); + mService.scheduleBindInput(binding); } @Override public void createImeSession(ArraySet<Integer> ignoreSet) { - mService.createImeSession(ignoreSet); + mService.scheduleCreateImeSession(ignoreSet); } @Override public void startInput(IBinder startInputToken, IInputContext inputContext, EditorInfo editorInfo, boolean restarting) { - mService.startInput(startInputToken, inputContext, editorInfo, restarting); + mService.scheduleStartInput(startInputToken, inputContext, editorInfo, restarting); } } @@ -4377,12 +4377,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * * @param binding Information given to an accessibility service about a client connecting to it. */ - public void bindInput(InputBinding binding) { - AccessibilityUserState userState; + public void scheduleBindInput(InputBinding binding) { + mMainHandler.sendMessage(obtainMessage(AccessibilityManagerService::bindInput, this, + binding)); + } + + private void bindInput(InputBinding binding) { synchronized (mLock) { // Keep records of these in case new Accessibility Services are enabled. mInputBinding = binding; - userState = getCurrentUserStateLocked(); + AccessibilityUserState userState = getCurrentUserStateLocked(); for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) { final AccessibilityServiceConnection service = userState.mBoundServices.get(i); if (service.requestImeApis()) { @@ -4395,11 +4399,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub /** * Unbind input for accessibility services which request ime capabilities. */ - public void unbindInput() { - AccessibilityUserState userState; - // TODO(b/218182733): Resolve the Imf lock and mLock possible deadlock + public void scheduleUnbindInput() { + mMainHandler.sendMessage(obtainMessage(AccessibilityManagerService::unbindInput, this)); + } + + private void unbindInput() { synchronized (mLock) { - userState = getCurrentUserStateLocked(); + AccessibilityUserState userState = getCurrentUserStateLocked(); for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) { final AccessibilityServiceConnection service = userState.mBoundServices.get(i); if (service.requestImeApis()) { @@ -4412,16 +4418,21 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub /** * Start input for accessibility services which request ime capabilities. */ - public void startInput(IBinder startInputToken, IInputContext inputContext, + public void scheduleStartInput(IBinder startInputToken, IInputContext inputContext, + EditorInfo editorInfo, boolean restarting) { + mMainHandler.sendMessage(obtainMessage(AccessibilityManagerService::startInput, this, + startInputToken, inputContext, editorInfo, restarting)); + } + + private void startInput(IBinder startInputToken, IInputContext inputContext, EditorInfo editorInfo, boolean restarting) { - AccessibilityUserState userState; synchronized (mLock) { // Keep records of these in case new Accessibility Services are enabled. mStartInputToken = startInputToken; mInputContext = inputContext; mEditorInfo = editorInfo; mRestarting = restarting; - userState = getCurrentUserStateLocked(); + AccessibilityUserState userState = getCurrentUserStateLocked(); for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) { final AccessibilityServiceConnection service = userState.mBoundServices.get(i); if (service.requestImeApis()) { @@ -4435,11 +4446,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * Request input sessions from all accessibility services which request ime capabilities and * whose id is not in the ignoreSet */ - public void createImeSession(ArraySet<Integer> ignoreSet) { - AccessibilityUserState userState; + public void scheduleCreateImeSession(ArraySet<Integer> ignoreSet) { + mMainHandler.sendMessage(obtainMessage(AccessibilityManagerService::createImeSession, + this, ignoreSet)); + } + + private void createImeSession(ArraySet<Integer> ignoreSet) { synchronized (mLock) { mInputSessionRequested = true; - userState = getCurrentUserStateLocked(); + AccessibilityUserState userState = getCurrentUserStateLocked(); for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) { final AccessibilityServiceConnection service = userState.mBoundServices.get(i); if ((!ignoreSet.contains(service.mId)) && service.requestImeApis()) { @@ -4455,10 +4470,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * @param sessions Sessions to enable or disable. * @param enabled True if enable the sessions or false if disable the sessions. */ - public void setImeSessionEnabled(SparseArray<IInputMethodSession> sessions, boolean enabled) { - AccessibilityUserState userState; + public void scheduleSetImeSessionEnabled(SparseArray<IInputMethodSession> sessions, + boolean enabled) { + mMainHandler.sendMessage(obtainMessage(AccessibilityManagerService::setImeSessionEnabled, + this, sessions, enabled)); + } + + private void setImeSessionEnabled(SparseArray<IInputMethodSession> sessions, boolean enabled) { synchronized (mLock) { - userState = getCurrentUserStateLocked(); + AccessibilityUserState userState = getCurrentUserStateLocked(); for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) { final AccessibilityServiceConnection service = userState.mBoundServices.get(i); if (sessions.contains(service.mId) && service.requestImeApis()) { diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java index fa32452f389e..ecc45eb743c6 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java @@ -18,6 +18,7 @@ package com.android.server.accessibility.magnification; import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL; import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN; +import static android.util.TypedValue.COMPLEX_UNIT_DIP; import static android.view.accessibility.MagnificationAnimationCallback.STUB_ANIMATION_CALLBACK; import static com.android.server.accessibility.AccessibilityManagerService.INVALID_SERVICE_ID; @@ -31,15 +32,20 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.res.CompatibilityInfo; import android.graphics.Rect; import android.graphics.Region; +import android.hardware.display.DisplayManagerInternal; import android.os.Handler; import android.os.Message; import android.text.TextUtils; +import android.util.DisplayMetrics; import android.util.MathUtils; import android.util.Slog; import android.util.SparseArray; +import android.util.TypedValue; import android.view.Display; +import android.view.DisplayInfo; import android.view.MagnificationSpec; import android.view.View; import android.view.accessibility.MagnificationAnimationCallback; @@ -93,6 +99,8 @@ public class FullScreenMagnificationController implements // Whether the following typing focus feature for magnification is enabled. private boolean mMagnificationFollowTypingEnabled = true; + private final DisplayManagerInternal mDisplayManagerInternal; + /** * This class implements {@link WindowManagerInternal.MagnificationCallbacks} and holds * magnification information per display. @@ -395,6 +403,18 @@ public class FullScreenMagnificationController implements outRegion.set(mMagnificationRegion); } + private DisplayMetrics getDisplayMetricsForId() { + final DisplayMetrics outMetrics = new DisplayMetrics(); + final DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(mDisplayId); + if (displayInfo != null) { + displayInfo.getLogicalMetrics(outMetrics, + CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null); + } else { + outMetrics.setToDefaults(); + } + return outMetrics; + } + void requestRectangleOnScreen(int left, int top, int right, int bottom) { synchronized (mLock) { final Rect magnifiedFrame = mTempRect; @@ -408,6 +428,12 @@ public class FullScreenMagnificationController implements final float scrollX; final float scrollY; + // We offset an additional distance for a user to know the surrounding context. + DisplayMetrics metrics = getDisplayMetricsForId(); + final float offsetViewportX = (float) magnifFrameInScreenCoords.width() / 4; + final float offsetViewportY = + TypedValue.applyDimension(COMPLEX_UNIT_DIP, 10, metrics); + if (right - left > magnifFrameInScreenCoords.width()) { final int direction = TextUtils .getLayoutDirectionFromLocale(Locale.getDefault()); @@ -417,9 +443,9 @@ public class FullScreenMagnificationController implements scrollX = right - magnifFrameInScreenCoords.right; } } else if (left < magnifFrameInScreenCoords.left) { - scrollX = left - magnifFrameInScreenCoords.left; + scrollX = left - magnifFrameInScreenCoords.left - offsetViewportX; } else if (right > magnifFrameInScreenCoords.right) { - scrollX = right - magnifFrameInScreenCoords.right; + scrollX = right - magnifFrameInScreenCoords.right + offsetViewportX; } else { scrollX = 0; } @@ -427,9 +453,9 @@ public class FullScreenMagnificationController implements if (bottom - top > magnifFrameInScreenCoords.height()) { scrollY = top - magnifFrameInScreenCoords.top; } else if (top < magnifFrameInScreenCoords.top) { - scrollY = top - magnifFrameInScreenCoords.top; + scrollY = top - magnifFrameInScreenCoords.top - offsetViewportY; } else if (bottom > magnifFrameInScreenCoords.bottom) { - scrollY = bottom - magnifFrameInScreenCoords.bottom; + scrollY = bottom - magnifFrameInScreenCoords.bottom + offsetViewportY; } else { scrollY = 0; } @@ -687,6 +713,7 @@ public class FullScreenMagnificationController implements mScreenStateObserver = new ScreenStateObserver(mControllerCtx.getContext(), this); mMagnificationInfoChangedCallback = magnificationInfoChangedCallback; mScaleProvider = scaleProvider; + mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); } /** diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java index bf8b18ce3157..7a5fa628f645 100644 --- a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java +++ b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java @@ -51,6 +51,7 @@ import android.os.Parcel; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.UserHandle; +import android.util.Log; import android.util.PackageUtils; import android.util.Slog; @@ -332,13 +333,28 @@ class AssociationRequestsProcessor { } } - String[] sameOemPackages = mContext.getResources() + // Below we check if the requesting package is allowlisted (usually by the OEM) for creating + // CDM associations without user confirmation (prompt). + // For this we'll check to config arrays: + // - com.android.internal.R.array.config_companionDevicePackages + // and + // - com.android.internal.R.array.config_companionDeviceCerts. + // Both arrays are expected to contain similar number of entries. + // config_companionDevicePackages contains package names of the allowlisted packages. + // config_companionDeviceCerts contains SHA256 digests of the signatures of the + // corresponding packages. + // If a package may be signed with one of several certificates, its package name would + // appear multiple times in the config_companionDevicePackages, with different entries + // (one for each of the valid signing certificates) at the corresponding positions in + // config_companionDeviceCerts. + final String[] allowlistedPackages = mContext.getResources() .getStringArray(com.android.internal.R.array.config_companionDevicePackages); - if (!ArrayUtils.contains(sameOemPackages, packageName)) { - Slog.w(TAG, packageName - + " can not silently create associations due to no package found." - + " Packages from OEM: " + Arrays.toString(sameOemPackages) - ); + if (!ArrayUtils.contains(allowlistedPackages, packageName)) { + if (DEBUG) { + Log.d(TAG, packageName + " is not allowlisted for creating associations " + + "without user confirmation (prompt)"); + Log.v(TAG, "Allowlisted packages=" + Arrays.toString(allowlistedPackages)); + } return false; } @@ -361,44 +377,41 @@ class AssociationRequestsProcessor { } } - String[] sameOemCerts = mContext.getResources() + final String[] allowlistedPackagesSignatureDigests = mContext.getResources() .getStringArray(com.android.internal.R.array.config_companionDeviceCerts); - - Signature[] signatures = mPackageManager.getPackage(packageName).getSigningDetails() - .getSignatures(); - String[] apkCerts = PackageUtils.computeSignaturesSha256Digests(signatures); - - Set<String> sameOemPackageCerts = - getSameOemPackageCerts(packageName, sameOemPackages, sameOemCerts); - - for (String cert : apkCerts) { - if (sameOemPackageCerts.contains(cert)) { - return true; + final Set<String> allowlistedSignatureDigestsForRequestingPackage = new HashSet<>(); + for (int i = 0; i < allowlistedPackages.length; i++) { + if (allowlistedPackages[i].equals(packageName)) { + final String digest = allowlistedPackagesSignatureDigests[i].replaceAll(":", ""); + allowlistedSignatureDigestsForRequestingPackage.add(digest); } } - Slog.w(TAG, packageName - + " can not silently create associations. " + packageName - + " has SHA256 certs from APK: " + Arrays.toString(apkCerts) - + " and from OEM: " + Arrays.toString(sameOemCerts) - ); + final Signature[] requestingPackageSignatures = mPackageManager.getPackage(packageName) + .getSigningDetails().getSignatures(); + final String[] requestingPackageSignatureDigests = + PackageUtils.computeSignaturesSha256Digests(requestingPackageSignatures); - return false; - } - - private static Set<String> getSameOemPackageCerts( - String packageName, String[] oemPackages, String[] sameOemCerts) { - Set<String> sameOemPackageCerts = new HashSet<>(); + boolean requestingPackageSignatureAllowlisted = false; + for (String signatureDigest : requestingPackageSignatureDigests) { + if (allowlistedSignatureDigestsForRequestingPackage.contains(signatureDigest)) { + requestingPackageSignatureAllowlisted = true; + break; + } + } - // Assume OEM may enter same package name in the parallel string array with - // multiple APK certs corresponding to it - for (int i = 0; i < oemPackages.length; i++) { - if (oemPackages[i].equals(packageName)) { - sameOemPackageCerts.add(sameOemCerts[i].replaceAll(":", "")); + if (!requestingPackageSignatureAllowlisted) { + Slog.w(TAG, "Certificate mismatch for allowlisted package " + packageName); + if (DEBUG) { + Log.d(TAG, " > allowlisted signatures for " + packageName + ": [" + + String.join(", ", allowlistedSignatureDigestsForRequestingPackage) + + "]"); + Log.d(TAG, " > actual signatures for " + packageName + ": " + + Arrays.toString(requestingPackageSignatureDigests)); } } - return sameOemPackageCerts; + return requestingPackageSignatureAllowlisted; } /** diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java b/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java index 4c7b9b80d5be..a6bd48056e12 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java @@ -51,12 +51,12 @@ class CompanionDeviceServiceConnector extends ServiceConnector.Impl<ICompanionDe void onBindingDied(@UserIdInt int userId, @NonNull String packageName); } - @UserIdInt - private final int mUserId; - @NonNull - private final ComponentName mComponentName; - @Nullable - private Listener mListener; + private final @UserIdInt int mUserId; + private final @NonNull ComponentName mComponentName; + // IMPORTANT: this can (and will!) be null (at the moment, CompanionApplicationController only + // installs a listener to the primary ServiceConnector), hence we should always null-check the + // reference before calling on it. + private @Nullable Listener mListener; /** * Create a CompanionDeviceServiceConnector instance. @@ -125,7 +125,9 @@ class CompanionDeviceServiceConnector extends ServiceConnector.Impl<ICompanionDe if (DEBUG) Log.d(TAG, "onBindingDied() " + mComponentName.toShortString()); - mListener.onBindingDied(mUserId, mComponentName.getPackageName()); + if (mListener != null) { + mListener.onBindingDied(mUserId, mComponentName.getPackageName()); + } } @Override diff --git a/services/core/java/com/android/server/NetworkTimeUpdateService.java b/services/core/java/com/android/server/NetworkTimeUpdateService.java index 186ff6215c9c..2015dc929a59 100644 --- a/services/core/java/com/android/server/NetworkTimeUpdateService.java +++ b/services/core/java/com/android/server/NetworkTimeUpdateService.java @@ -196,13 +196,15 @@ public class NetworkTimeUpdateService extends Binder { * Overrides the NTP server config for tests. Passing {@code null} to a parameter clears the * test value, i.e. so the normal value will be used next time. */ - void setServerConfigForTests(@Nullable String hostname, @Nullable Duration timeout) { + void setServerConfigForTests( + @Nullable String hostname, @Nullable Integer port, @Nullable Duration timeout) { mContext.enforceCallingPermission( android.Manifest.permission.SET_TIME, "set NTP server config for tests"); mLocalLog.log("Setting server config for tests: hostname=" + hostname + + ", port=" + port + ", timeout=" + timeout); - mTime.setServerConfigForTests(hostname, timeout); + mTime.setServerConfigForTests(hostname, port, timeout); } private void onPollNetworkTime(int event) { diff --git a/services/core/java/com/android/server/NetworkTimeUpdateServiceShellCommand.java b/services/core/java/com/android/server/NetworkTimeUpdateServiceShellCommand.java index 464af01a009a..d7504ceb9d4d 100644 --- a/services/core/java/com/android/server/NetworkTimeUpdateServiceShellCommand.java +++ b/services/core/java/com/android/server/NetworkTimeUpdateServiceShellCommand.java @@ -46,6 +46,7 @@ class NetworkTimeUpdateServiceShellCommand extends ShellCommand { */ private static final String SHELL_COMMAND_SET_SERVER_CONFIG = "set_server_config"; private static final String SET_SERVER_CONFIG_HOSTNAME_ARG = "--hostname"; + private static final String SET_SERVER_CONFIG_PORT_ARG = "--port"; private static final String SET_SERVER_CONFIG_TIMEOUT_ARG = "--timeout_millis"; @NonNull @@ -87,6 +88,7 @@ class NetworkTimeUpdateServiceShellCommand extends ShellCommand { private int runSetServerConfig() { String hostname = null; + Integer port = null; Duration timeout = null; String opt; while ((opt = getNextArg()) != null) { @@ -95,6 +97,10 @@ class NetworkTimeUpdateServiceShellCommand extends ShellCommand { hostname = getNextArgRequired(); break; } + case SET_SERVER_CONFIG_PORT_ARG: { + port = Integer.parseInt(getNextArgRequired()); + break; + } case SET_SERVER_CONFIG_TIMEOUT_ARG: { timeout = Duration.ofMillis(Integer.parseInt(getNextArgRequired())); break; @@ -104,7 +110,7 @@ class NetworkTimeUpdateServiceShellCommand extends ShellCommand { } } } - mNetworkTimeUpdateService.setServerConfigForTests(hostname, timeout); + mNetworkTimeUpdateService.setServerConfigForTests(hostname, port, timeout); return 0; } @@ -120,8 +126,9 @@ class NetworkTimeUpdateServiceShellCommand extends ShellCommand { pw.printf(" Refreshes the latest time. Prints whether it was successful.\n"); pw.printf(" %s\n", SHELL_COMMAND_SET_SERVER_CONFIG); pw.printf(" Sets the NTP server config for tests. The config is not persisted.\n"); - pw.printf(" Options: [%s <hostname>] [%s <millis>]\n", - SET_SERVER_CONFIG_HOSTNAME_ARG, SET_SERVER_CONFIG_TIMEOUT_ARG); + pw.printf(" Options: [%s <hostname>] [%s <port>] [%s <millis>]\n", + SET_SERVER_CONFIG_HOSTNAME_ARG, SET_SERVER_CONFIG_PORT_ARG, + SET_SERVER_CONFIG_TIMEOUT_ARG); pw.printf(" Each key/value is optional and must be specified to override the\n"); pw.printf(" normal value, not specifying a key causes it to reset to the original.\n"); pw.println(); diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 677fc7931f64..a2cfe4928249 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -3130,23 +3130,6 @@ class StorageManagerService extends IStorageManager.Stub } /** - * Is userdata convertible to file based encryption? - * @return non zero for convertible - */ - @Override - public boolean isConvertibleToFBE() throws RemoteException { - mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, - "no permission to access the crypt keeper"); - - try { - return mVold.isConvertibleToFbe(); - } catch (Exception e) { - Slog.wtf(TAG, e); - return false; - } - } - - /** * Check whether the device supports filesystem checkpointing. * * @return true if the device supports filesystem checkpointing, false otherwise. diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 0735648c1069..db2c566e8337 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2911,12 +2911,35 @@ public class ActivityManagerService extends IActivityManager.Stub return mAtmInternal.compatibilityInfoForPackage(ai); } + /** + * Enforces that the uid that calls a method is not an + * {@link UserHandle#isIsolated(int) isolated} uid. + * + * @param caller the name of the method being called. + * @throws SecurityException if the calling uid is an isolated uid. + */ /* package */ void enforceNotIsolatedCaller(String caller) { if (UserHandle.isIsolated(Binder.getCallingUid())) { throw new SecurityException("Isolated process not allowed to call " + caller); } } + /** + * Enforces that the uid that calls a method is not an + * {@link UserHandle#isIsolated(int) isolated} uid or an + * {@link Process#isSdkSandboxUid(int) SDK sandbox} uid. + * + * @param caller the name of the method being called. + * @throws SecurityException if the calling uid is an isolated uid or SDK sandbox uid. + */ + void enforceNotIsolatedOrSdkSandboxCaller(String caller) { + enforceNotIsolatedCaller(caller); + + if (Process.isSdkSandboxUid(Binder.getCallingUid())) { + throw new SecurityException("SDK sandbox process not allowed to call " + caller); + } + } + @Override public void setPackageScreenCompatMode(String packageName, int mode) { mActivityTaskManager.setPackageScreenCompatMode(packageName, mode); @@ -9186,6 +9209,10 @@ public class ActivityManagerService extends IActivityManager.Stub mAppRestrictionController.dump(pw, ""); } + void dumpAppRestrictionController(ProtoOutputStream proto, int uid) { + mAppRestrictionController.dumpAsProto(proto, uid); + } + /** * Wrapper function to print out debug data filtered by specified arguments. */ @@ -9298,6 +9325,29 @@ public class ActivityManagerService extends IActivityManager.Stub mProcessList.writeProcessesToProtoLSP(proto, dumpPackage); } } + } else if ("app-restrictions".equals(cmd)) { + int uid = Process.INVALID_UID; + boolean error = false; + for (int i = 0; i < args.length; i++) { + if ("--uid".equals(args[i])) { + if (i + 1 < args.length) { + try { + uid = Integer.parseInt(args[i + 1]); + } catch (NumberFormatException e) { + error = true; + } + } else { + error = true; + } + break; + } + } + if (error) { + pw.println("Invalid --uid argument"); + pw.println("Use -h for help."); + } else { + dumpAppRestrictionController(proto, uid); + } } else { // default option, dump everything, output is ActivityManagerServiceProto synchronized (this) { @@ -12843,7 +12893,7 @@ public class ActivityManagerService extends IActivityManager.Stub public Intent registerReceiverWithFeature(IApplicationThread caller, String callerPackage, String callerFeatureId, String receiverId, IIntentReceiver receiver, IntentFilter filter, String permission, int userId, int flags) { - enforceNotIsolatedCaller("registerReceiver"); + enforceNotIsolatedOrSdkSandboxCaller("registerReceiver"); ArrayList<Intent> stickyIntents = null; ProcessRecord callerApp = null; final boolean visibleToInstantApps diff --git a/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java b/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java index d1cf0049d146..6f11b0001c7a 100644 --- a/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java +++ b/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java @@ -26,10 +26,13 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.os.SystemClock; +import android.util.ArrayMap; import android.util.Pair; import android.util.Slog; +import android.util.SparseArray; import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.am.AppBatteryExemptionTracker.AppBatteryExemptionPolicy; import com.android.server.am.AppBatteryExemptionTracker.UidBatteryStates; @@ -65,6 +68,11 @@ final class AppBatteryExemptionTracker // As it's a UID-based tracker, anywhere which requires a package name, use this default name. static final String DEFAULT_NAME = ""; + // As it's a UID-based tracker, while the state change event it receives could be + // in the combination of UID + package name, we'd have to leverage each package's state. + @GuardedBy("mLock") + private UidProcessMap<Integer> mUidPackageStates = new UidProcessMap<>(); + AppBatteryExemptionTracker(Context context, AppRestrictionController controller) { this(context, controller, null, null); } @@ -103,12 +111,75 @@ final class AppBatteryExemptionTracker .getUidBatteryUsage(uid); final int stateTypeIndex = stateTypeToIndex(stateType); synchronized (mLock) { - UidBatteryStates pkg = mPkgEvents.get(uid, DEFAULT_NAME); - if (pkg == null) { - pkg = createAppStateEvents(uid, DEFAULT_NAME); - mPkgEvents.put(uid, DEFAULT_NAME, pkg); + final SparseArray<ArrayMap<String, Integer>> map = mUidPackageStates.getMap(); + ArrayMap<String, Integer> pkgsStates = map.get(uid); + if (pkgsStates == null) { + pkgsStates = new ArrayMap<>(); + map.put(uid, pkgsStates); + } + int states = 0; + int indexOfPkg = pkgsStates.indexOfKey(packageName); + if (indexOfPkg >= 0) { + states = pkgsStates.valueAt(indexOfPkg); + } else { + pkgsStates.put(packageName, 0); + indexOfPkg = pkgsStates.indexOfKey(packageName); + } + boolean addEvent = false; + if (start) { + // Check if there is another package within this UID with this type of event start. + boolean alreadyStarted = false; + for (int i = pkgsStates.size() - 1; i >= 0; i--) { + final int s = pkgsStates.valueAt(i); + if ((s & stateType) != 0) { + alreadyStarted = true; + break; + } + } + pkgsStates.setValueAt(indexOfPkg, states | stateType); + if (!alreadyStarted) { + // This is the first package within this UID with this type of event start. + addEvent = true; + } + } else { + states &= ~stateType; + pkgsStates.setValueAt(indexOfPkg, states); + boolean allStopped = true; + for (int i = pkgsStates.size() - 1; i >= 0; i--) { + final int s = pkgsStates.valueAt(i); + if ((s & stateType) != 0) { + allStopped = false; + break; + } + } + if (allStopped) { + // None of the packages in this UID has an active event of this type. + addEvent = true; + } + if (states == 0) { // None of the states of this package are active, prune it. + pkgsStates.removeAt(indexOfPkg); + if (pkgsStates.size() == 0) { + map.remove(uid); + } + } + } + if (addEvent) { + UidBatteryStates pkg = mPkgEvents.get(uid, DEFAULT_NAME); + if (pkg == null) { + pkg = createAppStateEvents(uid, DEFAULT_NAME); + mPkgEvents.put(uid, DEFAULT_NAME, pkg); + } + pkg.addEvent(start, now, batteryUsage, stateTypeIndex); } - pkg.addEvent(start, now, batteryUsage, stateTypeIndex); + } + } + + @VisibleForTesting + @Override + void reset() { + super.reset(); + synchronized (mLock) { + mUidPackageStates.clear(); } } @@ -116,6 +187,7 @@ final class AppBatteryExemptionTracker if (!enabled) { synchronized (mLock) { mPkgEvents.clear(); + mUidPackageStates.clear(); } } } diff --git a/services/core/java/com/android/server/am/AppBatteryTracker.java b/services/core/java/com/android/server/am/AppBatteryTracker.java index ea1e3357c4dc..b7185ed7a302 100644 --- a/services/core/java/com/android/server/am/AppBatteryTracker.java +++ b/services/core/java/com/android/server/am/AppBatteryTracker.java @@ -46,6 +46,7 @@ import android.content.Context; import android.content.pm.ServiceInfo; import android.content.res.Resources; import android.content.res.TypedArray; +import android.os.AppBatteryStatsProto; import android.os.BatteryConsumer; import android.os.BatteryConsumer.Dimensions; import android.os.BatteryStatsInternal; @@ -62,6 +63,7 @@ import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.TimeUtils; +import android.util.proto.ProtoOutputStream; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; @@ -676,6 +678,63 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> super.dump(pw, prefix); } + @Override + void dumpAsProto(ProtoOutputStream proto, int uid) { + synchronized (mLock) { + final SparseArray<ImmutableBatteryUsage> uidConsumers = mUidBatteryUsageInWindow; + if (uid != android.os.Process.INVALID_UID) { + final BatteryUsage usage = uidConsumers.get(uid); + if (usage != null) { + dumpUidStats(proto, uid, usage); + } + } else { + for (int i = 0, size = uidConsumers.size(); i < size; i++) { + final int aUid = uidConsumers.keyAt(i); + final BatteryUsage usage = uidConsumers.valueAt(i); + dumpUidStats(proto, aUid, usage); + } + } + } + } + + private void dumpUidStats(ProtoOutputStream proto, int uid, BatteryUsage usage) { + if (usage.mUsage == null) { + return; + } + + final double foregroundUsage = usage.getUsagePowerMah(PROCESS_STATE_FOREGROUND); + final double backgroundUsage = usage.getUsagePowerMah(PROCESS_STATE_BACKGROUND); + final double fgsUsage = usage.getUsagePowerMah(PROCESS_STATE_FOREGROUND_SERVICE); + + if (foregroundUsage == 0 && backgroundUsage == 0 && fgsUsage == 0) { + return; + } + + final long token = proto.start(AppBatteryStatsProto.UID_STATS); + proto.write(AppBatteryStatsProto.UidStats.UID, uid); + dumpProcessStateStats(proto, + AppBatteryStatsProto.UidStats.ProcessStateStats.FOREGROUND, + foregroundUsage); + dumpProcessStateStats(proto, + AppBatteryStatsProto.UidStats.ProcessStateStats.BACKGROUND, + backgroundUsage); + dumpProcessStateStats(proto, + AppBatteryStatsProto.UidStats.ProcessStateStats.FOREGROUND_SERVICE, + fgsUsage); + proto.end(token); + } + + private void dumpProcessStateStats(ProtoOutputStream proto, int processState, double powerMah) { + if (powerMah == 0) { + return; + } + + final long token = proto.start(AppBatteryStatsProto.UidStats.PROCESS_STATE_STATS); + proto.write(AppBatteryStatsProto.UidStats.ProcessStateStats.PROCESS_STATE, processState); + proto.write(AppBatteryStatsProto.UidStats.ProcessStateStats.POWER_MAH, powerMah); + proto.end(token); + } + static class BatteryUsage { static final int BATTERY_USAGE_INDEX_UNSPECIFIED = PROCESS_STATE_UNSPECIFIED; static final int BATTERY_USAGE_INDEX_FOREGROUND = PROCESS_STATE_FOREGROUND; @@ -795,6 +854,15 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> return formatBatteryUsage(mUsage); } + double getUsagePowerMah(@BatteryConsumer.ProcessState int processState) { + switch (processState) { + case PROCESS_STATE_FOREGROUND: return mUsage[1]; + case PROCESS_STATE_BACKGROUND: return mUsage[2]; + case PROCESS_STATE_FOREGROUND_SERVICE: return mUsage[3]; + } + return 0; + } + boolean isValid() { for (int i = 0; i < mUsage.length; i++) { if (mUsage[i] < 0.0d) { diff --git a/services/core/java/com/android/server/am/AppPermissionTracker.java b/services/core/java/com/android/server/am/AppPermissionTracker.java index 69f70ca0d0e0..622d7465d4bf 100644 --- a/services/core/java/com/android/server/am/AppPermissionTracker.java +++ b/services/core/java/com/android/server/am/AppPermissionTracker.java @@ -17,6 +17,14 @@ package com.android.server.am; import static android.Manifest.permission.ACCESS_FINE_LOCATION; +import static android.Manifest.permission.CAMERA; +import static android.Manifest.permission.RECORD_AUDIO; +import static android.app.AppOpsManager.OPSTR_CAMERA; +import static android.app.AppOpsManager.OPSTR_FINE_LOCATION; +import static android.app.AppOpsManager.OPSTR_RECORD_AUDIO; +import static android.app.AppOpsManager.OP_NONE; +import static android.app.AppOpsManager.opToPublicName; +import static android.app.AppOpsManager.strOpToOp; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.Process.SYSTEM_UID; @@ -27,28 +35,37 @@ import static com.android.server.am.AppRestrictionController.DEVICE_CONFIG_SUBNA import static com.android.server.am.BaseAppStateTracker.STATE_TYPE_PERMISSION; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.AppOpsManager; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager.OnPermissionsChangedListener; import android.content.pm.PackageManagerInternal; import android.os.Handler; import android.os.Message; +import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.permission.PermissionManager; import android.provider.DeviceConfig; +import android.text.TextUtils; import android.util.ArraySet; +import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; +import com.android.internal.app.IAppOpsCallback; +import com.android.internal.app.IAppOpsService; import com.android.server.am.AppPermissionTracker.AppPermissionPolicy; import com.android.server.pm.permission.PermissionManagerServiceInternal; import java.io.PrintWriter; import java.lang.reflect.Constructor; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; /** * The tracker for monitoring selected permission state of apps. @@ -61,8 +78,17 @@ final class AppPermissionTracker extends BaseAppStateTracker<AppPermissionPolicy private final MyHandler mHandler; + /** + * Keep a new instance of callback for each appop we're monitoring, + * as the AppOpsService doesn't support monitoring multiple appops with single callback + * instance (except the ALL_OPS case). + */ + @GuardedBy("mAppOpsCallbacks") + private final SparseArray<MyAppOpsCallback> mAppOpsCallbacks = new SparseArray<>(); + @GuardedBy("mLock") - private SparseArray<ArraySet<String>> mUidGrantedPermissionsInMonitor = new SparseArray<>(); + private SparseArray<ArraySet<UidGrantedPermissionState>> mUidGrantedPermissionsInMonitor = + new SparseArray<>(); private volatile boolean mLockedBootCompleted = false; @@ -82,12 +108,25 @@ final class AppPermissionTracker extends BaseAppStateTracker<AppPermissionPolicy mHandler.obtainMessage(MyHandler.MSG_PERMISSIONS_CHANGED, uid, 0).sendToTarget(); } + private void handleAppOpsInit() { + final ArrayList<Integer> ops = new ArrayList<>(); + final Pair[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor(); + for (int i = 0; i < permissions.length; i++) { + final Pair<String, Integer> pair = permissions[i]; + if (pair.second != OP_NONE) { + ops.add(pair.second); + } + } + startWatchingMode(ops.toArray(new Integer[ops.size()])); + } + private void handlePermissionsInit() { final int[] allUsers = mInjector.getUserManagerInternal().getUserIds(); final PackageManagerInternal pmi = mInjector.getPackageManagerInternal(); final PermissionManagerServiceInternal pm = mInjector.getPermissionManagerServiceInternal(); - final String[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor(); - final SparseArray<ArraySet<String>> uidPerms = mUidGrantedPermissionsInMonitor; + final Pair[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor(); + final SparseArray<ArraySet<UidGrantedPermissionState>> uidPerms = + mUidGrantedPermissionsInMonitor; for (int userId : allUsers) { final List<ApplicationInfo> apps = pmi.getInstalledApplications(0, userId, SYSTEM_UID); if (apps == null) { @@ -96,33 +135,44 @@ final class AppPermissionTracker extends BaseAppStateTracker<AppPermissionPolicy final long now = SystemClock.elapsedRealtime(); for (int i = 0, size = apps.size(); i < size; i++) { final ApplicationInfo ai = apps.get(i); - for (String permission : permissions) { - if (pm.checkUidPermission(ai.uid, permission) != PERMISSION_GRANTED) { + for (Pair<String, Integer> permission : permissions) { + final UidGrantedPermissionState state = new UidGrantedPermissionState( + ai.uid, permission.first, permission.second); + if (!state.isGranted()) { + // No need to track it. continue; } synchronized (mLock) { - ArraySet<String> grantedPermissions = uidPerms.get(ai.uid); + ArraySet<UidGrantedPermissionState> grantedPermissions = + uidPerms.get(ai.uid); if (grantedPermissions == null) { - grantedPermissions = new ArraySet<String>(); + grantedPermissions = new ArraySet<UidGrantedPermissionState>(); uidPerms.put(ai.uid, grantedPermissions); + // This UID has at least one active permission-in-interest now, + // let the listeners know. + notifyListenersOnStateChange(ai.uid, DEFAULT_NAME, true, now, + STATE_TYPE_PERMISSION); } - grantedPermissions.add(permission); - notifyListenersOnStateChange(ai.uid, DEFAULT_NAME, true, now, - STATE_TYPE_PERMISSION); + grantedPermissions.add(state); } } } } } + private void handleAppOpsDestroy() { + stopWatchingMode(); + } + private void handlePermissionsDestroy() { synchronized (mLock) { - final SparseArray<ArraySet<String>> uidPerms = mUidGrantedPermissionsInMonitor; + final SparseArray<ArraySet<UidGrantedPermissionState>> uidPerms = + mUidGrantedPermissionsInMonitor; final long now = SystemClock.elapsedRealtime(); for (int i = 0, size = uidPerms.size(); i < size; i++) { final int uid = uidPerms.keyAt(i); - final ArraySet<String> grantedPermissions = uidPerms.valueAt(i); - for (int j = 0, numOfPerms = grantedPermissions.size(); j < numOfPerms; j++) { + final ArraySet<UidGrantedPermissionState> grantedPermissions = uidPerms.valueAt(i); + if (grantedPermissions.size() > 0) { notifyListenersOnStateChange(uid, DEFAULT_NAME, false, now, STATE_TYPE_PERMISSION); } @@ -131,44 +181,78 @@ final class AppPermissionTracker extends BaseAppStateTracker<AppPermissionPolicy } } + private void handleOpChanged(int op, int uid, String packageName) { + if (DEBUG_PERMISSION_TRACKER) { + final IAppOpsService appOpsService = mInjector.getIAppOpsService(); + try { + final int mode = appOpsService.checkOperation(op, uid, packageName); + Slog.i(TAG, "onOpChanged: " + opToPublicName(op) + + " " + UserHandle.formatUid(uid) + + " " + packageName + " " + mode); + } catch (RemoteException e) { + // Intra-process call, should never happen. + } + } + final Pair[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor(); + if (permissions != null && permissions.length > 0) { + for (int i = 0; i < permissions.length; i++) { + final Pair<String, Integer> pair = permissions[i]; + if (pair.second != op) { + continue; + } + final UidGrantedPermissionState state = + new UidGrantedPermissionState(uid, pair.first, op); + synchronized (mLock) { + handlePermissionsChangedLocked(uid, new UidGrantedPermissionState[] {state}); + } + break; + } + } + } + private void handlePermissionsChanged(int uid) { - final String[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor(); + if (DEBUG_PERMISSION_TRACKER) { + Slog.i(TAG, "handlePermissionsChanged " + UserHandle.formatUid(uid)); + } + final Pair[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor(); if (permissions != null && permissions.length > 0) { final PermissionManagerServiceInternal pm = mInjector.getPermissionManagerServiceInternal(); - final boolean[] states = new boolean[permissions.length]; + final UidGrantedPermissionState[] states = + new UidGrantedPermissionState[permissions.length]; for (int i = 0; i < permissions.length; i++) { - states[i] = pm.checkUidPermission(uid, permissions[i]) == PERMISSION_GRANTED; + final Pair<String, Integer> pair = permissions[i]; + states[i] = new UidGrantedPermissionState(uid, pair.first, pair.second); if (DEBUG_PERMISSION_TRACKER) { - Slog.i(TAG, UserHandle.formatUid(uid) + " " + permissions[i] + "=" + states[i]); + Slog.i(TAG, states[i].toString()); } } synchronized (mLock) { - handlePermissionsChangedLocked(uid, permissions, states); + handlePermissionsChangedLocked(uid, states); } } } @GuardedBy("mLock") - private void handlePermissionsChangedLocked(int uid, String[] permissions, boolean[] states) { + private void handlePermissionsChangedLocked(int uid, UidGrantedPermissionState[] states) { final int index = mUidGrantedPermissionsInMonitor.indexOfKey(uid); - ArraySet<String> grantedPermissions = index >= 0 + ArraySet<UidGrantedPermissionState> grantedPermissions = index >= 0 ? mUidGrantedPermissionsInMonitor.valueAt(index) : null; final long now = SystemClock.elapsedRealtime(); - for (int i = 0; i < permissions.length; i++) { - final String permission = permissions[i]; - final boolean granted = states[i]; + for (int i = 0; i < states.length; i++) { + final boolean granted = states[i].isGranted(); boolean changed = false; if (granted) { if (grantedPermissions == null) { grantedPermissions = new ArraySet<>(); mUidGrantedPermissionsInMonitor.put(uid, grantedPermissions); + changed = true; } - changed = grantedPermissions.add(permission); - } else if (grantedPermissions != null) { - changed = grantedPermissions.remove(permission); - if (grantedPermissions.isEmpty()) { + grantedPermissions.add(states[i]); + } else if (grantedPermissions != null && !grantedPermissions.isEmpty()) { + if (grantedPermissions.remove(states[i]) && grantedPermissions.isEmpty()) { mUidGrantedPermissionsInMonitor.removeAt(index); + changed = true; } } if (changed) { @@ -178,10 +262,141 @@ final class AppPermissionTracker extends BaseAppStateTracker<AppPermissionPolicy } } + /** + * Represents the grant state of a permission + appop of the given UID. + */ + private class UidGrantedPermissionState { + final int mUid; + final @Nullable String mPermission; + final int mAppOp; + + private boolean mPermissionGranted; + private boolean mAppOpAllowed; + + UidGrantedPermissionState(int uid, @Nullable String permission, int appOp) { + mUid = uid; + mPermission = permission; + mAppOp = appOp; + updatePermissionState(); + updateAppOps(); + } + + void updatePermissionState() { + if (TextUtils.isEmpty(mPermission)) { + mPermissionGranted = true; + return; + } + mPermissionGranted = mInjector.getPermissionManagerServiceInternal() + .checkUidPermission(mUid, mPermission) == PERMISSION_GRANTED; + } + + void updateAppOps() { + if (mAppOp == OP_NONE) { + mAppOpAllowed = true; + return; + } + final String[] packages = mInjector.getPackageManager().getPackagesForUid(mUid); + if (packages != null) { + final IAppOpsService appOpsService = mInjector.getIAppOpsService(); + for (String pkg : packages) { + try { + final int mode = appOpsService.checkOperation(mAppOp, mUid, pkg); + if (mode == AppOpsManager.MODE_ALLOWED) { + mAppOpAllowed = true; + return; + } + } catch (RemoteException e) { + // Intra-process call, should never happen. + } + } + } + mAppOpAllowed = false; + } + + boolean isGranted() { + return mPermissionGranted && mAppOpAllowed; + } + + @Override + public boolean equals(Object other) { + if (other == null || !(other instanceof UidGrantedPermissionState)) { + return false; + } + final UidGrantedPermissionState otherState = (UidGrantedPermissionState) other; + return mUid == otherState.mUid && mAppOp == otherState.mAppOp + && Objects.equals(mPermission, otherState.mPermission); + } + + @Override + public int hashCode() { + return (Integer.hashCode(mUid) * 31 + Integer.hashCode(mAppOp)) * 31 + + (mPermission == null ? 0 : mPermission.hashCode()); + } + + @Override + public String toString() { + String s = "UidGrantedPermissionState{" + + System.identityHashCode(this) + " " + + UserHandle.formatUid(mUid) + ": "; + final boolean emptyPermissionName = TextUtils.isEmpty(mPermission); + if (!emptyPermissionName) { + s += mPermission + "=" + mPermissionGranted; + } + if (mAppOp != OP_NONE) { + if (!emptyPermissionName) { + s += ","; + } + s += opToPublicName(mAppOp) + "=" + mAppOpAllowed; + } + s += "}"; + return s; + } + } + + private void startWatchingMode(@NonNull Integer[] ops) { + synchronized (mAppOpsCallbacks) { + stopWatchingMode(); + final IAppOpsService appOpsService = mInjector.getIAppOpsService(); + try { + for (int op: ops) { + final MyAppOpsCallback cb = new MyAppOpsCallback(); + mAppOpsCallbacks.put(op, cb); + appOpsService.startWatchingModeWithFlags(op, null, + AppOpsManager.WATCH_FOREGROUND_CHANGES, cb); + } + } catch (RemoteException e) { + // Intra-process call, should never happen. + } + } + } + + private void stopWatchingMode() { + synchronized (mAppOpsCallbacks) { + final IAppOpsService appOpsService = mInjector.getIAppOpsService(); + for (int i = mAppOpsCallbacks.size() - 1; i >= 0; i--) { + try { + appOpsService.stopWatchingMode(mAppOpsCallbacks.valueAt(i)); + } catch (RemoteException e) { + // Intra-process call, should never happen. + } + } + mAppOpsCallbacks.clear(); + } + } + + private class MyAppOpsCallback extends IAppOpsCallback.Stub { + @Override + public void opChanged(int op, int uid, String packageName) { + mHandler.obtainMessage(MyHandler.MSG_APPOPS_CHANGED, op, uid, packageName) + .sendToTarget(); + } + } + private static class MyHandler extends Handler { static final int MSG_PERMISSIONS_INIT = 0; static final int MSG_PERMISSIONS_DESTROY = 1; static final int MSG_PERMISSIONS_CHANGED = 2; + static final int MSG_APPOPS_CHANGED = 3; private @NonNull AppPermissionTracker mTracker; @@ -194,14 +409,19 @@ final class AppPermissionTracker extends BaseAppStateTracker<AppPermissionPolicy public void handleMessage(Message msg) { switch (msg.what) { case MSG_PERMISSIONS_INIT: + mTracker.handleAppOpsInit(); mTracker.handlePermissionsInit(); break; case MSG_PERMISSIONS_DESTROY: mTracker.handlePermissionsDestroy(); + mTracker.handleAppOpsDestroy(); break; case MSG_PERMISSIONS_CHANGED: mTracker.handlePermissionsChanged(msg.arg1); break; + case MSG_APPOPS_CHANGED: + mTracker.handleOpChanged(msg.arg1, msg.arg2, (String) msg.obj); + break; } } } @@ -231,25 +451,41 @@ final class AppPermissionTracker extends BaseAppStateTracker<AppPermissionPolicy void dump(PrintWriter pw, String prefix) { pw.print(prefix); pw.println("APP PERMISSIONS TRACKER:"); - final String[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor(); + final Pair[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor(); final String prefixMore = " " + prefix; final String prefixMoreMore = " " + prefixMore; - for (String permission : permissions) { + for (Pair<String, Integer> permission : permissions) { pw.print(prefixMore); - pw.print(permission); + final boolean emptyPermissionName = TextUtils.isEmpty(permission.first); + if (!emptyPermissionName) { + pw.print(permission.first); + } + if (permission.second != OP_NONE) { + if (!emptyPermissionName) { + pw.print('+'); + } + pw.print(opToPublicName(permission.second)); + } pw.println(':'); synchronized (mLock) { - final SparseArray<ArraySet<String>> uidPerms = mUidGrantedPermissionsInMonitor; + final SparseArray<ArraySet<UidGrantedPermissionState>> uidPerms = + mUidGrantedPermissionsInMonitor; pw.print(prefixMoreMore); pw.print('['); boolean needDelimiter = false; for (int i = 0, size = uidPerms.size(); i < size; i++) { - if (uidPerms.valueAt(i).contains(permission)) { - if (needDelimiter) { - pw.print(','); + final ArraySet<UidGrantedPermissionState> uidPerm = uidPerms.valueAt(i); + for (int j = uidPerm.size() - 1; j >= 0; j--) { + final UidGrantedPermissionState state = uidPerm.valueAt(j); + if (state.mAppOp == permission.second + && TextUtils.equals(state.mPermission, permission.first)) { + if (needDelimiter) { + pw.print(','); + } + needDelimiter = true; + pw.print(UserHandle.formatUid(state.mUid)); + break; } - needDelimiter = true; - pw.print(UserHandle.formatUid(uidPerms.keyAt(i))); } } pw.println(']'); @@ -277,20 +513,25 @@ final class AppPermissionTracker extends BaseAppStateTracker<AppPermissionPolicy static final boolean DEFAULT_BG_PERMISSION_MONITOR_ENABLED = true; /** - * Default value to {@link #mBgPermissionsInMonitor}. + * Default value to {@link #mBgPermissionsInMonitor}, it comes in pair; + * the first string strings in the pair is the permission name, and the second string + * is the appops name, if they are associated. */ static final String[] DEFAULT_BG_PERMISSIONS_IN_MONITOR = new String[] { - ACCESS_FINE_LOCATION, + ACCESS_FINE_LOCATION, OPSTR_FINE_LOCATION, + CAMERA, OPSTR_CAMERA, + RECORD_AUDIO, OPSTR_RECORD_AUDIO, }; /** * @see #KEY_BG_PERMISSIONS_IN_MONITOR. */ - volatile String[] mBgPermissionsInMonitor = DEFAULT_BG_PERMISSIONS_IN_MONITOR; + volatile @NonNull Pair[] mBgPermissionsInMonitor; AppPermissionPolicy(@NonNull Injector injector, @NonNull AppPermissionTracker tracker) { super(injector, tracker, KEY_BG_PERMISSION_MONITOR_ENABLED, DEFAULT_BG_PERMISSION_MONITOR_ENABLED); + mBgPermissionsInMonitor = parsePermissionConfig(DEFAULT_BG_PERMISSIONS_IN_MONITOR); } @Override @@ -311,17 +552,38 @@ final class AppPermissionTracker extends BaseAppStateTracker<AppPermissionPolicy } } - String[] getBgPermissionsInMonitor() { + Pair[] getBgPermissionsInMonitor() { return mBgPermissionsInMonitor; } + private @NonNull Pair[] parsePermissionConfig(@NonNull String[] perms) { + final Pair[] result = new Pair[perms.length / 2]; + for (int i = 0, j = 0; i < perms.length; i += 2, j++) { + try { + result[j] = Pair.create(TextUtils.isEmpty(perms[i]) ? null : perms[i], + TextUtils.isEmpty(perms[i + 1]) ? OP_NONE : strOpToOp(perms[i + 1])); + } catch (Exception e) { + // Ignore. + } + } + return result; + } + private void updateBgPermissionsInMonitor() { final String config = DeviceConfig.getString( DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_BG_PERMISSIONS_IN_MONITOR, null); - mBgPermissionsInMonitor = config != null - ? config.split(",") : DEFAULT_BG_PERMISSIONS_IN_MONITOR; + final Pair[] newPermsInMonitor = parsePermissionConfig( + config != null ? config.split(",") : DEFAULT_BG_PERMISSIONS_IN_MONITOR); + if (!Arrays.equals(mBgPermissionsInMonitor, newPermsInMonitor)) { + mBgPermissionsInMonitor = newPermsInMonitor; + if (isEnabled()) { + // Trigger a reload. + onTrackerEnabled(false); + onTrackerEnabled(true); + } + } } @Override @@ -338,7 +600,21 @@ final class AppPermissionTracker extends BaseAppStateTracker<AppPermissionPolicy pw.print(prefix); pw.print(KEY_BG_PERMISSIONS_IN_MONITOR); pw.print('='); - pw.println(Arrays.toString(mBgPermissionsInMonitor)); + pw.print('['); + for (int i = 0; i < mBgPermissionsInMonitor.length; i++) { + if (i > 0) { + pw.print(','); + } + final Pair<String, Integer> pair = mBgPermissionsInMonitor[i]; + if (pair.first != null) { + pw.print(pair.first); + } + pw.print(','); + if (pair.second != OP_NONE) { + pw.print(opToPublicName(pair.second)); + } + } + pw.println(']'); } } } diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java index dc8403aea1b3..15484b2d618e 100644 --- a/services/core/java/com/android/server/am/AppRestrictionController.java +++ b/services/core/java/com/android/server/am/AppRestrictionController.java @@ -135,6 +135,7 @@ import android.util.Slog; import android.util.SparseArray; import android.util.SparseArrayMap; import android.util.TimeUtils; +import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -1369,6 +1370,12 @@ public final class AppRestrictionController { } } + void dumpAsProto(ProtoOutputStream proto, int uid) { + for (int i = 0, size = mAppStateTrackers.size(); i < size; i++) { + mAppStateTrackers.get(i).dumpAsProto(proto, uid); + } + } + private void applyRestrictionLevel(String pkgName, int uid, @RestrictionLevel int level, int curBucket, boolean allowUpdateBucket, int reason, int subReason) { int curLevel; diff --git a/services/core/java/com/android/server/am/BaseAppStateTracker.java b/services/core/java/com/android/server/am/BaseAppStateTracker.java index 0fada53d622e..570d7e53fadc 100644 --- a/services/core/java/com/android/server/am/BaseAppStateTracker.java +++ b/services/core/java/com/android/server/am/BaseAppStateTracker.java @@ -33,9 +33,12 @@ import android.media.session.MediaSessionManager; import android.os.BatteryManagerInternal; import android.os.BatteryStatsInternal; import android.os.Handler; +import android.os.ServiceManager; import android.permission.PermissionManager; import android.util.Slog; +import android.util.proto.ProtoOutputStream; +import com.android.internal.app.IAppOpsService; import com.android.server.DeviceIdleInternal; import com.android.server.LocalServices; import com.android.server.notification.NotificationManagerInternal; @@ -250,6 +253,9 @@ public abstract class BaseAppStateTracker<T extends BaseAppStatePolicy> { mInjector.getPolicy().dump(pw, " " + prefix); } + void dumpAsProto(ProtoOutputStream proto, int uid) { + } + static class Injector<T extends BaseAppStatePolicy> { T mAppStatePolicy; @@ -266,6 +272,7 @@ public abstract class BaseAppStateTracker<T extends BaseAppStatePolicy> { MediaSessionManager mMediaSessionManager; RoleManager mRoleManager; NotificationManagerInternal mNotificationManagerInternal; + IAppOpsService mIAppOpsService; void setPolicy(T policy) { mAppStatePolicy = policy; @@ -288,6 +295,8 @@ public abstract class BaseAppStateTracker<T extends BaseAppStatePolicy> { mRoleManager = context.getSystemService(RoleManager.class); mNotificationManagerInternal = LocalServices.getService( NotificationManagerInternal.class); + mIAppOpsService = IAppOpsService.Stub.asInterface( + ServiceManager.getService(Context.APP_OPS_SERVICE)); getPolicy().onSystemReady(); } @@ -358,5 +367,9 @@ public abstract class BaseAppStateTracker<T extends BaseAppStatePolicy> { NotificationManagerInternal getNotificationManagerInternal() { return mNotificationManagerInternal; } + + IAppOpsService getIAppOpsService() { + return mIAppOpsService; + } } } diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java index 1131fa8a32b8..4fdc88d3fd64 100644 --- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java +++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java @@ -561,8 +561,18 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { // We were asked to fetch Bluetooth data. final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); if (adapter != null) { - bluetoothReceiver = new SynchronousResultReceiver("bluetooth"); - adapter.requestControllerActivityEnergyInfo(bluetoothReceiver); + SynchronousResultReceiver resultReceiver = + new SynchronousResultReceiver("bluetooth"); + adapter.requestControllerActivityEnergyInfo( + Runnable::run, + info -> { + Bundle bundle = new Bundle(); + bundle.putParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY, + info); + resultReceiver.send(0, bundle); + } + ); + bluetoothReceiver = resultReceiver; } } diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 7ed3dcf40f65..9626bbe0b4b2 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -407,7 +407,6 @@ public class OomAdjuster { uids.clear(); uids.put(uidRec.getUid(), uidRec); updateUidsLSP(uids, SystemClock.elapsedRealtime()); - mProcessList.incrementProcStateSeqAndNotifyAppsLOSP(uids); } } @@ -1144,8 +1143,6 @@ public class OomAdjuster { } } - mProcessList.incrementProcStateSeqAndNotifyAppsLOSP(activeUids); - return mService.mAppProfiler.updateLowMemStateLSP(numCached, numEmpty, numTrimming); } @@ -1180,6 +1177,11 @@ public class OomAdjuster { @GuardedBy({"mService", "mProcLock"}) private void updateUidsLSP(ActiveUids activeUids, final long nowElapsed) { + // This compares previously set procstate to the current procstate in regards to whether + // or not the app's network access will be blocked. So, this needs to be called before + // we update the UidRecord's procstate by calling {@link UidRecord#setSetProcState}. + mProcessList.incrementProcStateSeqAndNotifyAppsLOSP(activeUids); + ArrayList<UidRecord> becameIdle = mTmpBecameIdle; becameIdle.clear(); diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java index e9205230a4ad..67d6d3111955 100644 --- a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java +++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java @@ -85,15 +85,6 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan Slog.w(TAG, "Failed to send connected event", ex); } } - - @Override - public void onDisconnected(@NonNull IGameService service) { - try { - service.disconnected(); - } catch (RemoteException ex) { - Slog.w(TAG, "Failed to send disconnected event", ex); - } - } }; private final ServiceLifecycleCallbacks<IGameSessionService> @@ -181,7 +172,8 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan } @Override - public void onForegroundServicesChanged(int pid, int uid, int serviceTypes) {} + public void onForegroundServicesChanged(int pid, int uid, int serviceTypes) { + } }; private final IGameServiceController mGameServiceController = @@ -338,7 +330,9 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan destroyAndClearAllGameSessionsLocked(); - mGameServiceConnector.unbind(); + mGameServiceConnector.post(IGameService::disconnected).whenComplete((result, t) -> { + mGameServiceConnector.unbind(); + }); mGameSessionServiceConnector.unbind(); mGameServiceConnector.setServiceLifecycleCallbacks(null); diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index d01be5820d34..309a4ff16753 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -102,6 +102,7 @@ import android.media.IRecordingConfigDispatcher; import android.media.IRingtonePlayer; import android.media.ISpatializerCallback; import android.media.ISpatializerHeadToSoundStagePoseCallback; +import android.media.ISpatializerHeadTrackerAvailableCallback; import android.media.ISpatializerHeadTrackingModeCallback; import android.media.ISpatializerOutputCallback; import android.media.IStrategyPreferredDevicesDispatcher; @@ -8723,6 +8724,11 @@ public class AudioService extends IAudioService.Stub return mSpatializerHelper.isHeadTrackerEnabled(Objects.requireNonNull(device)); } + /** @see Spatializer#isHeadTrackerAvailable() */ + public boolean isHeadTrackerAvailable() { + return mSpatializerHelper.isHeadTrackerAvailable(); + } + /** @see Spatializer#setSpatializerEnabled(boolean) */ public void setSpatializerEnabled(boolean enabled) { enforceModifyDefaultAudioEffectsPermission(); @@ -8767,6 +8773,13 @@ public class AudioService extends IAudioService.Stub mSpatializerHelper.unregisterHeadTrackingModeCallback(cb); } + /** @see Spatializer.SpatializerHeadTrackerAvailableDispatcherStub */ + public void registerSpatializerHeadTrackerAvailableCallback( + @NonNull ISpatializerHeadTrackerAvailableCallback cb, boolean register) { + Objects.requireNonNull(cb); + mSpatializerHelper.registerHeadTrackerAvailableCallback(cb, register); + } + /** @see Spatializer#setOnHeadToSoundstagePoseUpdatedListener */ public void registerHeadToSoundstagePoseCallback( @NonNull ISpatializerHeadToSoundStagePoseCallback cb) { diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java index 5af73c9e46ea..f0f04e27b7de 100644 --- a/services/core/java/com/android/server/audio/SpatializerHelper.java +++ b/services/core/java/com/android/server/audio/SpatializerHelper.java @@ -30,6 +30,7 @@ import android.media.INativeSpatializerCallback; import android.media.ISpatializer; import android.media.ISpatializerCallback; import android.media.ISpatializerHeadToSoundStagePoseCallback; +import android.media.ISpatializerHeadTrackerAvailableCallback; import android.media.ISpatializerHeadTrackingCallback; import android.media.ISpatializerHeadTrackingModeCallback; import android.media.ISpatializerOutputCallback; @@ -126,6 +127,7 @@ public class SpatializerHelper { private boolean mBinauralSupported = false; private int mActualHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED; private int mDesiredHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_RELATIVE_WORLD; + private boolean mHeadTrackerAvailable = false; /** * The desired head tracking mode when enabling head tracking, tracks mDesiredHeadTrackingMode, * except when head tracking gets disabled through setting the desired mode to @@ -137,6 +139,7 @@ public class SpatializerHelper { private @Nullable SpatializerCallback mSpatCallback; private @Nullable SpatializerHeadTrackingCallback mSpatHeadTrackingCallback; private @Nullable HelperDynamicSensorCallback mDynSensorCallback; + private boolean mIsHeadTrackingSupported = false; // default attributes and format that determine basic availability of spatialization private static final AudioAttributes DEFAULT_ATTRIBUTES = new AudioAttributes.Builder() @@ -811,8 +814,9 @@ public class SpatializerHelper { mSpat = AudioSystem.getSpatializer(mSpatCallback); try { mSpat.setLevel((byte) Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL); + mIsHeadTrackingSupported = mSpat.isHeadTrackingSupported(); //TODO: register heatracking callback only when sensors are registered - if (mSpat.isHeadTrackingSupported()) { + if (mIsHeadTrackingSupported) { mSpat.registerHeadTrackingCallback(mSpatHeadTrackingCallback); } } catch (RemoteException e) { @@ -830,11 +834,15 @@ public class SpatializerHelper { if (mSpat != null) { mSpatCallback = null; try { - mSpat.registerHeadTrackingCallback(null); + if (mIsHeadTrackingSupported) { + mSpat.registerHeadTrackingCallback(null); + } + mHeadTrackerAvailable = false; mSpat.release(); } catch (RemoteException e) { Log.e(TAG, "Can't set release spatializer cleanly", e); } + mIsHeadTrackingSupported = false; mSpat = null; } } @@ -890,6 +898,18 @@ public class SpatializerHelper { mHeadTrackingModeCallbacks.unregister(callback); } + final RemoteCallbackList<ISpatializerHeadTrackerAvailableCallback> mHeadTrackerCallbacks = + new RemoteCallbackList<>(); + + synchronized void registerHeadTrackerAvailableCallback( + @NonNull ISpatializerHeadTrackerAvailableCallback cb, boolean register) { + if (register) { + mHeadTrackerCallbacks.register(cb); + } else { + mHeadTrackerCallbacks.unregister(cb); + } + } + synchronized int[] getSupportedHeadTrackingModes() { switch (mState) { case STATE_UNINITIALIZED: @@ -1090,6 +1110,10 @@ public class SpatializerHelper { return false; } + synchronized boolean isHeadTrackerAvailable() { + return mHeadTrackerAvailable; + } + private boolean checkSpatForHeadTracking(String funcName) { switch (mState) { case STATE_UNINITIALIZED: @@ -1105,7 +1129,7 @@ public class SpatializerHelper { } break; } - return true; + return mIsHeadTrackingSupported; } private void dispatchActualHeadTrackingMode(int newMode) { @@ -1115,7 +1139,8 @@ public class SpatializerHelper { mHeadTrackingModeCallbacks.getBroadcastItem(i) .dispatchSpatializerActualHeadTrackingModeChanged(newMode); } catch (RemoteException e) { - Log.e(TAG, "Error in dispatchSpatializerActualHeadTrackingModeChanged", e); + Log.e(TAG, "Error in dispatchSpatializerActualHeadTrackingModeChanged(" + + newMode + ")", e); } } mHeadTrackingModeCallbacks.finishBroadcast(); @@ -1128,12 +1153,27 @@ public class SpatializerHelper { mHeadTrackingModeCallbacks.getBroadcastItem(i) .dispatchSpatializerDesiredHeadTrackingModeChanged(newMode); } catch (RemoteException e) { - Log.e(TAG, "Error in dispatchSpatializerDesiredHeadTrackingModeChanged", e); + Log.e(TAG, "Error in dispatchSpatializerDesiredHeadTrackingModeChanged(" + + newMode + ")", e); } } mHeadTrackingModeCallbacks.finishBroadcast(); } + private void dispatchHeadTrackerAvailable(boolean available) { + final int nbCallbacks = mHeadTrackerCallbacks.beginBroadcast(); + for (int i = 0; i < nbCallbacks; i++) { + try { + mHeadTrackerCallbacks.getBroadcastItem(i) + .dispatchSpatializerHeadTrackerAvailable(available); + } catch (RemoteException e) { + Log.e(TAG, "Error in dispatchSpatializerHeadTrackerAvailable(" + + available + ")", e); + } + } + mHeadTrackerCallbacks.finishBroadcast(); + } + //------------------------------------------------------ // head pose final RemoteCallbackList<ISpatializerHeadToSoundStagePoseCallback> mHeadPoseCallbacks = @@ -1279,13 +1319,8 @@ public class SpatializerHelper { Log.e(TAG, "not " + action + " sensors, null spatializer"); return; } - try { - if (!mSpat.isHeadTrackingSupported()) { - Log.e(TAG, "not " + action + " sensors, spatializer doesn't support headtracking"); - return; - } - } catch (RemoteException e) { - Log.e(TAG, "not " + action + " sensors, error querying headtracking", e); + if (!mIsHeadTrackingSupported) { + Log.e(TAG, "not " + action + " sensors, spatializer doesn't support headtracking"); return; } int headHandle = -1; @@ -1348,6 +1383,10 @@ public class SpatializerHelper { try { Log.i(TAG, "setHeadSensor:" + headHandle); mSpat.setHeadSensor(headHandle); + if (mHeadTrackerAvailable != (headHandle != -1)) { + mHeadTrackerAvailable = (headHandle != -1); + dispatchHeadTrackerAvailable(mHeadTrackerAvailable); + } } catch (Exception e) { Log.e(TAG, "Error calling setHeadSensor:" + headHandle, e); } diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java index 1b2e606117e7..1370fd83f6a8 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java +++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java @@ -38,7 +38,7 @@ import java.util.NoSuchElementException; */ public abstract class BaseClientMonitor implements IBinder.DeathRecipient { - private static final String TAG = "Biometrics/ClientMonitor"; + private static final String TAG = "BaseClientMonitor"; protected static final boolean DEBUG = true; // Counter used to distinguish between ClientMonitor instances to help debugging. @@ -120,8 +120,18 @@ public abstract class BaseClientMonitor implements IBinder.DeathRecipient { } /** + * Sets the lifecycle callback before the operation is started via + * {@link #start(ClientMonitorCallback)} when the client must wait for a cookie before starting. + * + * @param callback lifecycle callback (typically same callback used for starting the operation) + */ + public void waitForCookie(@NonNull ClientMonitorCallback callback) { + mCallback = callback; + } + + /** * Starts the ClientMonitor's lifecycle. - * @param callback invoked when the operation is complete (succeeds, fails, etc) + * @param callback invoked when the operation is complete (succeeds, fails, etc.) */ public void start(@NonNull ClientMonitorCallback callback) { mCallback = wrapCallbackForStart(callback); @@ -246,12 +256,12 @@ public abstract class BaseClientMonitor implements IBinder.DeathRecipient { } /** Unique request id. */ - public final long getRequestId() { + public long getRequestId() { return mRequestId; } /** If a unique id has been set via {@link #setRequestId(long)} */ - public final boolean hasRequestId() { + public boolean hasRequestId() { return mRequestId > 0; } diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java index d0ec4470d3e6..19a93f30937f 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java @@ -285,7 +285,7 @@ public class BiometricScheduler { // Not all operations start immediately. BiometricPrompt waits for its operation // to arrive at the head of the queue, before pinging it to start. - final int cookie = mCurrentOperation.isReadyToStart(); + final int cookie = mCurrentOperation.isReadyToStart(mInternalCallback); if (cookie == 0) { if (!mCurrentOperation.start(mInternalCallback)) { // Note down current length of queue @@ -463,6 +463,18 @@ public class BiometricScheduler { return mCurrentOperation != null ? mCurrentOperation.getClientMonitor() : null; } + /** The current operation if the requestId is set and matches. */ + @Deprecated + @Nullable + public BaseClientMonitor getCurrentClientIfMatches(long requestId) { + if (mCurrentOperation != null) { + if (mCurrentOperation.isMatchingRequestId(requestId)) { + return mCurrentOperation.getClientMonitor(); + } + } + return null; + } + public int getCurrentPendingCount() { return mPendingOperations.size(); } diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java index 15f0cadced99..968146a166ed 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java @@ -123,11 +123,12 @@ public class BiometricSchedulerOperation { * * @return cookie or 0 if ready/started */ - public int isReadyToStart() { + public int isReadyToStart(@NonNull ClientMonitorCallback callback) { if (mState == STATE_WAITING_FOR_COOKIE || mState == STATE_WAITING_IN_QUEUE) { final int cookie = mClientMonitor.getCookie(); if (cookie != 0) { mState = STATE_WAITING_FOR_COOKIE; + mClientMonitor.waitForCookie(getWrappedCallback(callback)); } return cookie; } @@ -137,7 +138,7 @@ public class BiometricSchedulerOperation { /** * Start this operation without waiting for a cookie - * (i.e. {@link #isReadyToStart() returns zero} + * (i.e. {@link #isReadyToStart(ClientMonitorCallback)} returns zero} * * @param callback lifecycle callback * @return if this operation started diff --git a/services/core/java/com/android/server/biometrics/sensors/SensorOverlays.java b/services/core/java/com/android/server/biometrics/sensors/SensorOverlays.java index 008717899aba..aeb6b6e2a907 100644 --- a/services/core/java/com/android/server/biometrics/sensors/SensorOverlays.java +++ b/services/core/java/com/android/server/biometrics/sensors/SensorOverlays.java @@ -84,7 +84,8 @@ public final class SensorOverlays { }; try { - mUdfpsOverlayController.get().showUdfpsOverlay(sensorId, reason, callback); + mUdfpsOverlayController.get().showUdfpsOverlay( + client.getRequestId(), sensorId, reason, callback); } catch (RemoteException e) { Slog.e(TAG, "Remote exception when showing the UDFPS overlay", e); } diff --git a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java index 4f900208841e..a0999771a1be 100644 --- a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java +++ b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java @@ -57,30 +57,25 @@ public class UserAwareBiometricScheduler extends BiometricScheduler { @Nullable private StopUserClient<?> mStopUserClient; private class ClientFinishedCallback implements ClientMonitorCallback { - private final BaseClientMonitor mOwner; + @NonNull private final BaseClientMonitor mOwner; - ClientFinishedCallback(BaseClientMonitor owner) { + ClientFinishedCallback(@NonNull BaseClientMonitor owner) { mOwner = owner; } @Override public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) { mHandler.post(() -> { - if (mOwner != clientMonitor) { - Slog.e(getTag(), "[Wrong client finished], actual: " - + clientMonitor + ", expected: " + mOwner); - return; - } - Slog.d(getTag(), "[Client finished] " + clientMonitor + ", success: " + success); if (mCurrentOperation != null && mCurrentOperation.isFor(mOwner)) { mCurrentOperation = null; - startNextOperationIfIdle(); } else { - // can usually be ignored (hal died, etc.) - Slog.d(getTag(), "operation is already null or different (reset?): " + // can happen if the hal dies and is usually okay + // do not unset the current operation that may be newer + Slog.w(getTag(), "operation is already null or different (reset?): " + mCurrentOperation); } + startNextOperationIfIdle(); }); } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index b4befd23671f..e8d8fb828542 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -928,7 +928,8 @@ public class FingerprintService extends SystemService { } @Override - public void onPointerDown(int sensorId, int x, int y, float minor, float major) { + public void onPointerDown(long requestId, int sensorId, int x, int y, + float minor, float major) { Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); final ServiceProvider provider = getProviderForSensor(sensorId); @@ -936,11 +937,11 @@ public class FingerprintService extends SystemService { Slog.w(TAG, "No matching provider for onFingerDown, sensorId: " + sensorId); return; } - provider.onPointerDown(sensorId, x, y, minor, major); + provider.onPointerDown(requestId, sensorId, x, y, minor, major); } @Override - public void onPointerUp(int sensorId) { + public void onPointerUp(long requestId, int sensorId) { Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); final ServiceProvider provider = getProviderForSensor(sensorId); @@ -948,11 +949,11 @@ public class FingerprintService extends SystemService { Slog.w(TAG, "No matching provider for onFingerUp, sensorId: " + sensorId); return; } - provider.onPointerUp(sensorId); + provider.onPointerUp(requestId, sensorId); } @Override - public void onUiReady(int sensorId) { + public void onUiReady(long requestId, int sensorId) { Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); final ServiceProvider provider = getProviderForSensor(sensorId); @@ -960,7 +961,7 @@ public class FingerprintService extends SystemService { Slog.w(TAG, "No matching provider for onUiReady, sensorId: " + sensorId); return; } - provider.onUiReady(sensorId); + provider.onUiReady(requestId, sensorId); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java index 0bdc4ebad66e..9cdbdc9158fb 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java @@ -142,11 +142,11 @@ public interface ServiceProvider { long getAuthenticatorId(int sensorId, int userId); - void onPointerDown(int sensorId, int x, int y, float minor, float major); + void onPointerDown(long requestId, int sensorId, int x, int y, float minor, float major); - void onPointerUp(int sensorId); + void onPointerUp(long requestId, int sensorId); - void onUiReady(int sensorId); + void onUiReady(long requestId, int sensorId); void setUdfpsOverlayController(@NonNull IUdfpsOverlayController controller); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index f810bca9707d..1fac8a8ce5c9 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -580,39 +580,37 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi } @Override - public void onPointerDown(int sensorId, int x, int y, float minor, float major) { + public void onPointerDown(long requestId, int sensorId, int x, int y, + float minor, float major) { final BaseClientMonitor client = - mSensors.get(sensorId).getScheduler().getCurrentClient(); + mSensors.get(sensorId).getScheduler().getCurrentClientIfMatches(requestId); if (!(client instanceof Udfps)) { Slog.e(getTag(), "onPointerDown received during client: " + client); return; } - final Udfps udfps = (Udfps) client; - udfps.onPointerDown(x, y, minor, major); + ((Udfps) client).onPointerDown(x, y, minor, major); } @Override - public void onPointerUp(int sensorId) { + public void onPointerUp(long requestId, int sensorId) { final BaseClientMonitor client = - mSensors.get(sensorId).getScheduler().getCurrentClient(); + mSensors.get(sensorId).getScheduler().getCurrentClientIfMatches(requestId); if (!(client instanceof Udfps)) { Slog.e(getTag(), "onPointerUp received during client: " + client); return; } - final Udfps udfps = (Udfps) client; - udfps.onPointerUp(); + ((Udfps) client).onPointerUp(); } @Override - public void onUiReady(int sensorId) { + public void onUiReady(long requestId, int sensorId) { final BaseClientMonitor client = - mSensors.get(sensorId).getScheduler().getCurrentClient(); + mSensors.get(sensorId).getScheduler().getCurrentClientIfMatches(requestId); if (!(client instanceof Udfps)) { Slog.e(getTag(), "onUiReady received during client: " + client); return; } - final Udfps udfps = (Udfps) client; - udfps.onUiReady(); + ((Udfps) client).onUiReady(); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index 9d60859a4a21..1d2a3655021c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -792,36 +792,34 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider } @Override - public void onPointerDown(int sensorId, int x, int y, float minor, float major) { - final BaseClientMonitor client = mScheduler.getCurrentClient(); + public void onPointerDown(long requestId, int sensorId, int x, int y, + float minor, float major) { + final BaseClientMonitor client = mScheduler.getCurrentClientIfMatches(requestId); if (!(client instanceof Udfps)) { Slog.w(TAG, "onFingerDown received during client: " + client); return; } - final Udfps udfps = (Udfps) client; - udfps.onPointerDown(x, y, minor, major); + ((Udfps) client).onPointerDown(x, y, minor, major); } @Override - public void onPointerUp(int sensorId) { - final BaseClientMonitor client = mScheduler.getCurrentClient(); + public void onPointerUp(long requestId, int sensorId) { + final BaseClientMonitor client = mScheduler.getCurrentClientIfMatches(requestId); if (!(client instanceof Udfps)) { Slog.w(TAG, "onFingerDown received during client: " + client); return; } - final Udfps udfps = (Udfps) client; - udfps.onPointerUp(); + ((Udfps) client).onPointerUp(); } @Override - public void onUiReady(int sensorId) { - final BaseClientMonitor client = mScheduler.getCurrentClient(); + public void onUiReady(long requestId, int sensorId) { + final BaseClientMonitor client = mScheduler.getCurrentClientIfMatches(requestId); if (!(client instanceof Udfps)) { Slog.w(TAG, "onUiReady received during client: " + client); return; } - final Udfps udfps = (Udfps) client; - udfps.onUiReady(); + ((Udfps) client).onUiReady(); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java index 149526f21fdb..a4e343e786c1 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java @@ -441,7 +441,8 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage } @Override - public void onPointerDown(int sensorId, int x, int y, float minor, float major) { + public void onPointerDown(long requestId, int sensorId, int x, int y, float minor, + float major) { mHandler.post(() -> { Slog.d(TAG, "onFingerDown"); final AuthenticationConsumer lastAuthenticatedConsumer = @@ -488,7 +489,7 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage } @Override - public void onPointerUp(int sensorId) { + public void onPointerUp(long requestId, int sensorId) { mHandler.post(() -> { Slog.d(TAG, "onFingerUp"); diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java index 7cb29215b5bf..cb04ddfd636d 100644 --- a/services/core/java/com/android/server/display/ColorFade.java +++ b/services/core/java/com/android/server/display/ColorFade.java @@ -156,9 +156,15 @@ final class ColorFade { mMode = mode; + DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(mDisplayId); + if (displayInfo == null) { + // displayInfo can be null if the associated display has been removed. There + // is a delay between the display being removed and ColorFade being dismissed. + return false; + } + // Get the display size and layer stack. // This is not expected to change while the color fade surface is showing. - DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(mDisplayId); mDisplayLayerStack = displayInfo.layerStack; mDisplayWidth = displayInfo.getNaturalWidth(); mDisplayHeight = displayInfo.getNaturalHeight(); diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 7a0cf4b592e7..540ae8165dbd 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -73,6 +73,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { private final SurfaceControlProxy mSurfaceControlProxy; + private final boolean mIsBootDisplayModeSupported; + // Called with SyncRoot lock held. public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler, Listener listener) { @@ -85,6 +87,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { super(syncRoot, context, handler, listener, TAG); mInjector = injector; mSurfaceControlProxy = mInjector.getSurfaceControlProxy(); + mIsBootDisplayModeSupported = mSurfaceControlProxy.getBootDisplayModeSupport(); } @Override @@ -349,8 +352,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { if (preferredRecord != null) { int preferredModeId = preferredRecord.mMode.getModeId(); - if (mSurfaceControlProxy.getBootDisplayModeSupport() - && mSystemPreferredModeId != preferredModeId) { + if (mIsBootDisplayModeSupported && mSystemPreferredModeId != preferredModeId) { mSystemPreferredModeId = preferredModeId; preferredModeChanged = true; } @@ -900,7 +902,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { } updateDeviceInfoLocked(); - if (!mSurfaceControlProxy.getBootDisplayModeSupport()) { + if (!mIsBootDisplayModeSupported) { return; } if (mUserPreferredModeId == INVALID_MODE_ID) { diff --git a/services/core/java/com/android/server/locales/LocaleManagerService.java b/services/core/java/com/android/server/locales/LocaleManagerService.java index 176c08c8da29..924db6a49eeb 100644 --- a/services/core/java/com/android/server/locales/LocaleManagerService.java +++ b/services/core/java/com/android/server/locales/LocaleManagerService.java @@ -22,12 +22,14 @@ import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.ILocaleManager; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; +import android.content.res.Configuration; import android.os.Binder; import android.os.HandlerThread; import android.os.LocaleList; @@ -154,6 +156,12 @@ public class LocaleManagerService extends SystemService { } @Override + @NonNull + public LocaleList getSystemLocales() throws RemoteException { + return LocaleManagerService.this.getSystemLocales(); + } + + @Override public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver) { @@ -426,6 +434,32 @@ public class LocaleManagerService extends SystemService { return null; } + /** + * Returns the current system locales. + */ + @NonNull + public LocaleList getSystemLocales() throws RemoteException { + final long token = Binder.clearCallingIdentity(); + try { + return getSystemLocalesUnchecked(); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @NonNull + private LocaleList getSystemLocalesUnchecked() throws RemoteException { + LocaleList systemLocales = null; + Configuration conf = ActivityManager.getService().getConfiguration(); + if (conf != null) { + systemLocales = conf.getLocales(); + } + if (systemLocales == null) { + systemLocales = LocaleList.getEmptyLocaleList(); + } + return systemLocales; + } + private void logMetric(@NonNull AppLocaleChangedAtomRecord atomRecordForMetrics) { FrameworkStatsLog.write(FrameworkStatsLog.APPLICATION_LOCALES_CHANGED, atomRecordForMetrics.mCallingUid, diff --git a/services/core/java/com/android/server/locales/TEST_MAPPING b/services/core/java/com/android/server/locales/TEST_MAPPING index 27d2851a1bbe..160542b6aa0f 100644 --- a/services/core/java/com/android/server/locales/TEST_MAPPING +++ b/services/core/java/com/android/server/locales/TEST_MAPPING @@ -9,10 +9,13 @@ ] }, { - "name": "CtsLocaleManagerTestCases" - }, - { "name": "CtsLocaleManagerHostTestCases" } + ], + "postsubmit": [ + // TODO(b/225192026): Move back to presubmit after b/225192026 is fixed + { + "name": "CtsLocaleManagerTestCases" + } ] } diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java index 3ce8e4659737..1937852fa333 100644 --- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java +++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java @@ -45,6 +45,7 @@ import android.os.RemoteException; import android.os.UserHandle; import android.util.ArrayMap; import android.util.Slog; +import android.window.WindowContainerToken; import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; @@ -410,6 +411,7 @@ public final class MediaProjectionManagerService extends SystemService private IBinder mToken; private IBinder.DeathRecipient mDeathEater; private boolean mRestoreSystemAlertWindow; + private WindowContainerToken mTaskRecordingWindowContainerToken = null; MediaProjection(int type, int uid, String packageName, int targetSdkVersion, boolean isPrivileged) { @@ -568,7 +570,7 @@ public final class MediaProjectionManagerService extends SystemService } } - @Override + @Override // Binder call public void registerCallback(IMediaProjectionCallback callback) { if (callback == null) { throw new IllegalArgumentException("callback must not be null"); @@ -576,7 +578,7 @@ public final class MediaProjectionManagerService extends SystemService mCallbackDelegate.add(callback); } - @Override + @Override // Binder call public void unregisterCallback(IMediaProjectionCallback callback) { if (callback == null) { throw new IllegalArgumentException("callback must not be null"); @@ -584,6 +586,17 @@ public final class MediaProjectionManagerService extends SystemService mCallbackDelegate.remove(callback); } + @Override // Binder call + public void setTaskRecordingWindowContainerToken(WindowContainerToken token) { + // TODO(b/221417940) set the task id to record from sysui, for the package chosen. + mTaskRecordingWindowContainerToken = token; + } + + @Override // Binder call + public WindowContainerToken getTaskRecordingWindowContainerToken() { + return mTaskRecordingWindowContainerToken; + } + public MediaProjectionInfo getProjectionInfo() { return new MediaProjectionInfo(packageName, userHandle); } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 35f6a94f3ed6..57ffba70ba2e 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -5421,6 +5421,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { try { mNetworkManager.setUidOnMeteredNetworkDenylist(uid, enable); mLogger.meteredAllowlistChanged(uid, enable); + if (Process.isApplicationUid(uid)) { + final int sdkSandboxUid = Process.toSdkSandboxUid(uid); + mNetworkManager.setUidOnMeteredNetworkDenylist(sdkSandboxUid, enable); + mLogger.meteredAllowlistChanged(sdkSandboxUid, enable); + } } catch (IllegalStateException e) { Log.wtf(TAG, "problem setting denylist (" + enable + ") rules for " + uid, e); } catch (RemoteException e) { @@ -5433,6 +5438,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { try { mNetworkManager.setUidOnMeteredNetworkAllowlist(uid, enable); mLogger.meteredDenylistChanged(uid, enable); + if (Process.isApplicationUid(uid)) { + final int sdkSandboxUid = Process.toSdkSandboxUid(uid); + mNetworkManager.setUidOnMeteredNetworkAllowlist(sdkSandboxUid, enable); + mLogger.meteredDenylistChanged(sdkSandboxUid, enable); + } } catch (IllegalStateException e) { Log.wtf(TAG, "problem setting allowlist (" + enable + ") rules for " + uid, e); } catch (RemoteException e) { @@ -5471,12 +5481,31 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } + private void addSdkSandboxUidsIfNeeded(SparseIntArray uidRules) { + final int size = uidRules.size(); + final SparseIntArray sdkSandboxUids = new SparseIntArray(); + for (int index = 0; index < size; index++) { + final int uid = uidRules.keyAt(index); + final int rule = uidRules.valueAt(index); + if (Process.isApplicationUid(uid)) { + sdkSandboxUids.put(Process.toSdkSandboxUid(uid), rule); + } + } + + for (int index = 0; index < sdkSandboxUids.size(); index++) { + final int uid = sdkSandboxUids.keyAt(index); + final int rule = sdkSandboxUids.valueAt(index); + uidRules.put(uid, rule); + } + } + /** * Set uid rules on a particular firewall chain. This is going to synchronize the rules given * here to netd. It will clean up dead rules and make sure the target chain only contains rules * specified here. */ private void setUidFirewallRulesUL(int chain, SparseIntArray uidRules) { + addSdkSandboxUidsIfNeeded(uidRules); try { int size = uidRules.size(); int[] uids = new int[size]; @@ -5519,6 +5548,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { try { mNetworkManager.setFirewallUidRule(chain, uid, rule); mLogger.uidFirewallRuleChanged(chain, uid, rule); + if (Process.isApplicationUid(uid)) { + final int sdkSandboxUid = Process.toSdkSandboxUid(uid); + mNetworkManager.setFirewallUidRule(chain, sdkSandboxUid, rule); + mLogger.uidFirewallRuleChanged(chain, sdkSandboxUid, rule); + } } catch (IllegalStateException e) { Log.wtf(TAG, "problem setting firewall uid rules", e); } catch (RemoteException e) { @@ -5555,15 +5589,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { */ private void resetUidFirewallRules(int uid) { try { - mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_DOZABLE, uid, FIREWALL_RULE_DEFAULT); - mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DEFAULT); - mNetworkManager - .setFirewallUidRule(FIREWALL_CHAIN_POWERSAVE, uid, FIREWALL_RULE_DEFAULT); - mNetworkManager - .setFirewallUidRule(FIREWALL_CHAIN_RESTRICTED, uid, FIREWALL_RULE_DEFAULT); - mNetworkManager - .setFirewallUidRule(FIREWALL_CHAIN_LOW_POWER_STANDBY, uid, - FIREWALL_RULE_DEFAULT); + mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_DOZABLE, uid, + FIREWALL_RULE_DEFAULT); + mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_STANDBY, uid, + FIREWALL_RULE_DEFAULT); + mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_POWERSAVE, uid, + FIREWALL_RULE_DEFAULT); + mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_RESTRICTED, uid, + FIREWALL_RULE_DEFAULT); + mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_LOW_POWER_STANDBY, uid, + FIREWALL_RULE_DEFAULT); mNetworkManager.setUidOnMeteredNetworkAllowlist(uid, false); mLogger.meteredAllowlistChanged(uid, false); mNetworkManager.setUidOnMeteredNetworkDenylist(uid, false); @@ -5573,6 +5608,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } catch (RemoteException e) { // ignored; service lives in system_server } + if (Process.isApplicationUid(uid)) { + resetUidFirewallRules(Process.toSdkSandboxUid(uid)); + } } @Deprecated diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java index 9ff4aab83cff..d26a1ac4fba0 100644 --- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java +++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java @@ -253,7 +253,8 @@ public final class BackgroundDexOptService { * * <p>This is only for shell command and only root or shell user can use this. * - * @param packageNames dex optimize the passed packages or all packages if null + * @param packageNames dex optimize the passed packages in the given order, or all packages in + * the default order if null * * @return true if dex optimization is complete. false if the task is cancelled or if there was * an error. @@ -268,11 +269,11 @@ public final class BackgroundDexOptService { resetStatesForNewDexOptRunLocked(Thread.currentThread()); } PackageManagerService pm = mInjector.getPackageManagerService(); - ArraySet<String> packagesToOptimize; + List<String> packagesToOptimize; if (packageNames == null) { packagesToOptimize = mDexOptHelper.getOptimizablePackages(pm.snapshotComputer()); } else { - packagesToOptimize = new ArraySet<>(packageNames); + packagesToOptimize = packageNames; } return runIdleOptimization(pm, packagesToOptimize, /* isPostBootUpdate= */ false); } finally { @@ -335,7 +336,7 @@ public final class BackgroundDexOptService { return false; } - ArraySet<String> pkgs = mDexOptHelper.getOptimizablePackages(pm.snapshotComputer()); + List<String> pkgs = mDexOptHelper.getOptimizablePackages(pm.snapshotComputer()); if (pkgs.isEmpty()) { Slog.i(TAG, "No packages to optimize"); markPostBootUpdateCompleted(params); @@ -525,7 +526,7 @@ public final class BackgroundDexOptService { } /** Returns true if completed */ - private boolean runIdleOptimization(PackageManagerService pm, ArraySet<String> pkgs, + private boolean runIdleOptimization(PackageManagerService pm, List<String> pkgs, boolean isPostBootUpdate) { synchronized (mLock) { mLastExecutionStartTimeMs = SystemClock.elapsedRealtime(); @@ -581,10 +582,9 @@ public final class BackgroundDexOptService { } @Status - private int idleOptimizePackages(PackageManagerService pm, ArraySet<String> pkgs, + private int idleOptimizePackages(PackageManagerService pm, List<String> pkgs, long lowStorageThreshold, boolean isPostBootUpdate) { ArraySet<String> updatedPackages = new ArraySet<>(); - ArraySet<String> updatedPackagesDueToSecondaryDex = new ArraySet<>(); try { boolean supportSecondaryDex = mInjector.supportSecondaryDex(); @@ -640,25 +640,12 @@ public final class BackgroundDexOptService { } } - pkgs = new ArraySet<>(pkgs); + pkgs = new ArrayList<>(pkgs); pkgs.removeAll(unusedPackages); } } - @Status int primaryResult = optimizePackages(pkgs, lowStorageThreshold, - /*isForPrimaryDex=*/ true, updatedPackages, isPostBootUpdate); - if (primaryResult != STATUS_OK) { - return primaryResult; - } - - if (!supportSecondaryDex) { - return STATUS_OK; - } - - @Status int secondaryResult = optimizePackages(pkgs, lowStorageThreshold, - /*isForPrimaryDex*/ false, updatedPackagesDueToSecondaryDex, - isPostBootUpdate); - return secondaryResult; + return optimizePackages(pkgs, lowStorageThreshold, updatedPackages, isPostBootUpdate); } finally { // Always let the pinner service know about changes. notifyPinService(updatedPackages); @@ -670,8 +657,10 @@ public final class BackgroundDexOptService { } @Status - private int optimizePackages(ArraySet<String> pkgs, long lowStorageThreshold, - boolean isForPrimaryDex, ArraySet<String> updatedPackages, boolean isPostBootUpdate) { + private int optimizePackages(List<String> pkgs, long lowStorageThreshold, + ArraySet<String> updatedPackages, boolean isPostBootUpdate) { + boolean supportSecondaryDex = mInjector.supportSecondaryDex(); + for (String pkg : pkgs) { int abortCode = abortIdleOptimizations(lowStorageThreshold); if (abortCode != STATUS_OK) { @@ -679,11 +668,23 @@ public final class BackgroundDexOptService { return abortCode; } - @DexOptResult int result = optimizePackage(pkg, isForPrimaryDex, isPostBootUpdate); - if (result == PackageDexOptimizer.DEX_OPT_PERFORMED) { + @DexOptResult int primaryResult = + optimizePackage(pkg, true /* isForPrimaryDex */, isPostBootUpdate); + if (primaryResult == PackageDexOptimizer.DEX_OPT_PERFORMED) { updatedPackages.add(pkg); - } else if (result != PackageDexOptimizer.DEX_OPT_SKIPPED) { - return convertPackageDexOptimizerStatusToInternal(result); + } else if (primaryResult != PackageDexOptimizer.DEX_OPT_SKIPPED) { + return convertPackageDexOptimizerStatusToInternal(primaryResult); + } + + if (!supportSecondaryDex) { + continue; + } + + @DexOptResult int secondaryResult = + optimizePackage(pkg, false /* isForPrimaryDex */, isPostBootUpdate); + if (secondaryResult != PackageDexOptimizer.DEX_OPT_PERFORMED + && secondaryResult != PackageDexOptimizer.DEX_OPT_SKIPPED) { + return convertPackageDexOptimizerStatusToInternal(secondaryResult); } } return STATUS_OK; diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java index 50b2e2321886..bb2ba5cc498d 100644 --- a/services/core/java/com/android/server/pm/DexOptHelper.java +++ b/services/core/java/com/android/server/pm/DexOptHelper.java @@ -293,8 +293,8 @@ final class DexOptHelper { MetricsLogger.histogram(mPm.mContext, "opt_dialog_time_s", elapsedTimeSeconds); } - public ArraySet<String> getOptimizablePackages(@NonNull Computer snapshot) { - ArraySet<String> pkgs = new ArraySet<>(); + public List<String> getOptimizablePackages(@NonNull Computer snapshot) { + ArrayList<String> pkgs = new ArrayList<>(); mPm.forEachPackageState(snapshot, packageState -> { final AndroidPackage pkg = packageState.getPkg(); if (pkg != null && mPm.mPackageDexOptimizer.canOptimizePackage(pkg)) { diff --git a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java b/services/core/java/com/android/server/pm/InitAppsHelper.java index 91750dee7688..15f26e7a6d6c 100644 --- a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java +++ b/services/core/java/com/android/server/pm/InitAppsHelper.java @@ -32,6 +32,7 @@ import static com.android.server.pm.PackageManagerService.SYSTEM_PARTITIONS; import static com.android.server.pm.PackageManagerService.TAG; import static com.android.server.pm.pkg.parsing.ParsingPackageUtils.PARSE_CHECK_MAX_SDK_VERSION; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.pm.parsing.ApkLiteParseUtils; import android.os.Environment; @@ -61,14 +62,24 @@ import java.util.concurrent.ExecutorService; * further cleanup and eventually all the installation/scanning related logic will go to another * class. */ -final class InitAndSystemPackageHelper { +final class InitAppsHelper { private final PackageManagerService mPm; - private final List<ScanPartition> mDirsToScanAsSystem; private final int mScanFlags; private final int mSystemParseFlags; private final int mSystemScanFlags; private final InstallPackageHelper mInstallPackageHelper; + private final ApexManager mApexManager; + private final ExecutorService mExecutorService; + /* Tracks how long system scan took */ + private long mSystemScanTime; + /* Track of the number of cached system apps */ + private int mCachedSystemApps; + /* Track of the number of system apps */ + private int mSystemPackagesCount; + private final boolean mIsDeviceUpgrading; + private final boolean mIsOnlyCoreApps; + private final List<ScanPartition> mSystemPartitions; /** * Tracks new system packages [received in an OTA] that we expect to @@ -76,21 +87,33 @@ final class InitAndSystemPackageHelper { * are package location. */ private final ArrayMap<String, File> mExpectingBetter = new ArrayMap<>(); + /* Tracks of any system packages that no longer exist that needs to be pruned. */ + private final List<String> mPossiblyDeletedUpdatedSystemApps = new ArrayList<>(); + // Tracks of stub packages that must either be replaced with full versions in the /data + // partition or be disabled. + private final List<String> mStubSystemApps = new ArrayList<>(); // TODO(b/198166813): remove PMS dependency - InitAndSystemPackageHelper(PackageManagerService pm) { + InitAppsHelper(PackageManagerService pm, ApexManager apexManager, + InstallPackageHelper installPackageHelper, + List<ScanPartition> systemPartitions) { mPm = pm; - mInstallPackageHelper = new InstallPackageHelper(pm); + mApexManager = apexManager; + mInstallPackageHelper = installPackageHelper; + mSystemPartitions = systemPartitions; mDirsToScanAsSystem = getSystemScanPartitions(); + mIsDeviceUpgrading = mPm.isDeviceUpgrading(); + mIsOnlyCoreApps = mPm.isOnlyCoreApps(); // Set flag to monitor and not change apk file paths when scanning install directories. int scanFlags = SCAN_BOOTING | SCAN_INITIAL; - if (mPm.isDeviceUpgrading() || mPm.isFirstBoot()) { + if (mIsDeviceUpgrading || mPm.isFirstBoot()) { mScanFlags = scanFlags | SCAN_FIRST_BOOT_OR_UPGRADE; } else { mScanFlags = scanFlags; } mSystemParseFlags = mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR; mSystemScanFlags = scanFlags | SCAN_AS_SYSTEM; + mExecutorService = ParallelPackageParser.makeExecutorService(); } private List<File> getFrameworkResApkSplitFiles() { @@ -118,7 +141,7 @@ final class InitAndSystemPackageHelper { private List<ScanPartition> getSystemScanPartitions() { final List<ScanPartition> scanPartitions = new ArrayList<>(); - scanPartitions.addAll(mPm.mInjector.getSystemPartitions()); + scanPartitions.addAll(mSystemPartitions); scanPartitions.addAll(getApexScanPartitions()); Slog.d(TAG, "Directories scanned as system partitions: " + scanPartitions); return scanPartitions; @@ -126,8 +149,7 @@ final class InitAndSystemPackageHelper { private List<ScanPartition> getApexScanPartitions() { final List<ScanPartition> scanPartitions = new ArrayList<>(); - final List<ApexManager.ActiveApexInfo> activeApexInfos = - mPm.mApexManager.getActiveApexInfos(); + final List<ApexManager.ActiveApexInfo> activeApexInfos = mApexManager.getActiveApexInfos(); for (int i = 0; i < activeApexInfos.size(); i++) { final ScanPartition scanPartition = resolveApexToScanPartition(activeApexInfos.get(i)); if (scanPartition != null) { @@ -144,117 +166,134 @@ final class InitAndSystemPackageHelper { if (apexInfo.preInstalledApexPath.getAbsolutePath().equals( sp.getFolder().getAbsolutePath()) || apexInfo.preInstalledApexPath.getAbsolutePath().startsWith( - sp.getFolder().getAbsolutePath() + File.separator)) { + sp.getFolder().getAbsolutePath() + File.separator)) { return new ScanPartition(apexInfo.apexDirectory, sp, SCAN_AS_APK_IN_APEX); } } return null; } - public OverlayConfig initPackages( - WatchedArrayMap<String, PackageSetting> packageSettings, int[] userIds, - long startTime) { - PackageParser2 packageParser = mPm.mInjector.getScanningCachingPackageParser(); - - ExecutorService executorService = ParallelPackageParser.makeExecutorService(); + /** + * Install apps from system dirs. + */ + @GuardedBy({"mPm.mInstallLock", "mPm.mLock"}) + public OverlayConfig initSystemApps(PackageParser2 packageParser, + WatchedArrayMap<String, PackageSetting> packageSettings, + int[] userIds, long startTime) { // Prepare apex package info before scanning APKs, this information is needed when // scanning apk in apex. - mPm.mApexManager.scanApexPackagesTraced(packageParser, executorService); + mApexManager.scanApexPackagesTraced(packageParser, mExecutorService); - scanSystemDirs(packageParser, executorService); + scanSystemDirs(packageParser, mExecutorService); // Parse overlay configuration files to set default enable state, mutability, and // priority of system overlays. final ArrayMap<String, File> apkInApexPreInstalledPaths = new ArrayMap<>(); - for (ApexManager.ActiveApexInfo apexInfo : mPm.mApexManager.getActiveApexInfos()) { - for (String packageName : mPm.mApexManager.getApksInApex(apexInfo.apexModuleName)) { + for (ApexManager.ActiveApexInfo apexInfo : mApexManager.getActiveApexInfos()) { + for (String packageName : mApexManager.getApksInApex(apexInfo.apexModuleName)) { apkInApexPreInstalledPaths.put(packageName, apexInfo.preInstalledApexPath); } } - OverlayConfig overlayConfig = OverlayConfig.initializeSystemInstance( + final OverlayConfig overlayConfig = OverlayConfig.initializeSystemInstance( consumer -> mPm.forEachPackage(mPm.snapshotComputer(), pkg -> consumer.accept(pkg, pkg.isSystem(), - apkInApexPreInstalledPaths.get(pkg.getPackageName())))); - // Prune any system packages that no longer exist. - final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>(); - // Stub packages must either be replaced with full versions in the /data - // partition or be disabled. - final List<String> stubSystemApps = new ArrayList<>(); - - if (!mPm.isOnlyCoreApps()) { + apkInApexPreInstalledPaths.get(pkg.getPackageName())))); + + if (!mIsOnlyCoreApps) { // do this first before mucking with mPackages for the "expecting better" case - updateStubSystemAppsList(stubSystemApps); + updateStubSystemAppsList(mStubSystemApps); mInstallPackageHelper.prepareSystemPackageCleanUp(packageSettings, - possiblyDeletedUpdatedSystemApps, mExpectingBetter, userIds); + mPossiblyDeletedUpdatedSystemApps, mExpectingBetter, userIds); } - final int cachedSystemApps = PackageCacher.sCachedPackageReadCount.get(); + logSystemAppsScanningTime(startTime); + return overlayConfig; + } + + @GuardedBy({"mPm.mInstallLock", "mPm.mLock"}) + private void logSystemAppsScanningTime(long startTime) { + mCachedSystemApps = PackageCacher.sCachedPackageReadCount.get(); // Remove any shared userIDs that have no associated packages mPm.mSettings.pruneSharedUsersLPw(); - final long systemScanTime = SystemClock.uptimeMillis() - startTime; - final int systemPackagesCount = mPm.mPackages.size(); - Slog.i(TAG, "Finished scanning system apps. Time: " + systemScanTime - + " ms, packageCount: " + systemPackagesCount + mSystemScanTime = SystemClock.uptimeMillis() - startTime; + mSystemPackagesCount = mPm.mPackages.size(); + Slog.i(TAG, "Finished scanning system apps. Time: " + mSystemScanTime + + " ms, packageCount: " + mSystemPackagesCount + " , timePerPackage: " - + (systemPackagesCount == 0 ? 0 : systemScanTime / systemPackagesCount) - + " , cached: " + cachedSystemApps); - if (mPm.isDeviceUpgrading() && systemPackagesCount > 0) { + + (mSystemPackagesCount == 0 ? 0 : mSystemScanTime / mSystemPackagesCount) + + " , cached: " + mCachedSystemApps); + if (mIsDeviceUpgrading && mSystemPackagesCount > 0) { //CHECKSTYLE:OFF IndentationCheck FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED, BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME, - systemScanTime / systemPackagesCount); + mSystemScanTime / mSystemPackagesCount); //CHECKSTYLE:ON IndentationCheck } + } - if (!mPm.isOnlyCoreApps()) { + /** + * Install apps/updates from data dir and fix system apps that are affected. + */ + @GuardedBy({"mPm.mInstallLock", "mPm.mLock"}) + public void initNonSystemApps(PackageParser2 packageParser, @NonNull int[] userIds, + long startTime) { + if (!mIsOnlyCoreApps) { EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START, SystemClock.uptimeMillis()); scanDirTracedLI(mPm.getAppInstallDir(), /* frameworkSplits= */ null, 0, - mScanFlags | SCAN_REQUIRE_KNOWN, 0, - packageParser, executorService); - + mScanFlags | SCAN_REQUIRE_KNOWN, + packageParser, mExecutorService); } - List<Runnable> unfinishedTasks = executorService.shutdownNow(); + List<Runnable> unfinishedTasks = mExecutorService.shutdownNow(); if (!unfinishedTasks.isEmpty()) { throw new IllegalStateException("Not all tasks finished before calling close: " + unfinishedTasks); } - - if (!mPm.isOnlyCoreApps()) { - mInstallPackageHelper.cleanupDisabledPackageSettings(possiblyDeletedUpdatedSystemApps, - userIds, mScanFlags); - mInstallPackageHelper.checkExistingBetterPackages(mExpectingBetter, - stubSystemApps, mSystemScanFlags, mSystemParseFlags); - - // Uncompress and install any stubbed system applications. - // This must be done last to ensure all stubs are replaced or disabled. - mInstallPackageHelper.installSystemStubPackages(stubSystemApps, mScanFlags); - - final int cachedNonSystemApps = PackageCacher.sCachedPackageReadCount.get() - - cachedSystemApps; - - final long dataScanTime = SystemClock.uptimeMillis() - systemScanTime - startTime; - final int dataPackagesCount = mPm.mPackages.size() - systemPackagesCount; - Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime - + " ms, packageCount: " + dataPackagesCount - + " , timePerPackage: " - + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount) - + " , cached: " + cachedNonSystemApps); - if (mPm.isDeviceUpgrading() && dataPackagesCount > 0) { - //CHECKSTYLE:OFF IndentationCheck - FrameworkStatsLog.write( - FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED, - BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME, - dataScanTime / dataPackagesCount); - //CHECKSTYLE:OFF IndentationCheck - } + if (!mIsOnlyCoreApps) { + fixSystemPackages(userIds); + logNonSystemAppScanningTime(startTime); } mExpectingBetter.clear(); - mPm.mSettings.pruneRenamedPackagesLPw(); - packageParser.close(); - return overlayConfig; + } + + /** + * Clean up system packages now that some system package updates have been installed from + * the data dir. Also install system stub packages as the last step. + */ + @GuardedBy({"mPm.mInstallLock", "mPm.mLock"}) + private void fixSystemPackages(@NonNull int[] userIds) { + mInstallPackageHelper.cleanupDisabledPackageSettings(mPossiblyDeletedUpdatedSystemApps, + userIds, mScanFlags); + mInstallPackageHelper.checkExistingBetterPackages(mExpectingBetter, + mStubSystemApps, mSystemScanFlags, mSystemParseFlags); + + // Uncompress and install any stubbed system applications. + // This must be done last to ensure all stubs are replaced or disabled. + mInstallPackageHelper.installSystemStubPackages(mStubSystemApps, mScanFlags); + } + + @GuardedBy({"mPm.mInstallLock", "mPm.mLock"}) + private void logNonSystemAppScanningTime(long startTime) { + final int cachedNonSystemApps = PackageCacher.sCachedPackageReadCount.get() + - mCachedSystemApps; + + final long dataScanTime = SystemClock.uptimeMillis() - mSystemScanTime - startTime; + final int dataPackagesCount = mPm.mPackages.size() - mSystemPackagesCount; + Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime + + " ms, packageCount: " + dataPackagesCount + + " , timePerPackage: " + + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount) + + " , cached: " + cachedNonSystemApps); + if (mIsDeviceUpgrading && dataPackagesCount > 0) { + //CHECKSTYLE:OFF IndentationCheck + FrameworkStatsLog.write( + FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED, + BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME, + dataScanTime / dataPackagesCount); + //CHECKSTYLE:OFF IndentationCheck + } } /** @@ -274,13 +313,13 @@ final class InitAndSystemPackageHelper { continue; } scanDirTracedLI(partition.getOverlayFolder(), /* frameworkSplits= */ null, - mSystemParseFlags, mSystemScanFlags | partition.scanFlag, 0, + mSystemParseFlags, mSystemScanFlags | partition.scanFlag, packageParser, executorService); } scanDirTracedLI(frameworkDir, null, mSystemParseFlags, - mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0, + mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, packageParser, executorService); if (!mPm.mPackages.containsKey("android")) { throw new IllegalStateException( @@ -292,11 +331,11 @@ final class InitAndSystemPackageHelper { if (partition.getPrivAppFolder() != null) { scanDirTracedLI(partition.getPrivAppFolder(), /* frameworkSplits= */ null, mSystemParseFlags, - mSystemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0, + mSystemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, packageParser, executorService); } scanDirTracedLI(partition.getAppFolder(), /* frameworkSplits= */ null, - mSystemParseFlags, mSystemScanFlags | partition.scanFlag, 0, + mSystemParseFlags, mSystemScanFlags | partition.scanFlag, packageParser, executorService); } } @@ -315,7 +354,7 @@ final class InitAndSystemPackageHelper { @GuardedBy({"mPm.mInstallLock", "mPm.mLock"}) private void scanDirTracedLI(File scanDir, List<File> frameworkSplits, int parseFlags, int scanFlags, - long currentTime, PackageParser2 packageParser, ExecutorService executorService) { + PackageParser2 packageParser, ExecutorService executorService) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]"); try { if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) { @@ -323,7 +362,7 @@ final class InitAndSystemPackageHelper { parseFlags |= PARSE_CHECK_MAX_SDK_VERSION; } mInstallPackageHelper.installPackagesFromDir(scanDir, frameworkSplits, parseFlags, - scanFlags, currentTime, packageParser, executorService); + scanFlags, packageParser, executorService); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index b39b24f6defa..870a11ac2d61 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -3062,7 +3062,7 @@ final class InstallPackageHelper { final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm); removePackageHelper.removePackageLI(stubPkg, true /*chatty*/); try { - return scanSystemPackageTracedLI(scanFile, parseFlags, scanFlags, 0, null); + return scanSystemPackageTracedLI(scanFile, parseFlags, scanFlags, null); } catch (PackageManagerException e) { Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.getPackageName(), e); @@ -3194,7 +3194,7 @@ final class InstallPackageHelper { | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR; @PackageManagerService.ScanFlags int scanFlags = mPm.getSystemPackageScanFlags(codePath); final AndroidPackage pkg = scanSystemPackageTracedLI( - codePath, parseFlags, scanFlags, 0 /*currentTime*/, null); + codePath, parseFlags, scanFlags, null); PackageSetting pkgSetting = mPm.mSettings.getPackageLPr(pkg.getPackageName()); @@ -3368,7 +3368,7 @@ final class InstallPackageHelper { mRemovePackageHelper.removePackageLI(pkg, true); try { final File codePath = new File(pkg.getPath()); - scanSystemPackageTracedLI(codePath, 0, scanFlags, 0, null); + scanSystemPackageTracedLI(codePath, 0, scanFlags, null); } catch (PackageManagerException e) { Slog.e(TAG, "Failed to parse updated, ex-system package: " + e.getMessage()); @@ -3389,7 +3389,7 @@ final class InstallPackageHelper { @GuardedBy({"mPm.mInstallLock", "mPm.mLock"}) public void installPackagesFromDir(File scanDir, List<File> frameworkSplits, int parseFlags, - int scanFlags, long currentTime, PackageParser2 packageParser, + int scanFlags, PackageParser2 packageParser, ExecutorService executorService) { final File[] files = scanDir.listFiles(); if (ArrayUtils.isEmpty(files)) { @@ -3432,7 +3432,7 @@ final class InstallPackageHelper { parseResult.parsedPackage); } try { - addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags, currentTime, + addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags, null); } catch (PackageManagerException e) { errorCode = e.error; @@ -3495,7 +3495,7 @@ final class InstallPackageHelper { try { final AndroidPackage newPkg = scanSystemPackageTracedLI( - scanFile, reparseFlags, rescanFlags, 0, null); + scanFile, reparseFlags, rescanFlags, null); // We rescanned a stub, add it to the list of stubbed system packages if (newPkg.isStub()) { stubSystemApps.add(packageName); @@ -3509,14 +3509,14 @@ final class InstallPackageHelper { /** * Traces a package scan. - * @see #scanSystemPackageLI(File, int, int, long, UserHandle) + * @see #scanSystemPackageLI(File, int, int, UserHandle) */ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"}) public AndroidPackage scanSystemPackageTracedLI(File scanFile, final int parseFlags, - int scanFlags, long currentTime, UserHandle user) throws PackageManagerException { + int scanFlags, UserHandle user) throws PackageManagerException { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage [" + scanFile.toString() + "]"); try { - return scanSystemPackageLI(scanFile, parseFlags, scanFlags, currentTime, user); + return scanSystemPackageLI(scanFile, parseFlags, scanFlags, user); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } @@ -3528,7 +3528,7 @@ final class InstallPackageHelper { */ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"}) private AndroidPackage scanSystemPackageLI(File scanFile, int parseFlags, int scanFlags, - long currentTime, UserHandle user) throws PackageManagerException { + UserHandle user) throws PackageManagerException { if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage"); @@ -3544,7 +3544,7 @@ final class InstallPackageHelper { PackageManagerService.renameStaticSharedLibraryPackage(parsedPackage); } - return addForInitLI(parsedPackage, parseFlags, scanFlags, currentTime, user); + return addForInitLI(parsedPackage, parseFlags, scanFlags, user); } /** @@ -3563,11 +3563,11 @@ final class InstallPackageHelper { @GuardedBy({"mPm.mLock", "mPm.mInstallLock"}) private AndroidPackage addForInitLI(ParsedPackage parsedPackage, @ParsingPackageUtils.ParseFlags int parseFlags, - @PackageManagerService.ScanFlags int scanFlags, long currentTime, + @PackageManagerService.ScanFlags int scanFlags, @Nullable UserHandle user) throws PackageManagerException { final Pair<ScanResult, Boolean> scanResultPair = scanSystemPackageLI( - parsedPackage, parseFlags, scanFlags, currentTime, user); + parsedPackage, parseFlags, scanFlags, user); final ScanResult scanResult = scanResultPair.first; boolean shouldHideSystemApp = scanResultPair.second; if (scanResult.mSuccess) { @@ -3762,7 +3762,7 @@ final class InstallPackageHelper { private Pair<ScanResult, Boolean> scanSystemPackageLI(ParsedPackage parsedPackage, @ParsingPackageUtils.ParseFlags int parseFlags, - @PackageManagerService.ScanFlags int scanFlags, long currentTime, + @PackageManagerService.ScanFlags int scanFlags, @Nullable UserHandle user) throws PackageManagerException { final boolean scanSystemPartition = (parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0; @@ -3950,7 +3950,7 @@ final class InstallPackageHelper { } final ScanResult scanResult = scanPackageNewLI(parsedPackage, parseFlags, - scanFlags | SCAN_UPDATE_SIGNATURE, currentTime, user, null); + scanFlags | SCAN_UPDATE_SIGNATURE, 0 /* currentTime */, user, null); return new Pair<>(scanResult, shouldHideSystemApp); } diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index 5902f487d353..45c5116bea59 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -89,6 +89,14 @@ public class Installer extends SystemService { */ public static final int PROFILE_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES = 3; + /** + * The results of {@code getOdexVisibility}. See + * {@link #getOdexVisibility(String, String, String)} for details. + */ + public static final int ODEX_NOT_FOUND = 0; + public static final int ODEX_IS_PUBLIC = 1; + public static final int ODEX_IS_PRIVATE = 2; + public static final int FLAG_STORAGE_DE = IInstalld.FLAG_STORAGE_DE; public static final int FLAG_STORAGE_CE = IInstalld.FLAG_STORAGE_CE; @@ -866,6 +874,15 @@ public class Installer extends SystemService { } } + /** + * Prepares the app profile for the package at the given path: + * <ul> + * <li>Creates the current profile for the given user ID, unless the user ID is + * {@code UserHandle.USER_NULL}.</li> + * <li>Merges the profile from the dex metadata file (if present) into the reference + * profile.</li> + * </ul> + */ public boolean prepareAppProfile(String pkg, @UserIdInt int userId, @AppIdInt int appId, String profileName, String codePath, String dexMetadataPath) throws InstallerException { if (!checkBeforeRemote()) return false; @@ -1016,6 +1033,33 @@ public class Installer extends SystemService { } } + /** + * Returns the visibility of the optimized artifacts. + * + * @param packageName name of the package. + * @param apkPath path to the APK. + * @param instructionSet instruction set of the optimized artifacts. + * @param outputPath path to the directory that contains the optimized artifacts (i.e., the + * directory that {@link #dexopt} outputs to). + * + * @return {@link #ODEX_NOT_FOUND} if the optimized artifacts are not found, or + * {@link #ODEX_IS_PUBLIC} if the optimized artifacts are accessible by all apps, or + * {@link #ODEX_IS_PRIVATE} if the optimized artifacts are only accessible by this app. + * + * @throws InstallerException if failed to get the visibility of the optimized artifacts. + */ + public int getOdexVisibility(String packageName, String apkPath, String instructionSet, + String outputPath) throws InstallerException { + if (!checkBeforeRemote()) return -1; + BlockGuard.getVmPolicy().onPathAccess(apkPath); + BlockGuard.getVmPolicy().onPathAccess(outputPath); + try { + return mInstalld.getOdexVisibility(packageName, apkPath, instructionSet, outputPath); + } catch (Exception e) { + throw InstallerException.from(e); + } + } + public static class InstallerException extends Exception { public InstallerException(String detailMessage) { super(detailMessage); diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index 69d498794e64..27c6d9bec2e8 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -319,28 +319,42 @@ public class PackageDexOptimizer { String profileName = ArtManager.getProfileName( i == 0 ? null : pkg.getSplitNames()[i - 1]); + final boolean isUsedByOtherApps = options.isDexoptAsSharedLibrary() + || packageUseInfo.isUsedByOtherApps(path); + String compilerFilter = getRealCompilerFilter(pkg, options.getCompilerFilter()); + // If the app is used by other apps, we must not use the existing profile because it + // may contain user data, unless the profile is newly created on install. + final boolean resetProfile = isProfileGuidedCompilerFilter(compilerFilter) + && isUsedByOtherApps + && options.getCompilationReason() != PackageManagerService.REASON_INSTALL; String dexMetadataPath = null; - if (options.isDexoptInstallWithDexMetadata()) { + if (options.isDexoptInstallWithDexMetadata() || resetProfile) { File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(new File(path)); dexMetadataPath = dexMetadataFile == null ? null : dexMetadataFile.getAbsolutePath(); } - final boolean isUsedByOtherApps = options.isDexoptAsSharedLibrary() - || packageUseInfo.isUsedByOtherApps(path); - final String compilerFilter = getRealCompilerFilter(pkg, - options.getCompilerFilter(), isUsedByOtherApps); // If we don't have to check for profiles updates assume // PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA which will be a no-op with respect to // profiles. - final int profileAnalysisResult = options.isCheckForProfileUpdates() - ? analyseProfiles(pkg, sharedGid, profileName, compilerFilter) - : PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA; + int profileAnalysisResult = PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA; + if (resetProfile) { + if (!resetProfile(pkg, profileName, path, dexMetadataPath)) { + // Fall back to use the shared filter. + compilerFilter = + PackageManagerServiceCompilerMapping.getCompilerFilterForReason( + PackageManagerService.REASON_SHARED); + } + } else if (options.isCheckForProfileUpdates()) { + profileAnalysisResult = + analyseProfiles(pkg, sharedGid, profileName, compilerFilter); + } // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct // flags. - final int dexoptFlags = getDexFlags(pkg, pkgSetting, compilerFilter, options); + final int dexoptFlags = getDexFlags(pkg, pkgSetting, compilerFilter, resetProfile, + options); for (String dexCodeIsa : dexCodeInstructionSets) { int newResult = dexOptPath(pkg, pkgSetting, path, dexCodeIsa, compilerFilter, @@ -391,6 +405,30 @@ public class PackageDexOptimizer { } /** + * Resets the profiles of the dex file at {@code path} belonging to the package {@code pkg} to + * the initial state as if the package is newly installed. Returns true on success, or false + * otherwise. + */ + @GuardedBy("mInstallLock") + private boolean resetProfile(AndroidPackage pkg, String profileName, String path, + @Nullable String dexMetadataPath) { + if (dexMetadataPath != null) { + try { + mInstaller.clearAppProfiles(pkg.getPackageName(), profileName); + final int appId = UserHandle.getAppId(pkg.getUid()); + mInstaller.prepareAppProfile(pkg.getPackageName(), UserHandle.USER_NULL, + appId, profileName, path, dexMetadataPath); + return true; + } catch (InstallerException e) { + Slog.w(TAG, "Failed to reset profile", e); + return false; + } + } else { + return false; + } + } + + /** * Performs dexopt on the {@code path} belonging to the package {@code pkg}. * * @return @@ -405,15 +443,15 @@ public class PackageDexOptimizer { String classLoaderContext, int dexoptFlags, int uid, CompilerStats.PackageStats packageStats, boolean downgrade, String profileName, String dexMetadataPath, int compilationReason) { - int dexoptNeeded = getDexoptNeeded(path, isa, compilerFilter, classLoaderContext, - profileAnalysisResult, downgrade); + String oatDir = getPackageOatDirIfSupported(pkg, + pkgSetting.getTransientState().isUpdatedSystemApp()); + + int dexoptNeeded = getDexoptNeeded(pkg.getPackageName(), path, isa, compilerFilter, + classLoaderContext, profileAnalysisResult, downgrade, dexoptFlags, oatDir); if (Math.abs(dexoptNeeded) == DexFile.NO_DEXOPT_NEEDED) { return DEX_OPT_SKIPPED; } - String oatDir = getPackageOatDirIfSupported(pkg, - pkgSetting.getTransientState().isUpdatedSystemApp()); - Log.i(TAG, "Running dexopt (dexoptNeeded=" + dexoptNeeded + ") on: " + path + " pkg=" + pkg.getPackageName() + " isa=" + isa + " dexoptFlags=" + printDexoptFlags(dexoptFlags) @@ -456,6 +494,7 @@ public class PackageDexOptimizer { /** * Perform dexopt (if needed) on a system server code path). */ + @GuardedBy("mInstallLock") @DexOptResult public int dexoptSystemServerPath( String dexPath, PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options) { @@ -466,12 +505,15 @@ public class PackageDexOptimizer { int result = DEX_OPT_SKIPPED; for (String isa : dexUseInfo.getLoaderIsas()) { int dexoptNeeded = getDexoptNeeded( + PackageManagerService.PLATFORM_PACKAGE_NAME, dexPath, isa, options.getCompilerFilter(), dexUseInfo.getClassLoaderContext(), PROFILE_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES, - /* downgrade= */ false); + /* downgrade= */ false, + dexoptFlags, + /* oatDir= */ null); if (dexoptNeeded == DexFile.NO_DEXOPT_NEEDED) { continue; @@ -714,7 +756,7 @@ public class PackageDexOptimizer { } /** - * Returns the compiler filter that should be used to optimize the package code. + * Returns the compiler filter that should be used to optimize the secondary dex. * The target filter will be updated if the package code is used by other apps * or if it has the safe mode flag set. */ @@ -754,12 +796,12 @@ public class PackageDexOptimizer { } /** - * Returns the compiler filter that should be used to optimize the package code. - * The target filter will be updated if the package code is used by other apps - * or if it has the safe mode flag set. + * Returns the compiler filter that should be used to optimize the primary dex. + * The target filter will be updated if the package has the safe mode flag set. Note that this + * method does NOT take other app use into account. The caller should be responsible for + * handling the case where the package code is used by other apps. */ - private String getRealCompilerFilter(AndroidPackage pkg, String targetCompilerFilter, - boolean isUsedByOtherApps) { + private String getRealCompilerFilter(AndroidPackage pkg, String targetCompilerFilter) { // When an app or priv app is configured to run out of box, only verify it. if (pkg.isUseEmbeddedDex() || (pkg.isPrivileged() @@ -783,12 +825,6 @@ public class PackageDexOptimizer { return getSafeModeCompilerFilter(targetCompilerFilter); } - if (isProfileGuidedCompilerFilter(targetCompilerFilter) && isUsedByOtherApps) { - // If the dex files is used by other apps, apply the shared filter. - return PackageManagerServiceCompilerMapping.getCompilerFilterForReason( - PackageManagerService.REASON_SHARED); - } - return targetCompilerFilter; } @@ -799,14 +835,16 @@ public class PackageDexOptimizer { private int getDexFlags(ApplicationInfo info, String compilerFilter, DexoptOptions options) { return getDexFlags((info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0, info.getHiddenApiEnforcementPolicy(), info.splitDependencies, - info.requestsIsolatedSplitLoading(), compilerFilter, options); + info.requestsIsolatedSplitLoading(), compilerFilter, false /* resetProfile */, + options); } + private int getDexFlags(AndroidPackage pkg, @NonNull PackageStateInternal pkgSetting, - String compilerFilter, DexoptOptions options) { + String compilerFilter, boolean resetProfile, DexoptOptions options) { return getDexFlags(pkg.isDebuggable(), AndroidPackageUtils.getHiddenApiEnforcementPolicy(pkg, pkgSetting), pkg.getSplitDependencies(), pkg.isIsolatedSplitLoading(), compilerFilter, - options); + resetProfile, options); } /** @@ -815,13 +853,15 @@ public class PackageDexOptimizer { */ private int getDexFlags(boolean debuggable, int hiddenApiEnforcementPolicy, SparseArray<int[]> splitDependencies, boolean requestsIsolatedSplitLoading, - String compilerFilter, DexoptOptions options) { + String compilerFilter, boolean resetProfile, DexoptOptions options) { // Profile guide compiled oat files should not be public unles they are based // on profiles from dex metadata archives. // The flag isDexoptInstallWithDexMetadata applies only on installs when we know that // the user does not have an existing profile. + // The flag resetProfile applies only when the existing profile is already reset. boolean isProfileGuidedFilter = isProfileGuidedCompilerFilter(compilerFilter); - boolean isPublic = !isProfileGuidedFilter || options.isDexoptInstallWithDexMetadata(); + boolean isPublic = !isProfileGuidedFilter || options.isDexoptInstallWithDexMetadata() + || resetProfile; int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 0; // Some apps are executed with restrictions on hidden API usage. If this app is one // of them, pass a flag to dexopt to enable the same restrictions during compilation. @@ -866,8 +906,19 @@ public class PackageDexOptimizer { * Assesses if there's a need to perform dexopt on {@code path} for the given * configuration (isa, compiler filter, profile). */ - private int getDexoptNeeded(String path, String isa, String compilerFilter, - String classLoaderContext, int profileAnalysisResult, boolean downgrade) { + @GuardedBy("mInstallLock") + private int getDexoptNeeded(String packageName, String path, String isa, String compilerFilter, + String classLoaderContext, int profileAnalysisResult, boolean downgrade, + int dexoptFlags, String oatDir) { + final boolean shouldBePublic = (dexoptFlags & DEXOPT_PUBLIC) != 0; + // If the artifacts should be public while the current artifacts are not, we should + // re-compile anyway. + if (shouldBePublic && isOdexPrivate(packageName, path, isa, oatDir)) { + // Ensure compilation by pretending a compiler filter change on the apk/odex location + // (the reason for the '-'. A positive value means the 'oat' location). + return adjustDexoptNeeded(-DexFile.DEX2OAT_FOR_FILTER); + } + int dexoptNeeded; try { // A profile guided optimizations with an empty profile is essentially 'verify' and @@ -901,6 +952,18 @@ public class PackageDexOptimizer { return compilerFilter.endsWith("-profile"); } + /** Returns true if the current artifacts of the app are private to the app itself. */ + @GuardedBy("mInstallLock") + private boolean isOdexPrivate(String packageName, String path, String isa, String oatDir) { + try { + return mInstaller.getOdexVisibility(packageName, path, isa, oatDir) + == Installer.ODEX_IS_PRIVATE; + } catch (Exception e) { + Slog.w(TAG, "Failed to get odex visibility for " + path, e); + return false; + } + } + /** * Checks if there is an update on the profile information of the {@code pkg}. * If the compiler filter is not profile guided the method returns a safe default: diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index eaecb17103a5..b1b05bedfcad 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -229,7 +229,6 @@ import com.android.server.pm.resolution.ComponentResolverApi; import com.android.server.pm.verify.domain.DomainVerificationManagerInternal; import com.android.server.pm.verify.domain.DomainVerificationService; import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy; -import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV1; import com.android.server.sdksandbox.SdkSandboxManagerLocal; import com.android.server.storage.DeviceStorageMonitorInternal; import com.android.server.utils.SnapshotCache; @@ -936,7 +935,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService private final BroadcastHelper mBroadcastHelper; private final RemovePackageHelper mRemovePackageHelper; private final DeletePackageHelper mDeletePackageHelper; - private final InitAndSystemPackageHelper mInitAndSystemPackageHelper; + private final InitAppsHelper mInitAppsHelper; private final AppDataHelper mAppDataHelper; private final InstallPackageHelper mInstallPackageHelper; private final PreferredActivityHelper mPreferredActivityHelper; @@ -1671,7 +1670,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService mAppDataHelper = testParams.appDataHelper; mInstallPackageHelper = testParams.installPackageHelper; mRemovePackageHelper = testParams.removePackageHelper; - mInitAndSystemPackageHelper = testParams.initAndSystemPackageHelper; + mInitAppsHelper = testParams.initAndSystemPackageHelper; mDeletePackageHelper = testParams.deletePackageHelper; mPreferredActivityHelper = testParams.preferredActivityHelper; mResolveIntentHelper = testParams.resolveIntentHelper; @@ -1821,7 +1820,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService mAppDataHelper = new AppDataHelper(this); mInstallPackageHelper = new InstallPackageHelper(this, mAppDataHelper); mRemovePackageHelper = new RemovePackageHelper(this, mAppDataHelper); - mInitAndSystemPackageHelper = new InitAndSystemPackageHelper(this); + mInitAppsHelper = new InitAppsHelper(this, mApexManager, mInstallPackageHelper, + mInjector.getSystemPartitions()); mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper, mAppDataHelper); mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper); @@ -1956,8 +1956,11 @@ public class PackageManagerService implements PackageSender, TestUtilityService mIsEngBuild, mIsUserDebugBuild, mIncrementalVersion); final int[] userIds = mUserManager.getUserIds(); - mOverlayConfig = mInitAndSystemPackageHelper.initPackages(packageSettings, - userIds, startTime); + PackageParser2 packageParser = mInjector.getScanningCachingPackageParser(); + mOverlayConfig = mInitAppsHelper.initSystemApps(packageParser, packageSettings, userIds, + startTime); + mInitAppsHelper.initNonSystemApps(packageParser, userIds, startTime); + packageParser.close(); // Resolve the storage manager. mStorageManagerPackage = getStorageManagerPackageName(computer); @@ -7030,7 +7033,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService } boolean isExpectingBetter(String packageName) { - return mInitAndSystemPackageHelper.isExpectingBetter(packageName); + return mInitAppsHelper.isExpectingBetter(packageName); } int getDefParseFlags() { @@ -7129,13 +7132,12 @@ public class PackageManagerService implements PackageSender, TestUtilityService } boolean isOverlayMutable(String packageName) { - return (mOverlayConfig != null ? mOverlayConfig - : OverlayConfig.getSystemInstance()).isMutable(packageName); + return mOverlayConfig.isMutable(packageName); } @ScanFlags int getSystemPackageScanFlags(File codePath) { List<ScanPartition> dirsToScanAsSystem = - mInitAndSystemPackageHelper.getDirsToScanAsSystem(); + mInitAppsHelper.getDirsToScanAsSystem(); @PackageManagerService.ScanFlags int scanFlags = SCAN_AS_SYSTEM; for (int i = dirsToScanAsSystem.size() - 1; i >= 0; i--) { ScanPartition partition = dirsToScanAsSystem.get(i); @@ -7153,7 +7155,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService Pair<Integer, Integer> getSystemPackageRescanFlagsAndReparseFlags(File scanFile, int systemScanFlags, int systemParseFlags) { List<ScanPartition> dirsToScanAsSystem = - mInitAndSystemPackageHelper.getDirsToScanAsSystem(); + mInitAppsHelper.getDirsToScanAsSystem(); @ParsingPackageUtils.ParseFlags int reparseFlags = 0; @PackageManagerService.ScanFlags int rescanFlags = 0; for (int i1 = dirsToScanAsSystem.size() - 1; i1 >= 0; i1--) { diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java index 5bdda0b3c1d8..e466fe2c0e31 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java @@ -109,7 +109,7 @@ public final class PackageManagerServiceTestParams { public AppDataHelper appDataHelper; public InstallPackageHelper installPackageHelper; public RemovePackageHelper removePackageHelper; - public InitAndSystemPackageHelper initAndSystemPackageHelper; + public InitAppsHelper initAndSystemPackageHelper; public DeletePackageHelper deletePackageHelper; public PreferredActivityHelper preferredActivityHelper; public ResolveIntentHelper resolveIntentHelper; diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index 8921fee6c8e0..c8f809b6782f 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -1827,9 +1827,9 @@ class ShortcutPackage extends ShortcutPackageItem { ShortcutService.writeTagExtra(out, TAG_EXTRAS, si.getExtras()); final Map<String, Map<String, List<String>>> capabilityBindings = - si.getCapabilityBindings(); + si.getCapabilityBindingsInternal(); if (capabilityBindings != null && !capabilityBindings.isEmpty()) { - XmlUtils.writeMapXml(si.getCapabilityBindings(), NAME_CAPABILITY, out); + XmlUtils.writeMapXml(capabilityBindings, NAME_CAPABILITY, out); } } diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java index df19d3e58bfd..a991ed3c4792 100644 --- a/services/core/java/com/android/server/pm/StorageEventHelper.java +++ b/services/core/java/com/android/server/pm/StorageEventHelper.java @@ -151,7 +151,7 @@ public final class StorageEventHelper extends StorageEventListener { final AndroidPackage pkg; try { pkg = installPackageHelper.scanSystemPackageTracedLI( - ps.getPath(), parseFlags, SCAN_INITIAL, 0, null); + ps.getPath(), parseFlags, SCAN_INITIAL, null); loaded.add(pkg); } catch (PackageManagerException e) { diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING index 9c74dd795d30..88a298a756d4 100644 --- a/services/core/java/com/android/server/pm/TEST_MAPPING +++ b/services/core/java/com/android/server/pm/TEST_MAPPING @@ -52,6 +52,9 @@ "options": [ { "include-filter": "com.google.android.security.gts.PackageVerifierTest" + }, + { + "exclude-filter": "com.google.android.security.gts.PackageVerifierTest#testAdbInstall_timeout_allowed" } ] }, diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 34b7ad41e540..70053bdeb47e 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -5409,6 +5409,21 @@ public class UserManagerService extends IUserManager.Stub { (new Shell()).exec(this, in, out, err, args, callback, resultReceiver); } + private static final String PREFIX_HELP_COMMAND = " "; + private static final String PREFIX_HELP_DESCRIPTION = " "; + private static final String PREFIX_HELP_DESCRIPTION_EXTRA_LINES = " "; + + private static final String CMD_HELP = "help"; + private static final String CMD_LIST = "list"; + private static final String CMD_REPORT_SYSTEM_USER_PACKAGE_ALLOWLIST_PROBLEMS = + "report-system-user-package-whitelist-problems"; + + private static final String ARG_V = "-v"; + private static final String ARG_VERBOSE = "--verbose"; + private static final String ARG_ALL = "--all"; + private static final String ARG_CRITICAL_ONLY = "--critical-only"; + private static final String ARG_MODE = "--mode"; + private int onShellCommand(Shell shell, String cmd) { if (cmd == null) { return shell.handleDefaultCommands(cmd); @@ -5417,9 +5432,9 @@ public class UserManagerService extends IUserManager.Stub { final PrintWriter pw = shell.getOutPrintWriter(); try { switch(cmd) { - case "list": + case CMD_LIST: return runList(pw, shell); - case "report-system-user-package-whitelist-problems": + case CMD_REPORT_SYSTEM_USER_PACKAGE_ALLOWLIST_PROBLEMS: return runReportPackageWhitelistProblems(pw, shell); default: return shell.handleDefaultCommands(cmd); @@ -5436,10 +5451,10 @@ public class UserManagerService extends IUserManager.Stub { String opt; while ((opt = shell.getNextOption()) != null) { switch (opt) { - case "-v": + case ARG_V: verbose = true; break; - case "--all": + case ARG_ALL: all = true; break; default: @@ -5520,14 +5535,14 @@ public class UserManagerService extends IUserManager.Stub { String opt; while ((opt = shell.getNextOption()) != null) { switch (opt) { - case "-v": - case "--verbose": + case ARG_V: + case ARG_VERBOSE: verbose = true; break; - case "--critical-only": + case ARG_CRITICAL_ONLY: criticalOnly = true; break; - case "--mode": + case ARG_MODE: mode = Integer.parseInt(shell.getNextArgRequired()); break; default: @@ -6248,20 +6263,28 @@ public class UserManagerService extends IUserManager.Stub { @Override public void onHelp() { final PrintWriter pw = getOutPrintWriter(); - pw.println("User manager (user) commands:"); - pw.println(" help"); - pw.println(" Prints this help text."); - pw.println(""); - pw.println(" list [-v] [-all]"); - pw.println(" Prints all users on the system."); - pw.println(" report-system-user-package-whitelist-problems [-v | --verbose] " - + "[--critical-only] [--mode MODE]"); - pw.println(" Reports all issues on user-type package whitelist XML files. Options:"); - pw.println(" -v | --verbose : shows extra info, like number of issues"); - pw.println(" --critical-only: show only critical issues, excluding warnings"); - pw.println(" --mode MODE: shows what errors would be if device used mode MODE (where" - + " MODE is the whitelist mode integer as defined by " - + "config_userTypePackageWhitelistMode)"); + pw.printf("User manager (user) commands:\n"); + + pw.printf("%s%s\n", PREFIX_HELP_COMMAND, CMD_HELP); + pw.printf("%sPrints this help text.\n\n", PREFIX_HELP_DESCRIPTION); + + pw.printf("%s%s [%s] [%s]\n", PREFIX_HELP_COMMAND, CMD_LIST, ARG_V, ARG_ALL); + pw.printf("%sPrints all users on the system.\n\n", PREFIX_HELP_DESCRIPTION); + + pw.printf("%s%s [%s | %s] [%s] [%s MODE]\n", PREFIX_HELP_COMMAND, + CMD_REPORT_SYSTEM_USER_PACKAGE_ALLOWLIST_PROBLEMS, + ARG_V, ARG_VERBOSE, ARG_CRITICAL_ONLY, ARG_MODE); + + pw.printf("%sReports all issues on user-type package allowlist XML files. Options:\n", + PREFIX_HELP_DESCRIPTION); + pw.printf("%s%s | %s: shows extra info, like number of issues\n", + PREFIX_HELP_DESCRIPTION, ARG_V, ARG_VERBOSE); + pw.printf("%s%s: show only critical issues, excluding warnings\n", + PREFIX_HELP_DESCRIPTION, ARG_CRITICAL_ONLY); + pw.printf("%s%s MODE: shows what errors would be if device used mode MODE\n" + + "%s(where MODE is the allowlist mode integer as defined by " + + "config_userTypePackageWhitelistMode)\n\n", + PREFIX_HELP_DESCRIPTION, ARG_MODE, PREFIX_HELP_DESCRIPTION_EXTRA_LINES); } } diff --git a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java index 46fde4b59d8b..60602337ba1a 100644 --- a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java +++ b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java @@ -272,6 +272,10 @@ public class OneTimePermissionUserManager { mHandler.removeCallbacksAndMessages(mToken); if (importance > IMPORTANCE_CACHED) { + if (mRevokeAfterKilledDelay == 0) { + onPackageInactiveLocked(); + return; + } // Delay revocation in case app is restarting mHandler.postDelayed(() -> { int imp = mActivityManager.getUidImportance(mUid); 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 71554eee3127..5a05134bed81 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -562,12 +562,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @Override - public void revokeOwnPermissionsOnKill(@NonNull String packageName, - @NonNull List<String> permissions) { - mPermissionManagerServiceImpl.revokeOwnPermissionsOnKill(packageName, permissions); - } - - @Override public boolean shouldShowRequestPermissionRationale(String packageName, String permissionName, int userId) { return mPermissionManagerServiceImpl.shouldShowRequestPermissionRationale(packageName, diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java index d0609307e6a8..009d155e96ef 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java @@ -1597,25 +1597,6 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt } } - @Override - public void revokeOwnPermissionsOnKill(String packageName, List<String> permissions) { - final int callingUid = Binder.getCallingUid(); - int callingUserId = UserHandle.getUserId(callingUid); - int targetPackageUid = mPackageManagerInt.getPackageUid(packageName, 0, callingUserId); - if (targetPackageUid != callingUid) { - throw new SecurityException("uid " + callingUid - + " cannot revoke permissions for package " + packageName + " with uid " - + targetPackageUid); - } - for (String permName : permissions) { - if (!checkCallingOrSelfPermission(permName)) { - throw new SecurityException("uid " + callingUid + " cannot revoke permission " - + permName + " because it does not hold that permission"); - } - } - mPermissionControllerManager.revokeOwnPermissionsOnKill(packageName, permissions); - } - private boolean mayManageRolePermission(int uid) { final PackageManager packageManager = mContext.getPackageManager(); final String[] packageNames = packageManager.getPackagesForUid(uid); diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java index 3e28320a2130..3771f030aefa 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java @@ -327,28 +327,6 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte void revokePostNotificationPermissionWithoutKillForTest(String packageName, int userId); /** - * Triggers the revocation of one or more permissions for a package, under the following - * conditions: - * <ul> - * <li>The package {@code packageName} must be under the same UID as the calling process - * (typically, the target package is the calling package). - * <li>Each permission in {@code permissions} must be granted to the package - * {@code packageName}. - * <li>Each permission in {@code permissions} must be a runtime permission. - * </ul> - * <p> - * Background permissions which have no corresponding foreground permission still granted once - * the revocation is effective will also be revoked. - * <p> - * This revocation happens asynchronously and kills all processes running in the same UID as - * {@code packageName}. It will be triggered once it is safe to do so. - * - * @param packageName The name of the package for which the permissions will be revoked. - * @param permissions List of permissions to be revoked. - */ - void revokeOwnPermissionsOnKill(String packageName, List<String> permissions); - - /** * Get whether you should show UI with rationale for requesting a permission. You should do this * only if you do not have the permission and the context in which the permission is requested * does not clearly communicate to the user what would be the benefit from grating this diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java index b3239486a75f..f255db4a9801 100644 --- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java +++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java @@ -242,6 +242,11 @@ public class ParsingPackageUtils { public static final int PARSE_CHATTY = 1 << 31; + /** The total maximum number of activities, services, providers and activity-aliases */ + private static final int MAX_NUM_COMPONENTS = 30000; + private static final String MAX_NUM_COMPONENTS_ERR_MSG = + "Total number of components has exceeded the maximum number: " + MAX_NUM_COMPONENTS; + @IntDef(flag = true, prefix = { "PARSE_" }, value = { PARSE_CHATTY, PARSE_COLLECT_CERTIFICATES, @@ -837,11 +842,20 @@ public class ParsingPackageUtils { if (result.isError()) { return input.error(result); } + + if (hasTooManyComponents(pkg)) { + return input.error(MAX_NUM_COMPONENTS_ERR_MSG); + } } return input.success(pkg); } + private static boolean hasTooManyComponents(ParsingPackage pkg) { + return pkg.getActivities().size() + pkg.getServices().size() + pkg.getProviders().size() + > MAX_NUM_COMPONENTS; + } + /** * For parsing non-MainComponents. Main ones have an order and some special handling which is * done directly in {@link #parseSplitApplication(ParseInput, ParsingPackage, Resources, @@ -2145,6 +2159,9 @@ public class ParsingPackageUtils { if (result.isError()) { return input.error(result); } + if (hasTooManyComponents(pkg)) { + return input.error(MAX_NUM_COMPONENTS_ERR_MSG); + } } if (TextUtils.isEmpty(pkg.getStaticSharedLibName()) && TextUtils.isEmpty( diff --git a/services/core/java/com/android/server/soundtrigger_middleware/AidlUtil.java b/services/core/java/com/android/server/soundtrigger_middleware/AidlUtil.java new file mode 100644 index 000000000000..f3457f5a221b --- /dev/null +++ b/services/core/java/com/android/server/soundtrigger_middleware/AidlUtil.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.soundtrigger_middleware; + +import android.media.soundtrigger.PhraseRecognitionEvent; +import android.media.soundtrigger.PhraseRecognitionExtra; +import android.media.soundtrigger.RecognitionEvent; +import android.media.soundtrigger.RecognitionStatus; +import android.media.soundtrigger.SoundModelType; + +/** + * Utilities for working with sound trigger related AIDL generated types. + */ +public class AidlUtil { + /** + * Initialize a new recognition event. + * @return The new event. + */ + static RecognitionEvent newEmptyRecognitionEvent() { + RecognitionEvent result = new RecognitionEvent(); + result.data = new byte[0]; + return result; + } + + /** + * Initialize a new phrase recognition event. + * @return The new event. + */ + static PhraseRecognitionEvent newEmptyPhraseRecognitionEvent() { + PhraseRecognitionEvent result = new PhraseRecognitionEvent(); + result.common = newEmptyRecognitionEvent(); + result.phraseExtras = new PhraseRecognitionExtra[0]; + return result; + } + + /** + * Creates a new generic abort event. + * @return The new event. + */ + static RecognitionEvent newAbortEvent() { + RecognitionEvent event = newEmptyRecognitionEvent(); + event.type = SoundModelType.GENERIC; + event.status = RecognitionStatus.ABORTED; + return event; + } + + /** + * Creates a new generic phrase event. + * @return The new event. + */ + static PhraseRecognitionEvent newAbortPhraseEvent() { + PhraseRecognitionEvent event = newEmptyPhraseRecognitionEvent(); + event.common.type = SoundModelType.KEYPHRASE; + event.common.status = RecognitionStatus.ABORTED; + return event; + } +} diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java index 1cc05391b497..c0ab65a3215c 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java @@ -20,12 +20,10 @@ import android.annotation.NonNull; import android.media.permission.SafeCloseable; import android.media.soundtrigger.ModelParameterRange; import android.media.soundtrigger.PhraseRecognitionEvent; -import android.media.soundtrigger.PhraseRecognitionExtra; import android.media.soundtrigger.PhraseSoundModel; import android.media.soundtrigger.Properties; import android.media.soundtrigger.RecognitionConfig; import android.media.soundtrigger.RecognitionEvent; -import android.media.soundtrigger.RecognitionStatus; import android.media.soundtrigger.SoundModel; import android.media.soundtrigger.SoundModelType; import android.media.soundtrigger.Status; @@ -392,21 +390,14 @@ public class SoundTriggerHalConcurrentCaptureHandler implements ISoundTriggerHal /** Notify the client that recognition has been aborted. */ private static void notifyAbort(int modelHandle, LoadedModel model) { switch (model.type) { - case SoundModelType.GENERIC: { - RecognitionEvent event = newEmptyRecognitionEvent(); - event.status = RecognitionStatus.ABORTED; - event.type = SoundModelType.GENERIC; - model.callback.recognitionCallback(modelHandle, event); - } - break; - - case SoundModelType.KEYPHRASE: { - PhraseRecognitionEvent event = newEmptyPhraseRecognitionEvent(); - event.common.status = RecognitionStatus.ABORTED; - event.common.type = SoundModelType.KEYPHRASE; - model.callback.phraseRecognitionCallback(modelHandle, event); - } - break; + case SoundModelType.GENERIC: + model.callback.recognitionCallback(modelHandle, AidlUtil.newAbortEvent()); + break; + + case SoundModelType.KEYPHRASE: + model.callback.phraseRecognitionCallback(modelHandle, + AidlUtil.newAbortPhraseEvent()); + break; } } @@ -416,19 +407,6 @@ public class SoundTriggerHalConcurrentCaptureHandler implements ISoundTriggerHal mNotifier.unregisterListener(this); } - private static PhraseRecognitionEvent newEmptyPhraseRecognitionEvent() { - PhraseRecognitionEvent result = new PhraseRecognitionEvent(); - result.common = newEmptyRecognitionEvent(); - result.phraseExtras = new PhraseRecognitionExtra[0]; - return result; - } - - private static RecognitionEvent newEmptyRecognitionEvent() { - RecognitionEvent result = new RecognitionEvent(); - result.data = new byte[0]; - return result; - } - //////////////////////////////////////////////////////////////////////////////////////////////// // All methods below do trivial delegation - no interesting logic. @Override diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java index 934b0e46ee95..fd8dee8416f6 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java @@ -25,6 +25,7 @@ import android.media.soundtrigger.Properties; import android.media.soundtrigger.RecognitionConfig; import android.media.soundtrigger.RecognitionEvent; import android.media.soundtrigger.SoundModel; +import android.media.soundtrigger.SoundModelType; import android.media.soundtrigger.Status; import android.media.soundtrigger_middleware.ISoundTriggerCallback; import android.media.soundtrigger_middleware.ISoundTriggerModule; @@ -305,9 +306,12 @@ class SoundTriggerModule implements IBinder.DeathRecipient, ISoundTriggerHal.Glo @Override public void stopRecognition(int modelHandle) { + Model model; synchronized (SoundTriggerModule.this) { - mLoadedModels.get(modelHandle).stopRecognition(); + checkValid(); + model = mLoadedModels.get(modelHandle); } + model.stopRecognition(); } @Override @@ -374,6 +378,7 @@ class SoundTriggerModule implements IBinder.DeathRecipient, ISoundTriggerHal.Glo private class Model implements ISoundTriggerHal.ModelCallback { public int mHandle; private ModelState mState = ModelState.INIT; + private int mType = SoundModelType.INVALID; private SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession mSession; private @NonNull @@ -390,6 +395,7 @@ class SoundTriggerModule implements IBinder.DeathRecipient, ISoundTriggerHal.Glo SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession) { mSession = audioSession; mHandle = mHalService.loadSoundModel(model, this); + mType = SoundModelType.GENERIC; setState(ModelState.LOADED); mLoadedModels.put(mHandle, this); return mHandle; @@ -399,7 +405,7 @@ class SoundTriggerModule implements IBinder.DeathRecipient, ISoundTriggerHal.Glo SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession) { mSession = audioSession; mHandle = mHalService.loadPhraseSoundModel(model, this); - + mType = SoundModelType.KEYPHRASE; setState(ModelState.LOADED); mLoadedModels.put(mHandle, this); return mHandle; @@ -422,12 +428,41 @@ class SoundTriggerModule implements IBinder.DeathRecipient, ISoundTriggerHal.Glo } private void stopRecognition() { - if (getState() == ModelState.LOADED) { - // This call is idempotent in order to avoid races. - return; + synchronized (SoundTriggerModule.this) { + if (getState() == ModelState.LOADED) { + // This call is idempotent in order to avoid races. + return; + } } + // This must be invoked outside the lock. mHalService.stopRecognition(mHandle); - setState(ModelState.LOADED); + + // No more callbacks for this model after this point. + synchronized (SoundTriggerModule.this) { + // Generate an abortion callback to the client if the model is still active. + if (getState() == ModelState.ACTIVE) { + if (mCallback != null) { + try { + switch (mType) { + case SoundModelType.GENERIC: + mCallback.onRecognition(mHandle, AidlUtil.newAbortEvent(), + mSession.mSessionHandle); + break; + case SoundModelType.KEYPHRASE: + mCallback.onPhraseRecognition(mHandle, + AidlUtil.newAbortPhraseEvent(), + mSession.mSessionHandle); + break; + default: + throw new RuntimeException( + "Unexpected model type: " + mType); + } + } catch (RemoteException e) { + } + } + setState(ModelState.LOADED); + } + } } /** Request a forced recognition event. Will do nothing if recognition is inactive. */ @@ -518,4 +553,5 @@ class SoundTriggerModule implements IBinder.DeathRecipient, ISoundTriggerHal.Glo } } } + } diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index 2e60f130bc4b..526dccb72e39 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -1633,7 +1633,14 @@ public class StatsPullAtomService extends SystemService { if (adapter != null) { SynchronousResultReceiver bluetoothReceiver = new SynchronousResultReceiver("bluetooth"); - adapter.requestControllerActivityEnergyInfo(bluetoothReceiver); + adapter.requestControllerActivityEnergyInfo( + Runnable::run, + info -> { + Bundle bundle = new Bundle(); + bundle.putParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY, info); + bluetoothReceiver.send(0, bundle); + } + ); return awaitControllerInfo(bluetoothReceiver); } else { Slog.e(TAG, "Failed to get bluetooth adapter!"); @@ -2338,51 +2345,25 @@ public class StatsPullAtomService extends SystemService { } int pullProcessDmabufMemory(int atomTag, List<StatsEvent> pulledData) { - List<ProcessMemoryState> managedProcessList = - LocalServices.getService(ActivityManagerInternal.class) - .getMemoryStateForProcesses(); - for (ProcessMemoryState process : managedProcessList) { - KernelAllocationStats.ProcessDmabuf proc = - KernelAllocationStats.getDmabufAllocations(process.pid); - if (proc == null || (proc.retainedBuffersCount <= 0 && proc.mappedBuffersCount <= 0)) { - continue; - } - pulledData.add( - FrameworkStatsLog.buildStatsEvent( - atomTag, - process.uid, - process.processName, - process.oomScore, - proc.retainedSizeKb, - proc.retainedBuffersCount, - proc.mappedSizeKb, - proc.mappedBuffersCount)); + KernelAllocationStats.ProcessDmabuf[] procBufs = + KernelAllocationStats.getDmabufAllocations(); + + if (procBufs == null) { + return StatsManager.PULL_SKIP; } - SparseArray<String> processCmdlines = getProcessCmdlines(); - managedProcessList.forEach(managedProcess -> processCmdlines.delete(managedProcess.pid)); - int size = processCmdlines.size(); - for (int i = 0; i < size; ++i) { - int pid = processCmdlines.keyAt(i); - int uid = getUidForPid(pid); - // ignore root processes (unlikely to be interesting) - if (uid <= 0) { - continue; - } - KernelAllocationStats.ProcessDmabuf proc = - KernelAllocationStats.getDmabufAllocations(pid); - if (proc == null || (proc.retainedBuffersCount <= 0 && proc.mappedBuffersCount <= 0)) { - continue; - } - pulledData.add( - FrameworkStatsLog.buildStatsEvent( - atomTag, - uid, - processCmdlines.valueAt(i), - -1001 /*Placeholder for native processes, OOM_SCORE_ADJ_MIN - 1.*/, - proc.retainedSizeKb, - proc.retainedBuffersCount, - proc.mappedSizeKb, - proc.mappedBuffersCount)); + for (KernelAllocationStats.ProcessDmabuf procBuf : procBufs) { + pulledData.add(FrameworkStatsLog.buildStatsEvent( + atomTag, + procBuf.uid, + procBuf.processName, + procBuf.oomScore, + procBuf.retainedSizeKb, + procBuf.retainedBuffersCount, + 0, /* mapped_dmabuf_kb - deprecated */ + 0, /* mapped_dmabuf_count - deprecated */ + procBuf.surfaceFlingerSizeKb, + procBuf.surfaceFlingerCount + )); } return StatsManager.PULL_SUCCESS; } diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java index 20cd8f5c12f8..adca21676f9d 100644 --- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java +++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java @@ -198,6 +198,8 @@ public class TrustAgentWrapper { // Fall through. case MSG_REVOKE_TRUST: mTrusted = false; + mTrustable = false; + mWaitingForTrustableDowngrade = false; mDisplayTrustGrantedMessage = false; mMessage = null; mHandler.removeMessages(MSG_TRUST_TIMEOUT); diff --git a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java index 53a9244837ed..672458bef4a7 100644 --- a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java +++ b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java @@ -42,8 +42,8 @@ import android.media.tv.interactive.ITvInteractiveAppService; import android.media.tv.interactive.ITvInteractiveAppServiceCallback; import android.media.tv.interactive.ITvInteractiveAppSession; import android.media.tv.interactive.ITvInteractiveAppSessionCallback; -import android.media.tv.interactive.TvInteractiveAppInfo; import android.media.tv.interactive.TvInteractiveAppService; +import android.media.tv.interactive.TvInteractiveAppServiceInfo; import android.net.Uri; import android.os.Binder; import android.os.Bundle; @@ -130,7 +130,7 @@ public class TvInteractiveAppManagerService extends SystemService { new Intent(TvInteractiveAppService.SERVICE_INTERFACE), PackageManager.GET_SERVICES | PackageManager.GET_META_DATA, userId); - List<TvInteractiveAppInfo> iAppList = new ArrayList<>(); + List<TvInteractiveAppServiceInfo> iAppList = new ArrayList<>(); for (ResolveInfo ri : services) { ServiceInfo si = ri.serviceInfo; @@ -143,8 +143,8 @@ public class TvInteractiveAppManagerService extends SystemService { ComponentName component = new ComponentName(si.packageName, si.name); try { - TvInteractiveAppInfo info = - new TvInteractiveAppInfo(mContext, component); + TvInteractiveAppServiceInfo info = + new TvInteractiveAppServiceInfo(mContext, component); iAppList.add(info); } catch (Exception e) { Slogf.e(TAG, "failed to load TV Interactive App service " + si.name, e); @@ -154,10 +154,10 @@ public class TvInteractiveAppManagerService extends SystemService { } // sort the iApp list by iApp service id - Collections.sort(iAppList, Comparator.comparing(TvInteractiveAppInfo::getId)); + Collections.sort(iAppList, Comparator.comparing(TvInteractiveAppServiceInfo::getId)); Map<String, TvInteractiveAppState> iAppMap = new HashMap<>(); ArrayMap<String, Integer> tiasAppCount = new ArrayMap<>(iAppMap.size()); - for (TvInteractiveAppInfo info : iAppList) { + for (TvInteractiveAppServiceInfo info : iAppList) { String iAppServiceId = info.getId(); if (DEBUG) { Slogf.d(TAG, "add " + iAppServiceId); @@ -195,7 +195,7 @@ public class TvInteractiveAppManagerService extends SystemService { for (String iAppServiceId : userState.mIAppMap.keySet()) { if (!iAppMap.containsKey(iAppServiceId)) { - TvInteractiveAppInfo info = userState.mIAppMap.get(iAppServiceId).mInfo; + TvInteractiveAppServiceInfo info = userState.mIAppMap.get(iAppServiceId).mInfo; ServiceState serviceState = userState.mServiceStateMap.get(info.getComponent()); if (serviceState != null) { abortPendingCreateSessionRequestsLocked(serviceState, iAppServiceId, userId); @@ -283,7 +283,7 @@ public class TvInteractiveAppManagerService extends SystemService { userState.mCallbacks.finishBroadcast(); } - private int getInteractiveAppUid(TvInteractiveAppInfo info) { + private int getInteractiveAppUid(TvInteractiveAppServiceInfo info) { try { return getContext().getPackageManager().getApplicationInfo( info.getServiceInfo().packageName, 0).uid; @@ -642,7 +642,7 @@ public class TvInteractiveAppManagerService extends SystemService { private final class BinderService extends ITvInteractiveAppManager.Stub { @Override - public List<TvInteractiveAppInfo> getTvInteractiveAppServiceList(int userId) { + public List<TvInteractiveAppServiceInfo> getTvInteractiveAppServiceList(int userId) { final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), Binder.getCallingUid(), userId, "getTvInteractiveAppServiceList"); final long identity = Binder.clearCallingIdentity(); @@ -653,7 +653,7 @@ public class TvInteractiveAppManagerService extends SystemService { mGetServiceListCalled = true; } UserState userState = getOrCreateUserStateLocked(resolvedUserId); - List<TvInteractiveAppInfo> iAppList = new ArrayList<>(); + List<TvInteractiveAppServiceInfo> iAppList = new ArrayList<>(); for (TvInteractiveAppState state : userState.mIAppMap.values()) { iAppList.add(state.mInfo); } @@ -665,42 +665,6 @@ public class TvInteractiveAppManagerService extends SystemService { } @Override - public void prepare(String tiasId, int type, int userId) { - // TODO: bind service - final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), - Binder.getCallingUid(), userId, "prepare"); - final long identity = Binder.clearCallingIdentity(); - try { - synchronized (mLock) { - UserState userState = getOrCreateUserStateLocked(resolvedUserId); - TvInteractiveAppState iAppState = userState.mIAppMap.get(tiasId); - if (iAppState == null) { - Slogf.e(TAG, "failed to prepare TIAS - unknown TIAS id " + tiasId); - return; - } - ComponentName componentName = iAppState.mInfo.getComponent(); - ServiceState serviceState = userState.mServiceStateMap.get(componentName); - if (serviceState == null) { - serviceState = new ServiceState( - componentName, tiasId, resolvedUserId, true, type); - userState.mServiceStateMap.put(componentName, serviceState); - updateServiceConnectionLocked(componentName, resolvedUserId); - } else if (serviceState.mService != null) { - serviceState.mService.prepare(type); - } else { - serviceState.mPendingPrepare = true; - serviceState.mPendingPrepareType = type; - updateServiceConnectionLocked(componentName, resolvedUserId); - } - } - } catch (RemoteException e) { - Slogf.e(TAG, "error in prepare", e); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - @Override public void registerAppLinkInfo(String tiasId, AppLinkInfo appLinkInfo, int userId) { final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), Binder.getCallingUid(), userId, "registerAppLinkInfo: " + appLinkInfo); @@ -1360,6 +1324,32 @@ public class TvInteractiveAppManagerService extends SystemService { } @Override + public void sendSigningResult( + IBinder sessionToken, String signingId, byte[] result, int userId) { + if (DEBUG) { + Slogf.d(TAG, "sendSigningResult(signingId=%s)", signingId); + } + final int callingUid = Binder.getCallingUid(); + final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, + userId, "sendSigningResult"); + SessionState sessionState = null; + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + try { + sessionState = getSessionStateLocked(sessionToken, callingUid, + resolvedUserId); + getSessionLocked(sessionState).sendSigningResult(signingId, result); + } catch (RemoteException | SessionNotFoundException e) { + Slogf.e(TAG, "error in sendSigningResult", e); + } + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override public void setSurface(IBinder sessionToken, Surface surface, int userId) { final int callingUid = Binder.getCallingUid(); final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, @@ -1679,7 +1669,6 @@ public class TvInteractiveAppManagerService extends SystemService { } boolean shouldBind = (!serviceState.mSessionTokens.isEmpty()) - || (serviceState.mPendingPrepare) || (!serviceState.mPendingAppLinkInfo.isEmpty()) || (!serviceState.mPendingAppLinkCommand.isEmpty()); @@ -1738,7 +1727,7 @@ public class TvInteractiveAppManagerService extends SystemService { private static final class TvInteractiveAppState { private String mIAppServiceId; private ComponentName mComponentName; - private TvInteractiveAppInfo mInfo; + private TvInteractiveAppServiceInfo mInfo; private int mUid; private int mIAppNumber; } @@ -1831,22 +1820,13 @@ public class TvInteractiveAppManagerService extends SystemService { private final List<Pair<AppLinkInfo, Boolean>> mPendingAppLinkInfo = new ArrayList<>(); private final List<Bundle> mPendingAppLinkCommand = new ArrayList<>(); - private boolean mPendingPrepare = false; - private Integer mPendingPrepareType = null; private ITvInteractiveAppService mService; private ServiceCallback mCallback; private boolean mBound; private boolean mReconnecting; private ServiceState(ComponentName component, String tias, int userId) { - this(component, tias, userId, false, null); - } - - private ServiceState(ComponentName component, String tias, int userId, - boolean pendingPrepare, Integer prepareType) { mComponent = component; - mPendingPrepare = pendingPrepare; - mPendingPrepareType = prepareType; mConnection = new InteractiveAppServiceConnection(component, userId); mIAppServiceId = tias; } @@ -1894,19 +1874,6 @@ public class TvInteractiveAppManagerService extends SystemService { } } - if (serviceState.mPendingPrepare) { - final long identity = Binder.clearCallingIdentity(); - try { - serviceState.mService.prepare(serviceState.mPendingPrepareType); - serviceState.mPendingPrepare = false; - serviceState.mPendingPrepareType = null; - } catch (RemoteException e) { - Slogf.e(TAG, "error in prepare when onServiceConnected", e); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - if (!serviceState.mPendingAppLinkInfo.isEmpty()) { for (Iterator<Pair<AppLinkInfo, Boolean>> it = serviceState.mPendingAppLinkInfo.iterator(); @@ -2221,6 +2188,24 @@ public class TvInteractiveAppManagerService extends SystemService { } @Override + public void onRequestSigning(String id, String algorithm, String alias, byte[] data) { + synchronized (mLock) { + if (DEBUG) { + Slogf.d(TAG, "onRequestSigning"); + } + if (mSessionState.mSession == null || mSessionState.mClient == null) { + return; + } + try { + mSessionState.mClient.onRequestSigning( + id, algorithm, alias, data, mSessionState.mSeq); + } catch (RemoteException e) { + Slogf.e(TAG, "error in onRequestSigning", e); + } + } + } + + @Override public void onAdRequest(AdRequest request) { synchronized (mLock) { if (DEBUG) { diff --git a/services/core/java/com/android/server/utils/TimingsTraceAndSlog.java b/services/core/java/com/android/server/utils/TimingsTraceAndSlog.java index 397acfac2812..b45c962811ad 100644 --- a/services/core/java/com/android/server/utils/TimingsTraceAndSlog.java +++ b/services/core/java/com/android/server/utils/TimingsTraceAndSlog.java @@ -88,7 +88,7 @@ public final class TimingsTraceAndSlog extends TimingsTraceLog { @Override public void traceBegin(@NonNull String name) { - Slog.i(mTag, name); + Slog.d(mTag, name); super.traceBegin(name); } diff --git a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java index d4648a4f313c..11ddac6a9740 100644 --- a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java +++ b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java @@ -30,6 +30,7 @@ import android.os.IBinder; import android.os.InputConfig; import android.os.Looper; import android.os.Message; +import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.view.IWindow; @@ -94,8 +95,6 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener { mService = service; mAccessibilityController = accessibilityController; mHandler = new MyHandler(mService.mH.getLooper()); - - register(); } /** @@ -219,8 +218,10 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener { } mWindowsNotificationEnabled = register; if (mWindowsNotificationEnabled) { - populateVisibleWindowHandlesAndNotifyWindowsChangeIfNeeded(); + Pair<InputWindowHandle[], DisplayInfo[]> info = register(); + onWindowInfosChangedInternal(info.first, info.second); } else { + unregister(); releaseResources(); } } diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index c0cdec9ade7f..7f84f61a91ff 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -191,6 +191,35 @@ class ActivityMetricsLogger { private long mCurrentTransitionStartTimeNs; /** Non-null when a {@link TransitionInfo} is created for this state. */ private TransitionInfo mAssociatedTransitionInfo; + /** The sequence id for trace. It is used to map the traces before resolving intent. */ + private static int sTraceSeqId; + /** The trace format is "launchingActivity#$seqId:$state(:$packageName)". */ + final String mTraceName; + + LaunchingState() { + if (!Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { + mTraceName = null; + return; + } + // Use an id because the launching app is not yet known before resolving intent. + sTraceSeqId++; + mTraceName = "launchingActivity#" + sTraceSeqId; + Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, mTraceName, 0); + } + + void stopTrace(boolean abort) { + if (mTraceName == null) return; + Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, mTraceName, 0); + final String launchResult; + if (mAssociatedTransitionInfo == null) { + launchResult = ":failed"; + } else { + launchResult = (abort ? ":canceled:" : ":completed:") + + mAssociatedTransitionInfo.mLastLaunchedActivity.packageName; + } + // Put a supplement trace as the description of the async trace with the same id. + Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, mTraceName + launchResult); + } @VisibleForTesting boolean allDrawn() { @@ -549,10 +578,10 @@ class ActivityMetricsLogger { } if (existingInfo == null) { - // Only notify the observer for a new launching event. - launchObserverNotifyIntentStarted(intent, transitionStartTimeNs); final LaunchingState launchingState = new LaunchingState(); launchingState.mCurrentTransitionStartTimeNs = transitionStartTimeNs; + // Only notify the observer for a new launching event. + launchObserverNotifyIntentStarted(intent, transitionStartTimeNs); return launchingState; } existingInfo.mLaunchingState.mCurrentTransitionStartTimeNs = transitionStartTimeNs; @@ -574,7 +603,7 @@ class ActivityMetricsLogger { @Nullable ActivityOptions options) { if (launchedActivity == null) { // The launch is aborted, e.g. intent not resolved, class not found. - abort(null /* info */, "nothing launched"); + abort(launchingState, "nothing launched"); return; } @@ -601,7 +630,7 @@ class ActivityMetricsLogger { if (launchedActivity.isReportedDrawn() && launchedActivity.isVisible()) { // Launched activity is already visible. We cannot measure windows drawn delay. - abort(info, "launched activity already visible"); + abort(launchingState, "launched activity already visible"); return; } @@ -633,7 +662,7 @@ class ActivityMetricsLogger { final TransitionInfo newInfo = TransitionInfo.create(launchedActivity, launchingState, options, processRunning, processSwitch, newActivityCreated, resultCode); if (newInfo == null) { - abort(info, "unrecognized launch"); + abort(launchingState, "unrecognized launch"); return; } @@ -874,23 +903,29 @@ class ActivityMetricsLogger { } } + private void abort(@NonNull LaunchingState state, String cause) { + if (state.mAssociatedTransitionInfo != null) { + abort(state.mAssociatedTransitionInfo, cause); + return; + } + if (DEBUG_METRICS) Slog.i(TAG, "abort launch cause=" + cause); + state.stopTrace(true /* abort */); + launchObserverNotifyIntentFailed(); + } + /** Aborts tracking of current launch metrics. */ - private void abort(TransitionInfo info, String cause) { + private void abort(@NonNull TransitionInfo info, String cause) { done(true /* abort */, info, cause, 0L /* timestampNs */); } /** Called when the given transition (info) is no longer active. */ - private void done(boolean abort, @Nullable TransitionInfo info, String cause, + private void done(boolean abort, @NonNull TransitionInfo info, String cause, long timestampNs) { if (DEBUG_METRICS) { Slog.i(TAG, "done abort=" + abort + " cause=" + cause + " timestamp=" + timestampNs + " info=" + info); } - if (info == null) { - launchObserverNotifyIntentFailed(); - return; - } - + info.mLaunchingState.stopTrace(abort); stopLaunchTrace(info); final Boolean isHibernating = mLastHibernationStates.remove(info.mLastLaunchedActivity.packageName); @@ -1457,7 +1492,7 @@ class ActivityMetricsLogger { /** Starts trace for an activity is actually launching. */ private void startLaunchTrace(@NonNull TransitionInfo info) { if (DEBUG_METRICS) Slog.i(TAG, "startLaunchTrace " + info); - if (!Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { + if (info.mLaunchingState.mTraceName == null) { return; } info.mLaunchTraceName = "launching: " + info.mLastLaunchedActivity.packageName; diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 883ce99d313d..bfe1f30f671a 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -5603,19 +5603,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A "makeInvisible", true /* beforeStopping */); // Defer telling the client it is hidden if it can enter Pip and isn't current paused, // stopped or stopping. This gives it a chance to enter Pip in onPause(). - // TODO: There is still a question surrounding activities in multi-window mode that want - // to enter Pip after they are paused, but are still visible. I they should be okay to - // enter Pip in those cases, but not "auto-Pip" which is what this condition covers and - // the current contract for "auto-Pip" is that the app should enter it before onPause - // returns. Just need to confirm this reasoning makes sense. final boolean deferHidingClient = canEnterPictureInPicture && !isState(STARTED, STOPPING, STOPPED, PAUSED); - if (!mTransitionController.isShellTransitionsEnabled() - && deferHidingClient && pictureInPictureArgs.isAutoEnterEnabled()) { - // Go ahead and just put the activity in pip if it supports auto-pip. - mAtmService.enterPictureInPictureMode(this, pictureInPictureArgs); - return; - } setDeferHidingClient(deferHidingClient); setVisibility(false); diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 7d2dfa071597..77ec67f31d50 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -2730,10 +2730,11 @@ class ActivityStarter { false /* includingParents */); intentTask = intentTask.getParent().asTaskFragment().getTask(); } - // If the task is in multi-windowing mode, the activity may already be on + // If the activity is visible in multi-windowing mode, it may already be on // the top (visible to user but not the global top), then the result code // should be START_DELIVERED_TO_TOP instead of START_TASK_TO_FRONT. final boolean wasTopOfVisibleRootTask = intentActivity.mVisibleRequested + && intentActivity.inMultiWindowMode() && intentActivity == mTargetRootTask.topRunningActivity(); // We only want to move to the front, if we aren't going to launch on a // different root task. If we launch on a different root task, we will put the diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java index 07a0c372778b..87523f44d4b6 100644 --- a/services/core/java/com/android/server/wm/ContentRecorder.java +++ b/services/core/java/com/android/server/wm/ContentRecorder.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY; +import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONTENT_RECORDING; @@ -26,6 +27,7 @@ import android.content.res.Configuration; import android.graphics.Point; import android.graphics.Rect; import android.os.IBinder; +import android.provider.DeviceConfig; import android.view.ContentRecordingSession; import android.view.Display; import android.view.SurfaceControl; @@ -39,6 +41,11 @@ import com.android.internal.protolog.common.ProtoLog; final class ContentRecorder { /** + * The key for accessing the device config that controls if task recording is supported. + */ + @VisibleForTesting static final String KEY_RECORD_TASK_FEATURE = "record_task_content"; + + /** * The display content this class is handling recording for. */ @NonNull @@ -48,7 +55,7 @@ final class ContentRecorder { * The session for content recording, or null if this DisplayContent is not being used for * recording. */ - @VisibleForTesting private ContentRecordingSession mContentRecordingSession = null; + private ContentRecordingSession mContentRecordingSession = null; /** * The WindowContainer for the level of the hierarchy to record. @@ -187,6 +194,8 @@ final class ContentRecorder { mDisplayContent.mWmService.mTransactionFactory.get().remove(mRecordedSurface).apply(); mRecordedSurface = null; clearContentRecordingSession(); + // Do not need to force remove the VirtualDisplay; this is handled by the media + // projection service. } } @@ -215,46 +224,12 @@ final class ContentRecorder { return; } - final int contentToRecord = mContentRecordingSession.getContentToRecord(); - if (contentToRecord != RECORD_CONTENT_DISPLAY) { - // TODO(b/216625226) handle task-based recording - // Not a valid region, or recording is disabled, so fall back to prior MediaProjection - // approach. - clearContentRecordingSession(); - ProtoLog.v(WM_DEBUG_CONTENT_RECORDING, - "Unable to start recording due to invalid region for display %d", - mDisplayContent.getDisplayId()); - return; - } - // Given the WindowToken of the DisplayArea to record, retrieve the associated - // SurfaceControl. - IBinder tokenToRecord = mContentRecordingSession.getTokenToRecord(); - if (tokenToRecord == null) { - // Unexpectedly missing token. Fall back to prior MediaProjection approach. - clearContentRecordingSession(); - ProtoLog.v(WM_DEBUG_CONTENT_RECORDING, - "Unable to start recording due to null token for display %d", - mDisplayContent.getDisplayId()); - return; - } - - final WindowContainer wc = - mDisplayContent.mWmService.mWindowContextListenerController.getContainer( - tokenToRecord); - if (wc == null) { - // Un-set the window token to record for this VirtualDisplay. Fall back to the - // original MediaProjection approach. - mDisplayContent.mWmService.mDisplayManagerInternal.setWindowManagerMirroring( - mDisplayContent.getDisplayId(), false); - clearContentRecordingSession(); - ProtoLog.v(WM_DEBUG_CONTENT_RECORDING, - "Unable to retrieve window container to start recording for " - + "display %d", - mDisplayContent.getDisplayId()); + mRecordedWindowContainer = retrieveRecordedWindowContainer(); + if (mRecordedWindowContainer == null) { + // Either the token is missing, or the window associated with the token is missing. + // Error has already been handled, so just leave. return; } - // TODO(206461622) Migrate to using the RootDisplayArea - mRecordedWindowContainer = wc.getDisplayContent(); final Point surfaceSize = fetchSurfaceSizeIfPresent(); if (surfaceSize == null) { @@ -296,6 +271,107 @@ final class ContentRecorder { } /** + * Retrieves the {@link WindowContainer} for the level of the hierarchy to start recording, + * indicated by the {@link #mContentRecordingSession}. Performs any error handling and state + * updates necessary if the {@link WindowContainer} could not be retrieved. + * {@link #mContentRecordingSession} must be non-null. + * + * @return a {@link WindowContainer} to record, or {@code null} if an error was encountered. The + * error is logged and any cleanup is handled. + */ + @Nullable + private WindowContainer retrieveRecordedWindowContainer() { + final int contentToRecord = mContentRecordingSession.getContentToRecord(); + // Given the WindowToken of the region to record, retrieve the associated + // SurfaceControl. + final IBinder tokenToRecord = mContentRecordingSession.getTokenToRecord(); + if (tokenToRecord == null) { + handleStartRecordingFailed(); + ProtoLog.v(WM_DEBUG_CONTENT_RECORDING, + "Unable to start recording due to null token for display %d", + mDisplayContent.getDisplayId()); + return null; + } + switch (contentToRecord) { + case RECORD_CONTENT_DISPLAY: + final WindowContainer wc = + mDisplayContent.mWmService.mWindowContextListenerController.getContainer( + tokenToRecord); + if (wc == null) { + // Un-set the window token to record for this VirtualDisplay. Fall back to + // Display stack capture for the entire display. + mDisplayContent.mWmService.mDisplayManagerInternal.setWindowManagerMirroring( + mDisplayContent.getDisplayId(), false); + handleStartRecordingFailed(); + ProtoLog.v(WM_DEBUG_CONTENT_RECORDING, + "Unable to retrieve window container to start recording for " + + "display %d", mDisplayContent.getDisplayId()); + return null; + } + // TODO(206461622) Migrate to using the RootDisplayArea + return wc.getDisplayContent(); + case RECORD_CONTENT_TASK: + if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_WINDOW_MANAGER, + KEY_RECORD_TASK_FEATURE, false)) { + handleStartRecordingFailed(); + ProtoLog.v(WM_DEBUG_CONTENT_RECORDING, + "Unable to record task since feature is disabled %d", + mDisplayContent.getDisplayId()); + return null; + } + Task taskToRecord = WindowContainer.fromBinder(tokenToRecord).asTask(); + if (taskToRecord == null) { + handleStartRecordingFailed(); + ProtoLog.v(WM_DEBUG_CONTENT_RECORDING, + "Unable to retrieve task to start recording for " + + "display %d", mDisplayContent.getDisplayId()); + } + return taskToRecord; + default: + // Not a valid region, or recording is disabled, so fall back to Display stack + // capture for the entire display. + handleStartRecordingFailed(); + ProtoLog.v(WM_DEBUG_CONTENT_RECORDING, + "Unable to start recording due to invalid region for display %d", + mDisplayContent.getDisplayId()); + return null; + } + } + + /** + * Exit this recording session. + * <p> + * If this is a task session, tear down the recording entirely. Do not fall back + * to recording the entire display on the display stack; this would surprise the user + * given they selected task capture. + * </p><p> + * If this is a display session, just stop recording by layer mirroring. Fall back to recording + * from the display stack. + * </p> + */ + private void handleStartRecordingFailed() { + final boolean shouldExitTaskRecording = mContentRecordingSession != null + && mContentRecordingSession.getContentToRecord() == RECORD_CONTENT_TASK; + if (shouldExitTaskRecording) { + // Clean up the cached session first, since tearing down the display will generate + // display + // events which will trickle back to here. + clearContentRecordingSession(); + tearDownVirtualDisplay(); + } else { + clearContentRecordingSession(); + } + } + + /** + * Ensure recording does not fall back to the display stack; ensure the recording is stopped + * and the client notified by tearing down the virtual display. + */ + private void tearDownVirtualDisplay() { + // TODO(b/219761722) Clean up the VirtualDisplay if task mirroring fails + } + + /** * Apply transformations to the mirrored surface to ensure the captured contents are scaled to * fit and centred in the output surface. * diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 736732029510..f5ace6c78288 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -128,7 +128,6 @@ import static com.android.server.wm.DisplayContentProto.RESUMED_ACTIVITY; import static com.android.server.wm.DisplayContentProto.ROOT_DISPLAY_AREA; import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION; import static com.android.server.wm.DisplayContentProto.SLEEP_TOKENS; -import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION; @@ -5001,10 +5000,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // docked divider while keeping the app itself below the docked divider, so instead // we will put the docked divider below the IME. @see #assignRelativeLayerForImeTargetChild // - // In the case the IME target is animating, the animation Z order may be different - // than the WindowContainer Z order, so it's difficult to be sure we have the correct - // IME target. In this case we just layer the IME over its parent surface. - // // In the case where we have no IME target we let its window parent to place it. // // Keep IME window in surface parent as long as app's starting window @@ -5020,9 +5015,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // We don't need to set relative layer if the IME target in non-multi-window // mode is the activity main window since updateImeParent will ensure the IME // surface be attached on the fullscreen activity. - && imeTarget.mAttrs.type != TYPE_BASE_APPLICATION - && imeTarget.mToken.getActivity(app -> app.isAnimating(TRANSITION | PARENTS, - ANIMATION_TYPE_ALL & ~ANIMATION_TYPE_RECENTS)) == null; + && imeTarget.mAttrs.type != TYPE_BASE_APPLICATION; if (canImeTargetSetRelativeLayer) { mImeWindowsContainer.assignRelativeLayer(t, imeTarget.getSurfaceControl(), // TODO: We need to use an extra level on the app surface to ensure diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index 3951c567e89c..9844cb5fe8f8 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -344,7 +344,6 @@ class InsetsPolicy { // Navigation bar doesn't get influenced by anything else if (type == ITYPE_NAVIGATION_BAR || type == ITYPE_EXTRA_NAVIGATION_BAR) { - state.removeSource(ITYPE_IME); state.removeSource(ITYPE_STATUS_BAR); state.removeSource(ITYPE_CLIMATE_BAR); state.removeSource(ITYPE_CAPTION_BAR); diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index d187bd6f3dfd..2ac41a7c1c1d 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -1459,7 +1459,8 @@ class TaskFragment extends WindowContainer<WindowContainer> { // next activity. final boolean lastResumedCanPip = prev.checkEnterPictureInPictureState( "shouldAutoPipWhilePausing", userLeaving); - if (lastResumedCanPip && prev.pictureInPictureArgs.isAutoEnterEnabled()) { + if (userLeaving && lastResumedCanPip + && prev.pictureInPictureArgs.isAutoEnterEnabled()) { shouldAutoPip = true; } else if (!lastResumedCanPip) { // If the flag RESUME_WHILE_PAUSING is set, then continue to schedule the previous diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index c50888b5a172..eb88b8b0ea97 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -3278,7 +3278,7 @@ public class WindowManagerService extends IWindowManager.Stub mContext.enforceCallingOrSelfPermission( Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE, Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE - + " permission required to read keyguard visibility"); + + " permission required to subscribe to keyguard locked state changes"); } private void dispatchKeyguardLockedState() { diff --git a/services/core/jni/gnss/MeasurementCorrections.cpp b/services/core/jni/gnss/MeasurementCorrections.cpp index 8a3d84cbe9c6..07d0a45e9c40 100644 --- a/services/core/jni/gnss/MeasurementCorrections.cpp +++ b/services/core/jni/gnss/MeasurementCorrections.cpp @@ -44,6 +44,7 @@ using SingleSatCorrection_Aidl = using ReflectingPlane_V1_0 = android::hardware::gnss::measurement_corrections::V1_0::ReflectingPlane; using ReflectingPlane_Aidl = android::hardware::gnss::measurement_corrections::ReflectingPlane; +using ExcessPathInfo = SingleSatCorrection_Aidl::ExcessPathInfo; using GnssConstellationType_V1_0 = android::hardware::gnss::V1_0::GnssConstellationType; using GnssConstellationType_V2_0 = android::hardware::gnss::V2_0::GnssConstellationType; using GnssConstellationType_Aidl = android::hardware::gnss::GnssConstellationType; @@ -62,7 +63,7 @@ jmethodID method_correctionsHasEnvironmentBearing; jmethodID method_correctionsGetEnvironmentBearingDegrees; jmethodID method_correctionsGetEnvironmentBearingUncertaintyDegrees; jmethodID method_listSize; -jmethodID method_correctionListGet; +jmethodID method_listGet; jmethodID method_correctionSatFlags; jmethodID method_correctionSatConstType; jmethodID method_correctionSatId; @@ -71,10 +72,17 @@ jmethodID method_correctionSatIsLosProb; jmethodID method_correctionSatEpl; jmethodID method_correctionSatEplUnc; jmethodID method_correctionSatRefPlane; +jmethodID method_correctionSatAttenuation; +jmethodID method_correctionSatExcessPathInfoList; jmethodID method_correctionPlaneLatDeg; jmethodID method_correctionPlaneLngDeg; jmethodID method_correctionPlaneAltDeg; jmethodID method_correctionPlaneAzimDeg; +jmethodID method_excessPathInfoFlags; +jmethodID method_excessPathInfoEpl; +jmethodID method_excessPathInfoEplUnc; +jmethodID method_excessPathInfoRefPlane; +jmethodID method_excessPathInfoAttenuation; } // anonymous namespace void MeasurementCorrections_class_init_once(JNIEnv* env, jclass clazz) { @@ -103,7 +111,7 @@ void MeasurementCorrections_class_init_once(JNIEnv* env, jclass clazz) { jclass corrListClass = env->FindClass("java/util/List"); method_listSize = env->GetMethodID(corrListClass, "size", "()I"); - method_correctionListGet = env->GetMethodID(corrListClass, "get", "(I)Ljava/lang/Object;"); + method_listGet = env->GetMethodID(corrListClass, "get", "(I)Ljava/lang/Object;"); jclass singleSatCorrClass = env->FindClass("android/location/GnssSingleSatCorrection"); method_correctionSatFlags = @@ -121,12 +129,27 @@ void MeasurementCorrections_class_init_once(JNIEnv* env, jclass clazz) { env->GetMethodID(singleSatCorrClass, "getExcessPathLengthUncertaintyMeters", "()F"); method_correctionSatRefPlane = env->GetMethodID(singleSatCorrClass, "getReflectingPlane", "()Landroid/location/GnssReflectingPlane;"); + method_correctionSatAttenuation = + env->GetMethodID(singleSatCorrClass, "getCombinedAttenuationDb", "()F"); + method_correctionSatExcessPathInfoList = + env->GetMethodID(singleSatCorrClass, "getGnssExcessPathInfoList", "()Ljava/util/List;"); jclass refPlaneClass = env->FindClass("android/location/GnssReflectingPlane"); method_correctionPlaneLatDeg = env->GetMethodID(refPlaneClass, "getLatitudeDegrees", "()D"); method_correctionPlaneLngDeg = env->GetMethodID(refPlaneClass, "getLongitudeDegrees", "()D"); method_correctionPlaneAltDeg = env->GetMethodID(refPlaneClass, "getAltitudeMeters", "()D"); method_correctionPlaneAzimDeg = env->GetMethodID(refPlaneClass, "getAzimuthDegrees", "()D"); + + jclass excessPathInfoClass = env->FindClass("android/location/GnssExcessPathInfo"); + method_excessPathInfoFlags = env->GetMethodID(excessPathInfoClass, "getFlags", "()I"); + method_excessPathInfoEpl = + env->GetMethodID(excessPathInfoClass, "getExcessPathLengthMeters", "()F"); + method_excessPathInfoEplUnc = + env->GetMethodID(excessPathInfoClass, "getExcessPathLengthUncertaintyMeters", "()F"); + method_excessPathInfoRefPlane = env->GetMethodID(excessPathInfoClass, "getReflectingPlane", + "()Landroid/location/GnssReflectingPlane;"); + method_excessPathInfoAttenuation = + env->GetMethodID(excessPathInfoClass, "getAttenuationDb", "()F"); } template <> @@ -324,7 +347,8 @@ jboolean MeasurementCorrectionsIface_V1_1::setCallback( SingleSatCorrection_V1_0 MeasurementCorrectionsUtil::getSingleSatCorrection_1_0_withoutConstellation( JNIEnv* env, jobject singleSatCorrectionObj) { - jint correctionFlags = env->CallIntMethod(singleSatCorrectionObj, method_correctionSatFlags); + uint16_t corrFlags = static_cast<uint16_t>( + env->CallIntMethod(singleSatCorrectionObj, method_correctionSatFlags)); jint satId = env->CallIntMethod(singleSatCorrectionObj, method_correctionSatId); jfloat carrierFreqHz = env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatCarrierFreq); @@ -332,14 +356,16 @@ MeasurementCorrectionsUtil::getSingleSatCorrection_1_0_withoutConstellation( env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatIsLosProb); jfloat eplMeters = env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatEpl); jfloat eplUncMeters = env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatEplUnc); - uint16_t corrFlags = static_cast<uint16_t>(correctionFlags); ReflectingPlane_V1_0 reflectingPlane; - if ((corrFlags & GnssSingleSatCorrectionFlags_V1_0::HAS_REFLECTING_PLANE) != 0) - MeasurementCorrectionsUtil::getReflectingPlane<ReflectingPlane_V1_0>(env, - singleSatCorrectionObj, + if ((corrFlags & GnssSingleSatCorrectionFlags_V1_0::HAS_REFLECTING_PLANE) != 0) { + jobject reflectingPlaneObj = + env->CallObjectMethod(singleSatCorrectionObj, method_correctionSatRefPlane); + MeasurementCorrectionsUtil::setReflectingPlane<ReflectingPlane_V1_0>(env, + reflectingPlaneObj, reflectingPlane); - + env->DeleteLocalRef(reflectingPlaneObj); + } SingleSatCorrection_V1_0 singleSatCorrection = { .singleSatCorrectionFlags = corrFlags, .svid = static_cast<uint16_t>(satId), @@ -349,13 +375,14 @@ MeasurementCorrectionsUtil::getSingleSatCorrection_1_0_withoutConstellation( .excessPathLengthUncertaintyMeters = eplUncMeters, .reflectingPlane = reflectingPlane, }; - return singleSatCorrection; } SingleSatCorrection_Aidl MeasurementCorrectionsUtil::getSingleSatCorrection_Aidl( JNIEnv* env, jobject singleSatCorrectionObj) { - jint correctionFlags = env->CallIntMethod(singleSatCorrectionObj, method_correctionSatFlags); + int32_t corrFlags = static_cast<int32_t>( + env->CallIntMethod(singleSatCorrectionObj, method_correctionSatFlags)); + jint constType = env->CallIntMethod(singleSatCorrectionObj, method_correctionSatConstType); jint satId = env->CallIntMethod(singleSatCorrectionObj, method_correctionSatId); jfloat carrierFreqHz = env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatCarrierFreq); @@ -363,15 +390,10 @@ SingleSatCorrection_Aidl MeasurementCorrectionsUtil::getSingleSatCorrection_Aidl env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatIsLosProb); jfloat eplMeters = env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatEpl); jfloat eplUncMeters = env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatEplUnc); - int32_t corrFlags = static_cast<int32_t>(correctionFlags); - - ReflectingPlane_Aidl reflectingPlane; - if ((corrFlags & SingleSatCorrection_Aidl::SINGLE_SAT_CORRECTION_HAS_REFLECTING_PLANE) != 0) - MeasurementCorrectionsUtil::getReflectingPlane<ReflectingPlane_Aidl>(env, - singleSatCorrectionObj, - reflectingPlane); - - jint constType = env->CallIntMethod(singleSatCorrectionObj, method_correctionSatConstType); + jfloat attenuationDb = + env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatAttenuation); + std::vector<ExcessPathInfo> excessPathInfos = + MeasurementCorrectionsUtil::getExcessPathInfoList(env, singleSatCorrectionObj); SingleSatCorrection_Aidl singleSatCorrection; singleSatCorrection.singleSatCorrectionFlags = corrFlags; @@ -379,9 +401,10 @@ SingleSatCorrection_Aidl MeasurementCorrectionsUtil::getSingleSatCorrection_Aidl singleSatCorrection.svid = static_cast<int32_t>(satId); singleSatCorrection.carrierFrequencyHz = carrierFreqHz; singleSatCorrection.probSatIsLos = probSatIsLos; - singleSatCorrection.excessPathLengthMeters = eplMeters; - singleSatCorrection.excessPathLengthUncertaintyMeters = eplUncMeters; - singleSatCorrection.reflectingPlane = reflectingPlane; + singleSatCorrection.combinedExcessPathLengthMeters = eplMeters; + singleSatCorrection.combinedExcessPathLengthUncertaintyMeters = eplUncMeters; + singleSatCorrection.combinedAttenuationDb = attenuationDb; + singleSatCorrection.excessPathInfos = excessPathInfos; return singleSatCorrection; } @@ -391,8 +414,7 @@ void MeasurementCorrectionsUtil::getSingleSatCorrectionList_1_0( hardware::hidl_vec<SingleSatCorrection_V1_0>& list) { for (uint16_t i = 0; i < list.size(); ++i) { jobject singleSatCorrectionObj = - env->CallObjectMethod(singleSatCorrectionList, method_correctionListGet, i); - + env->CallObjectMethod(singleSatCorrectionList, method_listGet, i); SingleSatCorrection_V1_0 singleSatCorrection = getSingleSatCorrection_1_0_withoutConstellation(env, singleSatCorrectionObj); @@ -410,7 +432,7 @@ void MeasurementCorrectionsUtil::getSingleSatCorrectionList_1_1( hardware::hidl_vec<SingleSatCorrection_V1_1>& list) { for (uint16_t i = 0; i < list.size(); ++i) { jobject singleSatCorrectionObj = - env->CallObjectMethod(singleSatCorrectionList, method_correctionListGet, i); + env->CallObjectMethod(singleSatCorrectionList, method_listGet, i); SingleSatCorrection_V1_0 singleSatCorrection_1_0 = getSingleSatCorrection_1_0_withoutConstellation(env, singleSatCorrectionObj); @@ -431,7 +453,7 @@ void MeasurementCorrectionsUtil::getSingleSatCorrectionList_Aidl( JNIEnv* env, jobject singleSatCorrectionList, std::vector<SingleSatCorrection_Aidl>& list) { for (uint16_t i = 0; i < list.size(); ++i) { jobject singleSatCorrectionObj = - env->CallObjectMethod(singleSatCorrectionList, method_correctionListGet, i); + env->CallObjectMethod(singleSatCorrectionList, method_listGet, i); SingleSatCorrection_Aidl singleSatCorrection_Aidl = getSingleSatCorrection_Aidl(env, singleSatCorrectionObj); @@ -441,4 +463,63 @@ void MeasurementCorrectionsUtil::getSingleSatCorrectionList_Aidl( } } +template <> +void MeasurementCorrectionsUtil::setReflectingPlaneAzimuthDegrees<ReflectingPlane_V1_0>( + ReflectingPlane_V1_0& reflectingPlane, double azimuthDegreeRefPlane) { + reflectingPlane.azimuthDegrees = azimuthDegreeRefPlane; +} + +template <> +void MeasurementCorrectionsUtil::setReflectingPlaneAzimuthDegrees<ReflectingPlane_Aidl>( + ReflectingPlane_Aidl& reflectingPlane, double azimuthDegreeRefPlane) { + reflectingPlane.reflectingPlaneAzimuthDegrees = azimuthDegreeRefPlane; +} + +std::vector<ExcessPathInfo> MeasurementCorrectionsUtil::getExcessPathInfoList( + JNIEnv* env, jobject singleSatCorrectionObj) { + jobject excessPathInfoListObj = + env->CallObjectMethod(singleSatCorrectionObj, method_correctionSatExcessPathInfoList); + + int len = env->CallIntMethod(excessPathInfoListObj, method_listSize); + std::vector<ExcessPathInfo> list(len); + for (int i = 0; i < len; ++i) { + jobject excessPathInfoObj = env->CallObjectMethod(excessPathInfoListObj, method_listGet, i); + list[i] = getExcessPathInfo(env, excessPathInfoObj); + env->DeleteLocalRef(excessPathInfoObj); + } + env->DeleteLocalRef(excessPathInfoListObj); + return list; +} + +ExcessPathInfo MeasurementCorrectionsUtil::getExcessPathInfo(JNIEnv* env, + jobject excessPathInfoObj) { + ExcessPathInfo excessPathInfo; + jint flags = env->CallIntMethod(excessPathInfoObj, method_excessPathInfoFlags); + excessPathInfo.excessPathInfoFlags = flags; + if ((flags & ExcessPathInfo::EXCESS_PATH_INFO_HAS_EXCESS_PATH_LENGTH) != 0) { + jfloat epl = env->CallFloatMethod(excessPathInfoObj, method_excessPathInfoEpl); + excessPathInfo.excessPathLengthMeters = epl; + } + if ((flags & ExcessPathInfo::EXCESS_PATH_INFO_HAS_EXCESS_PATH_LENGTH_UNC) != 0) { + jfloat eplUnc = env->CallFloatMethod(excessPathInfoObj, method_excessPathInfoEplUnc); + excessPathInfo.excessPathLengthUncertaintyMeters = eplUnc; + } + if ((flags & ExcessPathInfo::EXCESS_PATH_INFO_HAS_REFLECTING_PLANE) != 0) { + ReflectingPlane_Aidl reflectingPlane; + jobject reflectingPlaneObj = + env->CallObjectMethod(excessPathInfoObj, method_excessPathInfoRefPlane); + MeasurementCorrectionsUtil::setReflectingPlane<ReflectingPlane_Aidl>(env, + reflectingPlaneObj, + reflectingPlane); + env->DeleteLocalRef(reflectingPlaneObj); + excessPathInfo.reflectingPlane = reflectingPlane; + } + if ((flags & ExcessPathInfo::EXCESS_PATH_INFO_HAS_ATTENUATION) != 0) { + jfloat attenuation = + env->CallFloatMethod(excessPathInfoObj, method_excessPathInfoAttenuation); + excessPathInfo.attenuationDb = attenuation; + } + return excessPathInfo; +} + } // namespace android::gnss diff --git a/services/core/jni/gnss/MeasurementCorrections.h b/services/core/jni/gnss/MeasurementCorrections.h index a2e602714326..598ad48d6ac1 100644 --- a/services/core/jni/gnss/MeasurementCorrections.h +++ b/services/core/jni/gnss/MeasurementCorrections.h @@ -43,7 +43,7 @@ extern jmethodID method_correctionsHasEnvironmentBearing; extern jmethodID method_correctionsGetEnvironmentBearingDegrees; extern jmethodID method_correctionsGetEnvironmentBearingUncertaintyDegrees; extern jmethodID method_listSize; -extern jmethodID method_correctionListGet; +extern jmethodID method_listGet; extern jmethodID method_correctionSatFlags; extern jmethodID method_correctionSatConstType; extern jmethodID method_correctionSatId; @@ -52,6 +52,7 @@ extern jmethodID method_correctionSatIsLosProb; extern jmethodID method_correctionSatEpl; extern jmethodID method_correctionSatEplUnc; extern jmethodID method_correctionSatRefPlane; +extern jmethodID method_correctionSatExcessPathInfos; extern jmethodID method_correctionPlaneLatDeg; extern jmethodID method_correctionPlaneLngDeg; extern jmethodID method_correctionPlaneAltDeg; @@ -130,14 +131,20 @@ struct MeasurementCorrectionsUtil { static bool translateMeasurementCorrections(JNIEnv* env, jobject correctionsObj, T& corrections); template <class T> - static void getReflectingPlane(JNIEnv* env, jobject singleSatCorrectionObj, T& reflectingPlane); + static void setReflectingPlane(JNIEnv* env, jobject reflectingPlaneObj, T& reflectingPlane); + template <class T> + static void setReflectingPlaneAzimuthDegrees(T& reflectingPlane, double azimuthDegreeRefPlane); + + static std::vector< + android::hardware::gnss::measurement_corrections::SingleSatCorrection::ExcessPathInfo> + getExcessPathInfoList(JNIEnv* env, jobject correctionsObj); + static android::hardware::gnss::measurement_corrections::SingleSatCorrection::ExcessPathInfo + getExcessPathInfo(JNIEnv* env, jobject correctionsObj); }; template <class T> -void MeasurementCorrectionsUtil::getReflectingPlane(JNIEnv* env, jobject singleSatCorrectionObj, +void MeasurementCorrectionsUtil::setReflectingPlane(JNIEnv* env, jobject reflectingPlaneObj, T& reflectingPlane) { - jobject reflectingPlaneObj = - env->CallObjectMethod(singleSatCorrectionObj, method_correctionSatRefPlane); jdouble latitudeDegreesRefPlane = env->CallDoubleMethod(reflectingPlaneObj, method_correctionPlaneLatDeg); jdouble longitudeDegreesRefPlane = @@ -149,8 +156,7 @@ void MeasurementCorrectionsUtil::getReflectingPlane(JNIEnv* env, jobject singleS reflectingPlane.latitudeDegrees = latitudeDegreesRefPlane; reflectingPlane.longitudeDegrees = longitudeDegreesRefPlane; reflectingPlane.altitudeMeters = altitudeDegreesRefPlane; - reflectingPlane.azimuthDegrees = azimuthDegreeRefPlane; - env->DeleteLocalRef(reflectingPlaneObj); + setReflectingPlaneAzimuthDegrees<T>(reflectingPlane, azimuthDegreeRefPlane); } } // namespace android::gnss diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java index 2f5ab0b31332..48c40523e9c2 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java @@ -1227,5 +1227,12 @@ class ActiveAdmin { pw.print("mSsidDenylist="); pw.println(mSsidDenylist); + + if (mFactoryResetProtectionPolicy != null) { + pw.println("mFactoryResetProtectionPolicy:"); + pw.increaseIndent(); + mFactoryResetProtectionPolicy.dump(pw); + pw.decreaseIndent(); + } } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java index ba00beea47cc..edfd6ed5f63d 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java @@ -15,6 +15,7 @@ */ package com.android.server.devicepolicy; +import android.accounts.Account; import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.admin.DevicePolicyDrawableResource; @@ -138,6 +139,11 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { return null; } + public void finalizeWorkProfileProvisioning( + UserHandle managedProfileUser, Account migratedAccount) { + + } + public void provisionFullyManagedDevice( FullyManagedDeviceProvisioningParams provisioningParams, String callerPackage) { } @@ -171,7 +177,7 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { public void setDrawables(@NonNull List<DevicePolicyDrawableResource> drawables){} @Override - public void resetDrawables(@NonNull String[] drawableIds){} + public void resetDrawables(@NonNull List<String> drawableIds){} @Override public ParcelableResource getDrawable( @@ -183,7 +189,7 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { public void setStrings(@NonNull List<DevicePolicyStringResource> strings){} @Override - public void resetStrings(String[] stringIds){} + public void resetStrings(@NonNull List<String> stringIds){} @Override public ParcelableResource getString(String stringId) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java index e70c071183ce..6f0330e97482 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java @@ -176,11 +176,11 @@ class DeviceManagementResourcesProvider { /** * Returns {@code false} if no resources were removed. */ - boolean removeDrawables(@NonNull String[] drawableIds) { + boolean removeDrawables(@NonNull List<String> drawableIds) { synchronized (mLock) { boolean removed = false; - for (int i = 0; i < drawableIds.length; i++) { - String drawableId = drawableIds[i]; + for (int i = 0; i < drawableIds.size(); i++) { + String drawableId = drawableIds.get(i); removed |= mUpdatedDrawablesForStyle.remove(drawableId) != null || mUpdatedDrawablesForSource.remove(drawableId) != null; } @@ -265,11 +265,11 @@ class DeviceManagementResourcesProvider { /** * Returns {@code false} if no resources were removed. */ - boolean removeStrings(@NonNull String[] stringIds) { + boolean removeStrings(@NonNull List<String> stringIds) { synchronized (mLock) { boolean removed = false; - for (int i = 0; i < stringIds.length; i++) { - String stringId = stringIds[i]; + for (int i = 0; i < stringIds.size(); i++) { + String stringId = stringIds.get(i); removed |= mUpdatedStrings.remove(stringId) != null; } if (!removed) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 3a98e4e0babf..e6b57e052293 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -27,6 +27,7 @@ import static android.app.admin.DeviceAdminReceiver.ACTION_COMPLIANCE_ACKNOWLEDG import static android.app.admin.DeviceAdminReceiver.EXTRA_TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE; import static android.app.admin.DevicePolicyManager.ACTION_CHECK_POLICY_COMPLIANCE; import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_RESOURCE_UPDATED; +import static android.app.admin.DevicePolicyManager.ACTION_MANAGED_PROFILE_PROVISIONED; import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE; import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE; import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER; @@ -45,6 +46,7 @@ import static android.app.admin.DevicePolicyManager.DELEGATION_SECURITY_LOGGING; import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT; import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED; import static android.app.admin.DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER; +import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE; import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_IDS; import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_TYPE; import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_TYPE_DRAWABLE; @@ -2122,7 +2124,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { + "profile: %d", doUserId, poUserId); Slogf.i(LOG_TAG, "Giving the PO additional power..."); - markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(poAdminComponent, poUserId); + setProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(poAdminComponent, poUserId, true); Slogf.i(LOG_TAG, "Migrating DO policies to PO..."); moveDoPoliciesToProfileParentAdminLocked(doAdmin, poAdmin.getParentActiveAdmin()); migratePersonalAppSuspensionLocked(doUserId, poUserId, poAdmin); @@ -14772,7 +14774,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override - public void markProfileOwnerOnOrganizationOwnedDevice(ComponentName who, int userId) { + public void setProfileOwnerOnOrganizationOwnedDevice(ComponentName who, int userId, + boolean isProfileOwnerOnOrganizationOwnedDevice) { if (!mHasFeature) { return; } @@ -14804,13 +14807,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // Grant access under lock. synchronized (getLockObject()) { - markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(who, userId); + setProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(who, userId, + isProfileOwnerOnOrganizationOwnedDevice); } } @GuardedBy("getLockObject()") - private void markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked( - ComponentName who, int userId) { + private void setProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked( + ComponentName who, int userId, boolean isProfileOwnerOnOrganizationOwnedDevice) { // Make sure that the user has a profile owner and that the specified // component is the profile owner of that user. if (!isProfileOwner(who, userId)) { @@ -14819,7 +14823,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { who.flattenToString(), userId)); } - Slogf.i(LOG_TAG, "Marking %s as profile owner on organization-owned device for user %d", + Slogf.i(LOG_TAG, "%s %s as profile owner on organization-owned device for user %d", + isProfileOwnerOnOrganizationOwnedDevice ? "Marking" : "Unmarking", who.flattenToString(), userId); // First, set restriction on removing the profile. @@ -14836,15 +14841,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { + " on user %d", parentUser.getIdentifier())); } - mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, true, + mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, + isProfileOwnerOnOrganizationOwnedDevice, parentUser); - mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, true, + mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, + isProfileOwnerOnOrganizationOwnedDevice, parentUser); }); - // markProfileOwnerOfOrganizationOwnedDevice will trigger writing of the profile owner + // setProfileOwnerOfOrganizationOwnedDevice will trigger writing of the profile owner // data, no need to do it manually. - mOwners.markProfileOwnerOfOrganizationOwnedDevice(userId); + mOwners.setProfileOwnerOfOrganizationOwnedDevice(userId, + isProfileOwnerOnOrganizationOwnedDevice); } private void pushMeteredDisabledPackagesLocked(int userId) { @@ -17781,7 +17789,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (provisioningParams.isOrganizationOwnedProvisioning()) { synchronized (getLockObject()) { - markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(admin, userInfo.id); + setProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(admin, userInfo.id, + true); } } @@ -17808,6 +17817,35 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } + @Override + public void finalizeWorkProfileProvisioning(UserHandle managedProfileUser, + Account migratedAccount) { + Preconditions.checkCallAuthorization( + hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)); + + if (!isManagedProfile(managedProfileUser.getIdentifier())) { + throw new IllegalStateException("Given user is not a managed profile"); + } + ComponentName profileOwnerComponent = + mOwners.getProfileOwnerComponent(managedProfileUser.getIdentifier()); + if (profileOwnerComponent == null) { + throw new IllegalStateException("There is no profile owner on the given profile"); + } + Intent primaryProfileSuccessIntent = new Intent(ACTION_MANAGED_PROFILE_PROVISIONED); + primaryProfileSuccessIntent.setPackage(profileOwnerComponent.getPackageName()); + primaryProfileSuccessIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES + | Intent.FLAG_RECEIVER_FOREGROUND); + primaryProfileSuccessIntent.putExtra(Intent.EXTRA_USER, managedProfileUser); + + if (migratedAccount != null) { + primaryProfileSuccessIntent.putExtra(EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE, + migratedAccount); + } + + mContext.sendBroadcastAsUser(primaryProfileSuccessIntent, + UserHandle.of(getProfileParentId(managedProfileUser.getIdentifier()))); + } + /** * Callback called at the beginning of {@link #createAndProvisionManagedProfile( * ManagedProfileProvisioningParams, String)} after the relevant prechecks have passed. @@ -18384,14 +18422,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } else { preferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT); } - List<Integer> allowedUids = Arrays.stream( - preferentialNetworkServiceConfig.getIncludedUids()).boxed().collect( - Collectors.toList()); - List<Integer> excludedUids = Arrays.stream( - preferentialNetworkServiceConfig.getExcludedUids()).boxed().collect( - Collectors.toList()); - preferenceBuilder.setIncludedUids(allowedUids); - preferenceBuilder.setExcludedUids(excludedUids); + preferenceBuilder.setIncludedUids(preferentialNetworkServiceConfig.getIncludedUids()); + preferenceBuilder.setExcludedUids(preferentialNetworkServiceConfig.getExcludedUids()); preferenceBuilder.setPreferenceEnterpriseId( preferentialNetworkServiceConfig.getNetworkId()); @@ -18688,13 +18720,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mInjector.binderWithCleanCallingIdentity(() -> { if (mDeviceManagementResourcesProvider.updateDrawables(drawables)) { sendDrawableUpdatedBroadcast( - drawables.stream().map(s -> s.getDrawableId()).toArray(String[]::new)); + drawables.stream().map(s -> s.getDrawableId()).collect( + Collectors.toList())); } }); } @Override - public void resetDrawables(@NonNull String[] drawableIds) { + public void resetDrawables(@NonNull List<String> drawableIds) { Preconditions.checkCallAuthorization(hasCallingOrSelfPermission( android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES)); @@ -18715,7 +18748,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { drawableId, drawableStyle, drawableSource)); } - private void sendDrawableUpdatedBroadcast(String[] drawableIds) { + private void sendDrawableUpdatedBroadcast(List<String> drawableIds) { sendResourceUpdatedBroadcast(EXTRA_RESOURCE_TYPE_DRAWABLE, drawableIds); } @@ -18729,12 +18762,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mInjector.binderWithCleanCallingIdentity(() -> { if (mDeviceManagementResourcesProvider.updateStrings(strings)) sendStringsUpdatedBroadcast( - strings.stream().map(s -> s.getStringId()).toArray(String[]::new)); + strings.stream().map(s -> s.getStringId()).collect(Collectors.toList())); }); } @Override - public void resetStrings(String[] stringIds) { + public void resetStrings(@NonNull List<String> stringIds) { Preconditions.checkCallAuthorization(hasCallingOrSelfPermission( android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES)); @@ -18751,13 +18784,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mDeviceManagementResourcesProvider.getString(stringId)); } - private void sendStringsUpdatedBroadcast(String[] stringIds) { + private void sendStringsUpdatedBroadcast(List<String> stringIds) { sendResourceUpdatedBroadcast(EXTRA_RESOURCE_TYPE_STRING, stringIds); } - private void sendResourceUpdatedBroadcast(int resourceType, String[] resourceIds) { + private void sendResourceUpdatedBroadcast(int resourceType, List<String> resourceIds) { final Intent intent = new Intent(ACTION_DEVICE_POLICY_RESOURCE_UPDATED); - intent.putExtra(EXTRA_RESOURCE_IDS, resourceIds); + intent.putExtra(EXTRA_RESOURCE_IDS, resourceIds.toArray(String[]::new)); intent.putExtra(EXTRA_RESOURCE_TYPE, resourceType); intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java index e1d720ca25c8..1fa2f53bea17 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java @@ -340,7 +340,7 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand { private int runMarkProfileOwnerOnOrganizationOwnedDevice(PrintWriter pw) { parseArgs(/* canHaveName= */ false); - mService.markProfileOwnerOnOrganizationOwnedDevice(mComponent, mUserId); + mService.setProfileOwnerOnOrganizationOwnedDevice(mComponent, mUserId, true); pw.printf("Success\n"); return 0; } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java index fe8f2235ed63..b0fdd723347f 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java @@ -620,18 +620,15 @@ class Owners { } } - /** - * Sets the indicator that the profile owner manages an organization-owned device, - * then write to file. - */ - void markProfileOwnerOfOrganizationOwnedDevice(int userId) { + /** Set whether the profile owner manages an organization-owned device, then write to file. */ + void setProfileOwnerOfOrganizationOwnedDevice(int userId, boolean isOrganizationOwnedDevice) { synchronized (mLock) { OwnerInfo profileOwner = mProfileOwners.get(userId); if (profileOwner != null) { - profileOwner.isOrganizationOwnedDevice = true; + profileOwner.isOrganizationOwnedDevice = isOrganizationOwnedDevice; } else { Slog.e(TAG, String.format( - "No profile owner for user %d to set as org-owned.", userId)); + "No profile owner for user %d to set org-owned flag.", userId)); } writeProfileOwner(userId); } diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt index 8f81e930d0cd..7a9c41256d61 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt @@ -143,6 +143,7 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag AndroidPackage::getLogo, AndroidPackage::getLocaleConfigRes, AndroidPackage::getManageSpaceActivityName, + AndroidPackage::getMaxSdkVersion, AndroidPackage::getMemtagMode, AndroidPackage::getMinSdkVersion, AndroidPackage::getNativeHeapZeroInitialized, diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java index 053551309661..8bab6d68c20e 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java @@ -119,6 +119,7 @@ import android.util.Pair; import androidx.test.runner.AndroidJUnit4; import com.android.internal.R; +import com.android.internal.app.IAppOpsService; import com.android.server.AppStateTracker; import com.android.server.DeviceIdleInternal; import com.android.server.am.AppBatteryExemptionTracker.AppBatteryExemptionPolicy; @@ -231,6 +232,7 @@ public final class BackgroundRestrictionTest { @Mock private MediaSessionManager mMediaSessionManager; @Mock private RoleManager mRoleManager; @Mock private TelephonyManager mTelephonyManager; + @Mock private IAppOpsService mIAppOpsService; private long mCurrentTimeMillis; @@ -2748,6 +2750,11 @@ public final class BackgroundRestrictionTest { RoleManager getRoleManager() { return BackgroundRestrictionTest.this.mRoleManager; } + + @Override + IAppOpsService getIAppOpsService() { + return BackgroundRestrictionTest.this.mIAppOpsService; + } } private class TestAppBatteryTrackerInjector extends TestBaseTrackerInjector<AppBatteryPolicy> { diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java index 784f732ba3b1..8d6269c93764 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java @@ -118,6 +118,7 @@ public class LocalDisplayAdapterTest { LocalServices.removeServiceForTest(LightsManager.class); LocalServices.addService(LightsManager.class, mMockedLightsManager); mInjector = new Injector(); + when(mSurfaceControlProxy.getBootDisplayModeSupport()).thenReturn(true); mAdapter = new LocalDisplayAdapter(mMockedSyncRoot, mMockedContext, mHandler, mListener, mInjector); spyOn(mAdapter); @@ -904,7 +905,6 @@ public class LocalDisplayAdapterTest { .thenReturn(display.dynamicInfo); when(mSurfaceControlProxy.getDesiredDisplayModeSpecs(display.token)) .thenReturn(display.desiredDisplayModeSpecs); - when(mSurfaceControlProxy.getBootDisplayModeSupport()).thenReturn(true); } private void updateAvailableDisplays() { diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java index 6f503c7dd941..444db9128662 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java @@ -19,7 +19,9 @@ package com.android.server.pm; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; @@ -36,23 +38,21 @@ import android.content.Intent; import android.content.IntentFilter; import android.os.HandlerThread; import android.os.PowerManager; -import android.util.ArraySet; import com.android.server.LocalServices; import com.android.server.PinnerService; import com.android.server.pm.dex.DexManager; -import com.android.server.pm.dex.DexoptOptions; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import java.util.Arrays; -import java.util.HashMap; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.stream.Collectors; @@ -66,9 +66,8 @@ public final class BackgroundDexOptServiceUnitTest { private static final long TEST_WAIT_TIMEOUT_MS = 10_000; - private static final ArraySet<String> DEFAULT_PACKAGE_LIST = new ArraySet<>( - Arrays.asList("aaa", "bbb")); - private static final ArraySet<String> EMPTY_PACKAGE_LIST = new ArraySet<>(); + private static final List<String> DEFAULT_PACKAGE_LIST = List.of("aaa", "bbb"); + private static final List<String> EMPTY_PACKAGE_LIST = List.of(); @Mock private Context mContext; @@ -116,9 +115,11 @@ public final class BackgroundDexOptServiceUnitTest { when(mInjector.getDataDirStorageLowBytes()).thenReturn(STORAGE_LOW_BYTES); when(mInjector.getDexOptThermalCutoff()).thenReturn(PowerManager.THERMAL_STATUS_CRITICAL); when(mInjector.getCurrentThermalStatus()).thenReturn(PowerManager.THERMAL_STATUS_NONE); + when(mInjector.supportSecondaryDex()).thenReturn(true); when(mDexOptHelper.getOptimizablePackages(any())).thenReturn(DEFAULT_PACKAGE_LIST); when(mDexOptHelper.performDexOptWithStatus(any())).thenReturn( PackageDexOptimizer.DEX_OPT_PERFORMED); + when(mDexOptHelper.performDexOpt(any())).thenReturn(true); mService = new BackgroundDexOptService(mInjector); } @@ -418,26 +419,16 @@ public final class BackgroundDexOptServiceUnitTest { verifyPerformDexOpt(DEFAULT_PACKAGE_LIST, totalJobRuns); } - private void verifyPerformDexOpt(ArraySet<String> pkgs, int expectedRuns) { - ArgumentCaptor<DexoptOptions> dexOptOptions = ArgumentCaptor.forClass(DexoptOptions.class); - verify(mDexOptHelper, atLeastOnce()).performDexOptWithStatus(dexOptOptions.capture()); - HashMap<String, Integer> primaryPkgs = new HashMap<>(); // K: pkg, V: dexopt runs left - for (String pkg : pkgs) { - primaryPkgs.put(pkg, expectedRuns); - } - - for (DexoptOptions opt : dexOptOptions.getAllValues()) { - assertThat(pkgs).contains(opt.getPackageName()); - assertThat(opt.isDexoptOnlySecondaryDex()).isFalse(); - Integer count = primaryPkgs.get(opt.getPackageName()); - assertThat(count).isNotNull(); - if (count == 1) { - primaryPkgs.remove(opt.getPackageName()); - } else { - primaryPkgs.put(opt.getPackageName(), count - 1); + private void verifyPerformDexOpt(List<String> pkgs, int expectedRuns) { + InOrder inOrder = inOrder(mDexOptHelper); + for (int i = 0; i < expectedRuns; i++) { + for (String pkg : pkgs) { + inOrder.verify(mDexOptHelper, times(1)).performDexOptWithStatus(argThat((option) -> + option.getPackageName().equals(pkg) && !option.isDexoptOnlySecondaryDex())); + inOrder.verify(mDexOptHelper, times(1)).performDexOpt(argThat((option) -> + option.getPackageName().equals(pkg) && option.isDexoptOnlySecondaryDex())); } } - assertThat(primaryPkgs).isEmpty(); } private static class StartAndWaitThread extends Thread { diff --git a/services/tests/mockingservicestests/src/com/android/server/utils/TimingsTraceAndSlogTest.java b/services/tests/mockingservicestests/src/com/android/server/utils/TimingsTraceAndSlogTest.java new file mode 100644 index 000000000000..52cd29cabb94 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/utils/TimingsTraceAndSlogTest.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.utils; + +import static android.os.Trace.TRACE_TAG_APP; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.contains; +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.matches; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; + +import android.os.Trace; +import android.util.Slog; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.dx.mockito.inline.extended.MockedVoidMethod; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoSession; + +import java.util.ArrayList; +import java.util.List; + +/** + * Tests for {@link TimingsTraceAndSlog}. + * + * <p>Usage: {@code atest FrameworksMockingServicesTests:TimingsTraceAndSlogTest} + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class TimingsTraceAndSlogTest { + + private static final String TAG = "TEST"; + + private MockitoSession mSession; + + @Before + public final void startMockSession() { + mSession = mockitoSession() + .spyStatic(Slog.class) + .spyStatic(Trace.class) + .startMocking(); + } + + @After + public final void finishMockSession() { + mSession.finishMocking(); + } + + @Test + public void testDifferentThreads() throws Exception { + TimingsTraceAndSlog log = new TimingsTraceAndSlog(TAG, TRACE_TAG_APP); + // Should be able to log on the same thread + log.traceBegin("test"); + log.traceEnd(); + final List<String> errors = new ArrayList<>(); + // Calling from a different thread should fail + Thread t = new Thread(() -> { + try { + log.traceBegin("test"); + errors.add("traceBegin should fail on a different thread"); + } catch (IllegalStateException expected) { + } + try { + log.traceEnd(); + errors.add("traceEnd should fail on a different thread"); + } catch (IllegalStateException expected) { + } + // Verify that creating a new log will work + TimingsTraceAndSlog log2 = new TimingsTraceAndSlog(TAG, TRACE_TAG_APP); + log2.traceBegin("test"); + log2.traceEnd(); + + }); + t.start(); + t.join(); + assertThat(errors).isEmpty(); + } + + @Test + public void testGetUnfinishedTracesForDebug() { + TimingsTraceAndSlog log = new TimingsTraceAndSlog(TAG, TRACE_TAG_APP); + assertThat(log.getUnfinishedTracesForDebug()).isEmpty(); + + log.traceBegin("One"); + assertThat(log.getUnfinishedTracesForDebug()).containsExactly("One").inOrder(); + + log.traceBegin("Two"); + assertThat(log.getUnfinishedTracesForDebug()).containsExactly("One", "Two").inOrder(); + + log.traceEnd(); + assertThat(log.getUnfinishedTracesForDebug()).containsExactly("One").inOrder(); + + log.traceEnd(); + assertThat(log.getUnfinishedTracesForDebug()).isEmpty(); + } + + @Test + public void testLogDuration() throws Exception { + TimingsTraceAndSlog log = new TimingsTraceAndSlog(TAG, TRACE_TAG_APP); + log.logDuration("logro", 42); + verify((MockedVoidMethod) () -> Slog.v(eq(TAG), contains("logro took to complete: 42ms"))); + } + + @Test + public void testOneLevel() throws Exception { + TimingsTraceAndSlog log = new TimingsTraceAndSlog(TAG, TRACE_TAG_APP); + log.traceBegin("test"); + log.traceEnd(); + + verify((MockedVoidMethod) () -> Trace.traceBegin(TRACE_TAG_APP, "test")); + verify((MockedVoidMethod) () -> Trace.traceEnd(TRACE_TAG_APP)); + verify((MockedVoidMethod) () -> Slog.v(eq(TAG), matches("test took to complete: \\dms"))); + } + + @Test + public void testMultipleLevels() throws Exception { + TimingsTraceAndSlog log = new TimingsTraceAndSlog(TAG, TRACE_TAG_APP); + log.traceBegin("L1"); + log.traceBegin("L2"); + log.traceEnd(); + log.traceEnd(); + + verify((MockedVoidMethod) () -> Trace.traceBegin(TRACE_TAG_APP, "L1")); + verify((MockedVoidMethod) () -> Trace.traceBegin(TRACE_TAG_APP, "L2")); + verify((MockedVoidMethod) () -> Trace.traceEnd(TRACE_TAG_APP), times(2)); // L1 and L2 + + verify((MockedVoidMethod) () -> Slog.v(eq(TAG), matches("L2 took to complete: \\d+ms"))); + verify((MockedVoidMethod) () -> Slog.v(eq(TAG), matches("L1 took to complete: \\d+ms"))); + } + + @Test + public void testEndNoBegin() throws Exception { + TimingsTraceAndSlog log = new TimingsTraceAndSlog(TAG, TRACE_TAG_APP); + log.traceEnd(); + verify((MockedVoidMethod) () -> Trace.traceEnd(TRACE_TAG_APP)); + verify((MockedVoidMethod) () -> Slog.d(eq(TAG), anyString()), never()); + verify((MockedVoidMethod) () -> Slog.w(TAG, "traceEnd called more times than traceBegin")); + } +} diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java index f3a0b7fa1ea7..0780d219dc80 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java @@ -28,6 +28,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; @@ -45,13 +46,16 @@ import android.content.IntentFilter; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.Region; +import android.hardware.display.DisplayManagerInternal; import android.os.Looper; +import android.view.DisplayInfo; import android.view.MagnificationSpec; import android.view.accessibility.MagnificationAnimationCallback; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; +import com.android.server.LocalServices; import com.android.server.accessibility.AccessibilityTraceManager; import com.android.server.accessibility.test.MessageCapturingHandler; import com.android.server.wm.WindowManagerInternal; @@ -113,6 +117,8 @@ public class FullScreenMagnificationControllerTest { FullScreenMagnificationController mFullScreenMagnificationController; + public DisplayManagerInternal mDisplayManagerInternalMock = mock(DisplayManagerInternal.class); + @Before public void setUp() { Looper looper = InstrumentationRegistry.getContext().getMainLooper(); @@ -125,6 +131,12 @@ public class FullScreenMagnificationControllerTest { when(mMockControllerCtx.getAnimationDuration()).thenReturn(1000L); initMockWindowManager(); + final DisplayInfo displayInfo = new DisplayInfo(); + displayInfo.logicalDensityDpi = 300; + doReturn(displayInfo).when(mDisplayManagerInternalMock).getDisplayInfo(anyInt()); + LocalServices.removeServiceForTest(DisplayManagerInternal.class); + LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock); + mFullScreenMagnificationController = new FullScreenMagnificationController( mMockControllerCtx, new Object(), mRequestObserver, mScaleProvider); } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java index eab96c09a00a..c17347320f52 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java @@ -72,9 +72,11 @@ public class BiometricSchedulerOperationTest { @Mock private ClientMonitorCallback mClientCallback; @Mock + private ClientMonitorCallback mOnStartCallback; + @Mock private FakeHal mHal; @Captor - ArgumentCaptor<ClientMonitorCallback> mStartCallback; + ArgumentCaptor<ClientMonitorCallback> mStartedCallbackCaptor; private Handler mHandler; private BiometricSchedulerOperation mOperation; @@ -91,17 +93,17 @@ public class BiometricSchedulerOperationTest { when(mClientMonitor.getCookie()).thenReturn(cookie); when(mClientMonitor.getFreshDaemon()).thenReturn(mHal); - assertThat(mOperation.isReadyToStart()).isEqualTo(cookie); + assertThat(mOperation.isReadyToStart(mOnStartCallback)).isEqualTo(cookie); assertThat(mOperation.isStarted()).isFalse(); assertThat(mOperation.isCanceling()).isFalse(); assertThat(mOperation.isFinished()).isFalse(); + verify(mClientMonitor).waitForCookie(any()); - final boolean started = mOperation.startWithCookie( - mock(ClientMonitorCallback.class), cookie); + final boolean started = mOperation.startWithCookie(mOnStartCallback, cookie); assertThat(started).isTrue(); - verify(mClientMonitor).start(mStartCallback.capture()); - mStartCallback.getValue().onClientStarted(mClientMonitor); + verify(mClientMonitor).start(mStartedCallbackCaptor.capture()); + mStartedCallbackCaptor.getValue().onClientStarted(mClientMonitor); assertThat(mOperation.isStarted()).isTrue(); } @@ -112,14 +114,15 @@ public class BiometricSchedulerOperationTest { when(mClientMonitor.getCookie()).thenReturn(goodCookie); when(mClientMonitor.getFreshDaemon()).thenReturn(mHal); - assertThat(mOperation.isReadyToStart()).isEqualTo(goodCookie); - final boolean started = mOperation.startWithCookie( - mock(ClientMonitorCallback.class), badCookie); + assertThat(mOperation.isReadyToStart(mOnStartCallback)).isEqualTo(goodCookie); + final boolean started = mOperation.startWithCookie(mOnStartCallback, badCookie); assertThat(started).isFalse(); assertThat(mOperation.isStarted()).isFalse(); assertThat(mOperation.isCanceling()).isFalse(); assertThat(mOperation.isFinished()).isFalse(); + verify(mClientMonitor).waitForCookie(any()); + verify(mClientMonitor, never()).start(any()); } @Test @@ -127,26 +130,25 @@ public class BiometricSchedulerOperationTest { when(mClientMonitor.getCookie()).thenReturn(0); when(mClientMonitor.getFreshDaemon()).thenReturn(mHal); - final ClientMonitorCallback cb = mock(ClientMonitorCallback.class); - mOperation.start(cb); - verify(mClientMonitor).start(mStartCallback.capture()); - mStartCallback.getValue().onClientStarted(mClientMonitor); + mOperation.start(mOnStartCallback); + verify(mClientMonitor).start(mStartedCallbackCaptor.capture()); + mStartedCallbackCaptor.getValue().onClientStarted(mClientMonitor); assertThat(mOperation.isStarted()).isTrue(); assertThat(mOperation.isCanceling()).isFalse(); assertThat(mOperation.isFinished()).isFalse(); verify(mClientCallback).onClientStarted(eq(mClientMonitor)); - verify(cb).onClientStarted(eq(mClientMonitor)); + verify(mOnStartCallback).onClientStarted(eq(mClientMonitor)); verify(mClientCallback, never()).onClientFinished(any(), anyBoolean()); - verify(cb, never()).onClientFinished(any(), anyBoolean()); + verify(mOnStartCallback, never()).onClientFinished(any(), anyBoolean()); - mStartCallback.getValue().onClientFinished(mClientMonitor, true); + mStartedCallbackCaptor.getValue().onClientFinished(mClientMonitor, true); assertThat(mOperation.isFinished()).isTrue(); assertThat(mOperation.isCanceling()).isFalse(); verify(mClientMonitor).destroy(); - verify(cb).onClientFinished(eq(mClientMonitor), eq(true)); + verify(mOnStartCallback).onClientFinished(eq(mClientMonitor), eq(true)); } @Test @@ -154,8 +156,7 @@ public class BiometricSchedulerOperationTest { when(mClientMonitor.getCookie()).thenReturn(0); when(mClientMonitor.getFreshDaemon()).thenReturn(null); - final ClientMonitorCallback cb = mock(ClientMonitorCallback.class); - mOperation.start(cb); + mOperation.start(mOnStartCallback); verify(mClientMonitor, never()).start(any()); assertThat(mOperation.isStarted()).isFalse(); @@ -163,9 +164,9 @@ public class BiometricSchedulerOperationTest { assertThat(mOperation.isFinished()).isTrue(); verify(mClientCallback, never()).onClientStarted(eq(mClientMonitor)); - verify(cb, never()).onClientStarted(eq(mClientMonitor)); + verify(mOnStartCallback, never()).onClientStarted(eq(mClientMonitor)); verify(mClientCallback).onClientFinished(eq(mClientMonitor), eq(false)); - verify(cb).onClientFinished(eq(mClientMonitor), eq(false)); + verify(mOnStartCallback).onClientFinished(eq(mClientMonitor), eq(false)); } @Test @@ -179,7 +180,7 @@ public class BiometricSchedulerOperationTest { public void cannotRestart() { when(mClientMonitor.getFreshDaemon()).thenReturn(mHal); - mOperation.start(mock(ClientMonitorCallback.class)); + mOperation.start(mOnStartCallback); assertThrows(IllegalStateException.class, () -> mOperation.start(mock(ClientMonitorCallback.class))); @@ -202,7 +203,7 @@ public class BiometricSchedulerOperationTest { public void cannotAbortRunning() { when(mClientMonitor.getFreshDaemon()).thenReturn(mHal); - mOperation.start(mock(ClientMonitorCallback.class)); + mOperation.start(mOnStartCallback); assertThrows(IllegalStateException.class, () -> mOperation.abort()); } @@ -211,11 +212,10 @@ public class BiometricSchedulerOperationTest { public void cancel() { when(mClientMonitor.getFreshDaemon()).thenReturn(mHal); - final ClientMonitorCallback startCb = mock(ClientMonitorCallback.class); final ClientMonitorCallback cancelCb = mock(ClientMonitorCallback.class); - mOperation.start(startCb); - verify(mClientMonitor).start(mStartCallback.capture()); - mStartCallback.getValue().onClientStarted(mClientMonitor); + mOperation.start(mOnStartCallback); + verify(mClientMonitor).start(mStartedCallbackCaptor.capture()); + mStartedCallbackCaptor.getValue().onClientStarted(mClientMonitor); mOperation.cancel(mHandler, cancelCb); assertThat(mOperation.isCanceling()).isTrue(); @@ -223,7 +223,7 @@ public class BiometricSchedulerOperationTest { verify(mClientMonitor, never()).cancelWithoutStarting(any()); verify(mClientMonitor, never()).destroy(); - mStartCallback.getValue().onClientFinished(mClientMonitor, true); + mStartedCallbackCaptor.getValue().onClientFinished(mClientMonitor, true); assertThat(mOperation.isFinished()).isTrue(); assertThat(mOperation.isCanceling()).isFalse(); @@ -315,12 +315,10 @@ public class BiometricSchedulerOperationTest { private void cancelWatchdog(boolean start) { when(mClientMonitor.getFreshDaemon()).thenReturn(mHal); - final ClientMonitorCallback opStartCallback = mock(ClientMonitorCallback.class); - mOperation.start(opStartCallback); + mOperation.start(mOnStartCallback); if (start) { - verify(mClientMonitor).start(mStartCallback.capture()); - mStartCallback.getValue().onClientStarted(mClientMonitor); - verify(opStartCallback).onClientStarted(eq(mClientMonitor)); + verify(mClientMonitor).start(mStartedCallbackCaptor.capture()); + mStartedCallbackCaptor.getValue().onClientStarted(mClientMonitor); } mOperation.cancel(mHandler, mock(ClientMonitorCallback.class)); @@ -331,7 +329,7 @@ public class BiometricSchedulerOperationTest { assertThat(mOperation.isFinished()).isTrue(); assertThat(mOperation.isCanceling()).isFalse(); - verify(opStartCallback).onClientFinished(eq(mClientMonitor), eq(false)); + verify(mOnStartCallback).onClientFinished(eq(mClientMonitor), eq(false)); verify(mClientMonitor).destroy(); } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java index 0fa2b41e8b32..45e3b4373266 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java @@ -196,7 +196,8 @@ public class BiometricSchedulerTest { // Schedule a BiometricPrompt authentication request mScheduler.scheduleClientMonitor(client1, callback1); - assertNotEquals(0, mScheduler.mCurrentOperation.isReadyToStart()); + assertNotEquals(0, mScheduler.mCurrentOperation.isReadyToStart( + mock(ClientMonitorCallback.class))); assertEquals(client1, mScheduler.mCurrentOperation.getClientMonitor()); assertEquals(0, mScheduler.mPendingOperations.size()); @@ -436,7 +437,8 @@ public class BiometricSchedulerTest { if (started || isEnroll) { // prep'd auth clients and enroll clients assertTrue(mScheduler.mCurrentOperation.isStarted()); } else { - assertNotEquals(0, mScheduler.mCurrentOperation.isReadyToStart()); + assertNotEquals(0, mScheduler.mCurrentOperation.isReadyToStart( + mock(ClientMonitorCallback.class))); } } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java index dc39b6d573db..5012335b533f 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java @@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.hardware.biometrics.BiometricOverlayConstants; import android.hardware.fingerprint.ISidefpsController; @@ -29,6 +30,7 @@ import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.mockito.Mock; @@ -43,6 +45,7 @@ import java.util.List; public class SensorOverlaysTest { private static final int SENSOR_ID = 11; + private static final long REQUEST_ID = 8; @Rule public final MockitoRule mockito = MockitoJUnit.rule(); @@ -50,6 +53,12 @@ public class SensorOverlaysTest { @Mock private ISidefpsController mSidefpsController; @Mock private AcquisitionClient<?> mAcquisitionClient; + @Before + public void setup() { + when(mAcquisitionClient.getRequestId()).thenReturn(REQUEST_ID); + when(mAcquisitionClient.hasRequestId()).thenReturn(true); + } + @Test public void noopWhenBothNull() { final SensorOverlays useless = new SensorOverlays(null, null); @@ -92,7 +101,8 @@ public class SensorOverlaysTest { sensorOverlays.show(SENSOR_ID, reason, mAcquisitionClient); if (udfps != null) { - verify(mUdfpsOverlayController).showUdfpsOverlay(eq(SENSOR_ID), eq(reason), any()); + verify(mUdfpsOverlayController).showUdfpsOverlay( + eq(REQUEST_ID), eq(SENSOR_ID), eq(reason), any()); } if (sidefps != null) { verify(mSidefpsController).show(eq(SENSOR_ID), eq(reason)); diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java index 52eee9a55cc7..8391914a0bb6 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java @@ -18,9 +18,8 @@ package com.android.server.biometrics.sensors; import static android.testing.TestableLooper.RunWithLooper; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -45,11 +44,15 @@ import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import java.util.ArrayList; +import java.util.List; import java.util.function.Supplier; @Presubmit @@ -61,9 +64,12 @@ public class UserAwareBiometricSchedulerTest { private static final String TAG = "UserAwareBiometricSchedulerTest"; private static final int TEST_SENSOR_ID = 0; + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + private Handler mHandler; private UserAwareBiometricScheduler mScheduler; - private IBinder mToken = new Binder(); + private final IBinder mToken = new Binder(); @Mock private Context mContext; @@ -74,15 +80,14 @@ public class UserAwareBiometricSchedulerTest { @Mock private BiometricContext mBiometricContext; - private TestUserStartedCallback mUserStartedCallback = new TestUserStartedCallback(); - private TestUserStoppedCallback mUserStoppedCallback = new TestUserStoppedCallback(); + private final TestUserStartedCallback mUserStartedCallback = new TestUserStartedCallback(); + private final TestUserStoppedCallback mUserStoppedCallback = new TestUserStoppedCallback(); private int mCurrentUserId = UserHandle.USER_NULL; private boolean mStartOperationsFinish = true; private int mStartUserClientCount = 0; @Before public void setUp() { - MockitoAnnotations.initMocks(this); mHandler = new Handler(TestableLooper.get(this).getLooper()); mScheduler = new UserAwareBiometricScheduler(TAG, mHandler, @@ -121,8 +126,8 @@ public class UserAwareBiometricSchedulerTest { mScheduler.scheduleClientMonitor(nextClient); waitForIdle(); - assertEquals(0, mUserStoppedCallback.numInvocations); - assertEquals(1, mUserStartedCallback.numInvocations); + assertThat(mUserStoppedCallback.mNumInvocations).isEqualTo(0); + assertThat(mUserStartedCallback.mStartedUsers).containsExactly(0); verify(nextClient).start(any()); } @@ -142,9 +147,9 @@ public class UserAwareBiometricSchedulerTest { waitForIdle(); } - assertEquals(0, mUserStoppedCallback.numInvocations); - assertEquals(0, mUserStartedCallback.numInvocations); - assertEquals(1, mStartUserClientCount); + assertThat(mUserStoppedCallback.mNumInvocations).isEqualTo(0); + assertThat(mUserStartedCallback.mStartedUsers).isEmpty(); + assertThat(mStartUserClientCount).isEqualTo(1); for (BaseClientMonitor client : nextClients) { verify(client, never()).start(any()); } @@ -163,13 +168,13 @@ public class UserAwareBiometricSchedulerTest { final TestStartUserClient startUserClient = (TestStartUserClient) mScheduler.mCurrentOperation.getClientMonitor(); mScheduler.reset(); - assertNull(mScheduler.mCurrentOperation); + assertThat(mScheduler.mCurrentOperation).isNull(); final BiometricSchedulerOperation fakeOperation = new BiometricSchedulerOperation( mock(BaseClientMonitor.class), new ClientMonitorCallback() {}); mScheduler.mCurrentOperation = fakeOperation; startUserClient.mCallback.onClientFinished(startUserClient, true); - assertSame(fakeOperation, mScheduler.mCurrentOperation); + assertThat(fakeOperation).isSameInstanceAs(mScheduler.mCurrentOperation); } @Test @@ -184,8 +189,8 @@ public class UserAwareBiometricSchedulerTest { waitForIdle(); verify(nextClient).start(any()); - assertEquals(0, mUserStoppedCallback.numInvocations); - assertEquals(0, mUserStartedCallback.numInvocations); + assertThat(mUserStoppedCallback.mNumInvocations).isEqualTo(0); + assertThat(mUserStartedCallback.mStartedUsers).isEmpty(); } @Test @@ -199,36 +204,67 @@ public class UserAwareBiometricSchedulerTest { mScheduler.scheduleClientMonitor(nextClient); waitForIdle(); - assertEquals(1, mUserStoppedCallback.numInvocations); + assertThat(mUserStoppedCallback.mNumInvocations).isEqualTo(1); waitForIdle(); - assertEquals(1, mUserStartedCallback.numInvocations); + assertThat(mUserStartedCallback.mStartedUsers).containsExactly(nextUserId); waitForIdle(); verify(nextClient).start(any()); } + @Test + public void testStartUser_alwaysStartsNextOperation() { + BaseClientMonitor nextClient = mock(BaseClientMonitor.class); + when(nextClient.getTargetUserId()).thenReturn(10); + + mScheduler.scheduleClientMonitor(nextClient); + + waitForIdle(); + verify(nextClient).start(any()); + + // finish first operation + mScheduler.getInternalCallback().onClientFinished(nextClient, true /* success */); + waitForIdle(); + + // schedule second operation but swap out the current operation + // before it runs so that it's not current when it's completion callback runs + nextClient = mock(BaseClientMonitor.class); + when(nextClient.getTargetUserId()).thenReturn(11); + mUserStartedCallback.mAfterStart = () -> mScheduler.mCurrentOperation = null; + mScheduler.scheduleClientMonitor(nextClient); + + waitForIdle(); + verify(nextClient).start(any()); + assertThat(mUserStartedCallback.mStartedUsers).containsExactly(10, 11).inOrder(); + assertThat(mUserStoppedCallback.mNumInvocations).isEqualTo(1); + } + private void waitForIdle() { TestableLooper.get(this).processAllMessages(); } private class TestUserStoppedCallback implements StopUserClient.UserStoppedCallback { - int numInvocations; + int mNumInvocations; @Override public void onUserStopped() { - numInvocations++; + mNumInvocations++; mCurrentUserId = UserHandle.USER_NULL; } } private class TestUserStartedCallback implements StartUserClient.UserStartedCallback<Object> { - int numInvocations; + final List<Integer> mStartedUsers = new ArrayList<>(); + Runnable mAfterStart = null; @Override public void onUserStarted(int newUserId, Object newObject, int halInterfaceVersion) { - numInvocations++; + mStartedUsers.add(newUserId); mCurrentUserId = newUserId; + if (mAfterStart != null) { + mAfterStart.run(); + } } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java index de0f038e8ec5..6c50ca35be79 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java @@ -71,6 +71,7 @@ public class FingerprintAuthenticationClientTest { private static final int USER_ID = 8; private static final long OP_ID = 7; + private static final long REQUEST_ID = 88; private static final int POINTER_ID = 0; private static final int TOUCH_X = 8; private static final int TOUCH_Y = 20; @@ -259,7 +260,7 @@ public class FingerprintAuthenticationClientTest { client.start(mCallback); - verify(mUdfpsOverlayController).showUdfpsOverlay(anyInt(), anyInt(), any()); + verify(mUdfpsOverlayController).showUdfpsOverlay(eq(REQUEST_ID), anyInt(), anyInt(), any()); verify(mSideFpsController).show(anyInt(), anyInt()); block.accept(client); @@ -277,7 +278,7 @@ public class FingerprintAuthenticationClientTest { final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback); return new FingerprintAuthenticationClient(mContext, () -> aidl, mToken, - 2 /* requestId */, mClientMonitorCallbackConverter, 5 /* targetUserId */, OP_ID, + REQUEST_ID, mClientMonitorCallbackConverter, 5 /* targetUserId */, OP_ID, false /* restricted */, "test-owner", 4 /* cookie */, false /* requireConfirmation */, 9 /* sensorId */, mBiometricLogger, mBiometricContext, true /* isStrongBiometric */, diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java index 5a96f5cca52a..f77eb0bcc59f 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java @@ -72,6 +72,7 @@ public class FingerprintEnrollClientTest { private static final byte[] HAT = new byte[69]; private static final int USER_ID = 8; + private static final long REQUEST_ID = 9; private static final int POINTER_ID = 0; private static final int TOUCH_X = 8; private static final int TOUCH_Y = 20; @@ -256,7 +257,7 @@ public class FingerprintEnrollClientTest { client.start(mCallback); - verify(mUdfpsOverlayController).showUdfpsOverlay(anyInt(), anyInt(), any()); + verify(mUdfpsOverlayController).showUdfpsOverlay(eq(REQUEST_ID), anyInt(), anyInt(), any()); verify(mSideFpsController).show(anyInt(), anyInt()); block.accept(client); @@ -273,7 +274,7 @@ public class FingerprintEnrollClientTest { when(mHal.getInterfaceVersion()).thenReturn(version); final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback); - return new FingerprintEnrollClient(mContext, () -> aidl, mToken, 6 /* requestId */, + return new FingerprintEnrollClient(mContext, () -> aidl, mToken, REQUEST_ID, mClientMonitorCallbackConverter, 0 /* userId */, HAT, "owner", mBiometricUtils, 8 /* sensorId */, mBiometricLogger, mBiometricContext, mSensorProps, mUdfpsOverlayController, diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 1ebcbe10fae6..a2409b8bef0f 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -4260,14 +4260,11 @@ public class DevicePolicyManagerTest extends DpmTestBase { dpm.setPreferentialNetworkServiceConfigs(List.of(preferentialNetworkServiceConfigEnabled)); assertThat(dpm.getPreferentialNetworkServiceConfigs().get(0) .isEnabled()).isTrue(); - List<Integer> includedList = new ArrayList<>(); - includedList.add(1); - includedList.add(2); ProfileNetworkPreference preferenceDetails = new ProfileNetworkPreference.Builder() .setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK) .setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1) - .setIncludedUids(includedList) + .setIncludedUids(new int[]{1, 2}) .build(); List<ProfileNetworkPreference> preferences = new ArrayList<>(); preferences.add(preferenceDetails); @@ -4295,14 +4292,11 @@ public class DevicePolicyManagerTest extends DpmTestBase { dpm.setPreferentialNetworkServiceConfigs(List.of(preferentialNetworkServiceConfigEnabled)); assertThat(dpm.getPreferentialNetworkServiceConfigs().get(0) .isEnabled()).isTrue(); - List<Integer> excludedUids = new ArrayList<>(); - excludedUids.add(1); - excludedUids.add(2); ProfileNetworkPreference preferenceDetails = new ProfileNetworkPreference.Builder() .setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK) .setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1) - .setExcludedUids(excludedUids) + .setExcludedUids(new int[]{1, 2}) .build(); List<ProfileNetworkPreference> preferences = new ArrayList<>(); preferences.clear(); @@ -6671,7 +6665,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { configureContextForAccess(mContext, false); assertExpectException(SecurityException.class, /* messageRegex= */ null, - () -> dpm.markProfileOwnerOnOrganizationOwnedDevice(admin2)); + () -> dpm.setProfileOwnerOnOrganizationOwnedDevice(admin2, true)); } @Test @@ -6680,7 +6674,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { configureContextForAccess(mContext, false); assertExpectException(SecurityException.class, /* messageRegex= */ null, - () -> dpm.markProfileOwnerOnOrganizationOwnedDevice(admin1)); + () -> dpm.setProfileOwnerOnOrganizationOwnedDevice(admin1, true)); } @Test @@ -6715,7 +6709,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { DpmMockContext.CALLER_MANAGED_PROVISIONING_UID); try { runAsCaller(mServiceContext, dpms, dpm -> { - dpm.markProfileOwnerOnOrganizationOwnedDevice(admin1); + dpm.setProfileOwnerOnOrganizationOwnedDevice(admin1, true); }); } finally { mServiceContext.binder.restoreCallingIdentity(ident); @@ -7050,7 +7044,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { configureContextForAccess(mServiceContext, true); runAsCaller(mServiceContext, dpms, dpm -> { - dpm.markProfileOwnerOnOrganizationOwnedDevice(who); + dpm.setProfileOwnerOnOrganizationOwnedDevice(who, true); }); mServiceContext.binder.restoreCallingIdentity(ident); } diff --git a/services/tests/servicestests/src/com/android/server/display/ColorFadeTest.java b/services/tests/servicestests/src/com/android/server/display/ColorFadeTest.java new file mode 100644 index 000000000000..26a83a23de33 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/display/ColorFadeTest.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display; + +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static org.junit.Assert.assertFalse; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.hardware.display.DisplayManagerInternal; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.LocalServices; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@Presubmit +@RunWith(AndroidJUnit4.class) +public class ColorFadeTest { + private static final int DISPLAY_ID = 123; + + private Context mContext; + + @Mock private DisplayManagerInternal mDisplayManagerInternalMock; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + addLocalServiceMock(DisplayManagerInternal.class, mDisplayManagerInternalMock); + mContext = getInstrumentation().getTargetContext(); + } + + @After + public void tearDown() { + LocalServices.removeServiceForTest(DisplayManagerInternal.class); + } + + @Test + public void testPrepareColorFadeForInvalidDisplay() { + when(mDisplayManagerInternalMock.getDisplayInfo(eq(DISPLAY_ID))).thenReturn(null); + ColorFade colorFade = new ColorFade(DISPLAY_ID); + assertFalse(colorFade.prepare(mContext, ColorFade.MODE_FADE)); + } + + private static <T> void addLocalServiceMock(Class<T> clazz, T mock) { + LocalServices.removeServiceForTest(clazz); + LocalServices.addService(clazz, mock); + } + +} diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java index 99edecfeed30..c78678431dac 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java @@ -16,7 +16,6 @@ package com.android.server.pm; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertBundlesEqual; -import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertEmpty; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertExpectException; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith; import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list; @@ -37,6 +36,8 @@ import android.app.Person; import android.content.ComponentName; import android.content.Intent; import android.content.LocusId; +import android.content.pm.Capability; +import android.content.pm.CapabilityParams; import android.content.pm.ShortcutInfo; import android.content.res.Resources; import android.graphics.BitmapFactory; @@ -258,10 +259,15 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { .setLongLived(true) .setExtras(pb) .setStartingTheme(android.R.style.Theme_Black_NoTitleBar_Fullscreen) - .addCapabilityBinding("action.intent.START_EXERCISE", - "exercise.type", list("running", "jogging")) - .addCapabilityBinding("action.intent.START_EXERCISE", - "exercise.duration", list("10m")) + .addCapabilityBinding( + new Capability.Builder("action.intent.START_EXERCISE").build(), + new CapabilityParams.Builder("exercise.type", "running") + .addAlias("jogging") + .build()) + .addCapabilityBinding( + new Capability.Builder("action.intent.START_EXERCISE").build(), + new CapabilityParams.Builder("exercise.duration", "10m") + .build()) .build(); si.addFlags(ShortcutInfo.FLAG_PINNED); si.setBitmapPath("abc"); @@ -299,13 +305,14 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertEquals(null, si.getDisabledMessageResName()); assertEquals("android:style/Theme.Black.NoTitleBar.Fullscreen", si.getStartingThemeResName()); - assertTrue(si.hasCapability("action.intent.START_EXERCISE")); - assertFalse(si.hasCapability("")); - assertFalse(si.hasCapability("random")); - assertEquals(list("running", "jogging"), si.getCapabilityParameterValues( - "action.intent.START_EXERCISE", "exercise.type")); - assertEmpty(si.getCapabilityParameterValues("action.intent.START_EXERCISE", "")); - assertEmpty(si.getCapabilityParameterValues("action.intent.START_EXERCISE", "random")); + assertEquals(list(new Capability.Builder("action.intent.START_EXERCISE").build()), + si.getCapabilities()); + assertEquals(list( + new CapabilityParams.Builder("exercise.type", "running") + .addAlias("jogging").build(), + new CapabilityParams.Builder("exercise.duration", "10m").build()), + si.getCapabilityParams( + new Capability.Builder("action.intent.START_EXERCISE").build())); } public void testShortcutInfoParcel_resId() { @@ -959,10 +966,15 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { .setRank(123) .setExtras(pb) .setLocusId(new LocusId("1.2.3.4.5")) - .addCapabilityBinding("action.intent.START_EXERCISE", - "exercise.type", list("running", "jogging")) - .addCapabilityBinding("action.intent.START_EXERCISE", - "exercise.duration", list("10m")) + .addCapabilityBinding( + new Capability.Builder("action.intent.START_EXERCISE").build(), + new CapabilityParams.Builder("exercise.type", "running") + .addAlias("jogging") + .build()) + .addCapabilityBinding( + new Capability.Builder("action.intent.START_EXERCISE").build(), + new CapabilityParams.Builder("exercise.duration", "10m") + .build()) .build(); sorig.setTimestamp(mInjectedCurrentTimeMillis); @@ -1024,13 +1036,14 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest { assertNull(si.getIconUri()); assertTrue(si.getLastChangedTimestamp() < now); - assertTrue(si.hasCapability("action.intent.START_EXERCISE")); - assertFalse(si.hasCapability("")); - assertFalse(si.hasCapability("random")); - assertEquals(list("running", "jogging"), si.getCapabilityParameterValues( - "action.intent.START_EXERCISE", "exercise.type")); - assertEmpty(si.getCapabilityParameterValues("action.intent.START_EXERCISE", "")); - assertEmpty(si.getCapabilityParameterValues("action.intent.START_EXERCISE", "random")); + assertEquals(list(new Capability.Builder("action.intent.START_EXERCISE").build()), + si.getCapabilities()); + assertEquals(list( + new CapabilityParams.Builder("exercise.type", "running") + .addAlias("jogging").build(), + new CapabilityParams.Builder("exercise.duration", "10m").build()), + si.getCapabilityParams( + new Capability.Builder("action.intent.START_EXERCISE").build())); // Make sure ranks are saved too. Because of the auto-adjusting, we need two shortcuts // to test it. diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java index 0eba6a335d00..0187e34cbc5f 100644 --- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java +++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java @@ -224,6 +224,11 @@ public class SoundTriggerMiddlewareImplTest { // Stop the recognition. stopRecognition(module, handle, hwHandle); + ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass( + RecognitionEvent.class); + verify(callback).onRecognition(eq(handle), eventCaptor.capture(), eq(101)); + assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().status); + // Unload the model. unloadModel(module, handle, hwHandle); module.detach(); @@ -268,6 +273,11 @@ public class SoundTriggerMiddlewareImplTest { // Stop the recognition. stopRecognition(module, handle, hwHandle); + ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass( + PhraseRecognitionEvent.class); + verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture(), eq(101)); + assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().common.status); + // Unload the model. unloadModel(module, handle, hwHandle); module.detach(); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index f9aa4b17bc2c..9902e83c3648 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -500,6 +500,23 @@ public class ActivityStarterTests extends WindowTestsBase { return Pair.create(splitPrimaryActivity, splitSecondActivity); } + @Test + public void testMoveVisibleTaskToFront() { + final ActivityRecord activity = new TaskBuilder(mSupervisor) + .setCreateActivity(true).build().getTopMostActivity(); + final ActivityRecord translucentActivity = new TaskBuilder(mSupervisor) + .setCreateActivity(true).build().getTopMostActivity(); + assertTrue(activity.mVisibleRequested); + + final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK, + false /* mockGetRootTask */); + starter.getIntent().setComponent(activity.mActivityComponent); + final int result = starter.setReason("testMoveVisibleTaskToFront").execute(); + + assertEquals(START_TASK_TO_FRONT, result); + assertEquals(1, activity.compareTo(translucentActivity)); + } + /** * Tests activity is cleaned up properly in a task mode violation. */ diff --git a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java index 50eefa066a45..c5117bb83976 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java @@ -19,12 +19,12 @@ package com.android.server.wm; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR; -import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK; import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.server.wm.ContentRecorder.KEY_RECORD_TASK_FEATURE; import static com.google.common.truth.Truth.assertThat; @@ -40,17 +40,22 @@ import android.hardware.display.VirtualDisplay; import android.os.Binder; import android.os.IBinder; import android.platform.test.annotations.Presubmit; +import android.provider.DeviceConfig; import android.util.DisplayMetrics; import android.view.ContentRecordingSession; import android.view.Surface; import android.view.SurfaceControl; +import androidx.annotation.NonNull; import androidx.test.filters.SmallTest; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.concurrent.CountDownLatch; + /** * Tests for the {@link ContentRecorder} class. * @@ -62,17 +67,18 @@ import org.junit.runner.RunWith; @RunWith(WindowTestRunner.class) public class ContentRecorderTests extends WindowTestsBase { private static final IBinder TEST_TOKEN = new RecordingTestToken(); - private final ContentRecordingSession mDefaultSession = + private static IBinder sTaskWindowContainerToken; + private final ContentRecordingSession mDisplaySession = ContentRecordingSession.createDisplaySession(TEST_TOKEN); + private ContentRecordingSession mTaskSession; private static Point sSurfaceSize; private ContentRecorder mContentRecorder; private SurfaceControl mRecordedSurface; + // Handle feature flag. + private ConfigListener mConfigListener; + private CountDownLatch mLatch; @Before public void setUp() { - // GIVEN MediaProjection has already initialized the WindowToken of the DisplayArea to - // mirror. - setUpDefaultTaskDisplayAreaWindowToken(); - // GIVEN SurfaceControl can successfully mirror the provided surface. sSurfaceSize = new Point( mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().width(), @@ -84,12 +90,32 @@ public class ContentRecorderTests extends WindowTestsBase { sSurfaceSize.x, sSurfaceSize.y, DisplayMetrics.DENSITY_140, new Surface(), VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR); final int displayId = virtualDisplay.getDisplay().getDisplayId(); - mDefaultSession.setDisplayId(displayId); - mWm.mRoot.onDisplayAdded(displayId); - final DisplayContent mVirtualDisplayContent = mWm.mRoot.getDisplayContent(displayId); - mContentRecorder = new ContentRecorder(mVirtualDisplayContent); - spyOn(mVirtualDisplayContent); + final DisplayContent virtualDisplayContent = mWm.mRoot.getDisplayContent(displayId); + mContentRecorder = new ContentRecorder(virtualDisplayContent); + spyOn(virtualDisplayContent); + + // GIVEN MediaProjection has already initialized the WindowToken of the DisplayArea to + // record. + setUpDefaultTaskDisplayAreaWindowToken(); + mDisplaySession.setDisplayId(displayId); + + // GIVEN there is a window token associated with a task to record. + sTaskWindowContainerToken = setUpTaskWindowContainerToken(virtualDisplayContent); + mTaskSession = ContentRecordingSession.createTaskSession(sTaskWindowContainerToken); + mTaskSession.setDisplayId(displayId); + + mConfigListener = new ConfigListener(); + DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_WINDOW_MANAGER, + mContext.getMainExecutor(), mConfigListener); + mLatch = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_RECORD_TASK_FEATURE, + "true", true); + } + + @After + public void teardown() { + DeviceConfig.removeOnPropertiesChangedListener(mConfigListener); } @Test @@ -102,37 +128,79 @@ public class ContentRecorderTests extends WindowTestsBase { @Test public void testUpdateRecording_display() { - mContentRecorder.setContentRecordingSession(mDefaultSession); + mContentRecorder.setContentRecordingSession(mDisplaySession); mContentRecorder.updateRecording(); assertThat(mContentRecorder.isCurrentlyRecording()).isTrue(); } @Test - public void testUpdateRecording_task() { - mDefaultSession.setContentToRecord(RECORD_CONTENT_TASK); - mContentRecorder.setContentRecordingSession(mDefaultSession); + public void testUpdateRecording_display_nullToken() { + ContentRecordingSession session = ContentRecordingSession.createDisplaySession(TEST_TOKEN); + session.setDisplayId(mDisplaySession.getDisplayId()); + session.setTokenToRecord(null); + mContentRecorder.setContentRecordingSession(session); mContentRecorder.updateRecording(); assertThat(mContentRecorder.isCurrentlyRecording()).isFalse(); } @Test - public void testUpdateRecording_wasPaused() { - mContentRecorder.setContentRecordingSession(mDefaultSession); + public void testUpdateRecording_display_noWindowContainer() { + doReturn(null).when( + mWm.mWindowContextListenerController).getContainer(any()); + mContentRecorder.setContentRecordingSession(mDisplaySession); mContentRecorder.updateRecording(); + assertThat(mContentRecorder.isCurrentlyRecording()).isFalse(); + } - mContentRecorder.pauseRecording(); + @Test + public void testUpdateRecording_task_featureDisabled() { + mLatch = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_RECORD_TASK_FEATURE, + "false", false); + mContentRecorder.setContentRecordingSession(mTaskSession); + mContentRecorder.updateRecording(); + assertThat(mContentRecorder.isCurrentlyRecording()).isFalse(); + } + + @Test + public void testUpdateRecording_task_featureEnabled() { + // Feature already enabled; don't need to again. + mContentRecorder.setContentRecordingSession(mTaskSession); mContentRecorder.updateRecording(); assertThat(mContentRecorder.isCurrentlyRecording()).isTrue(); } @Test - public void testUpdateRecording_wasStopped() { - mContentRecorder.setContentRecordingSession(mDefaultSession); + public void testUpdateRecording_task_nullToken() { + ContentRecordingSession session = ContentRecordingSession.createTaskSession( + sTaskWindowContainerToken); + session.setDisplayId(mDisplaySession.getDisplayId()); + session.setTokenToRecord(null); + mContentRecorder.setContentRecordingSession(session); mContentRecorder.updateRecording(); + assertThat(mContentRecorder.isCurrentlyRecording()).isFalse(); + // TODO(b/219761722) validate VirtualDisplay is torn down when can't set up task recording. + } - mContentRecorder.remove(); + @Test + public void testUpdateRecording_task_noWindowContainer() { + // Use the window container token of the DisplayContent, rather than task. + ContentRecordingSession invalidTaskSession = ContentRecordingSession.createTaskSession( + new WindowContainer.RemoteToken(mDisplayContent)); + mContentRecorder.setContentRecordingSession(invalidTaskSession); mContentRecorder.updateRecording(); assertThat(mContentRecorder.isCurrentlyRecording()).isFalse(); + // TODO(b/219761722) validate VirtualDisplay is torn down when can't set up task recording. + } + + @Test + public void testUpdateRecording_wasPaused() { + mContentRecorder.setContentRecordingSession(mDisplaySession); + mContentRecorder.updateRecording(); + + mContentRecorder.pauseRecording(); + mContentRecorder.updateRecording(); + assertThat(mContentRecorder.isCurrentlyRecording()).isTrue(); } @Test @@ -146,7 +214,7 @@ public class ContentRecorderTests extends WindowTestsBase { @Test public void testOnConfigurationChanged_resizesSurface() { - mContentRecorder.setContentRecordingSession(mDefaultSession); + mContentRecorder.setContentRecordingSession(mDisplaySession); mContentRecorder.updateRecording(); mContentRecorder.onConfigurationChanged(ORIENTATION_PORTRAIT); @@ -158,7 +226,7 @@ public class ContentRecorderTests extends WindowTestsBase { @Test public void testPauseRecording_pausesRecording() { - mContentRecorder.setContentRecordingSession(mDefaultSession); + mContentRecorder.setContentRecordingSession(mDisplaySession); mContentRecorder.updateRecording(); mContentRecorder.pauseRecording(); @@ -173,7 +241,7 @@ public class ContentRecorderTests extends WindowTestsBase { @Test public void testRemove_stopsRecording() { - mContentRecorder.setContentRecordingSession(mDefaultSession); + mContentRecorder.setContentRecordingSession(mDisplaySession); mContentRecorder.updateRecording(); mContentRecorder.remove(); @@ -188,8 +256,9 @@ public class ContentRecorderTests extends WindowTestsBase { @Test public void testUpdateMirroredSurface_capturedAreaResized() { - mContentRecorder.setContentRecordingSession(mDefaultSession); + mContentRecorder.setContentRecordingSession(mDisplaySession); mContentRecorder.updateRecording(); + assertThat(mContentRecorder.isCurrentlyRecording()).isTrue(); // WHEN attempting to mirror on the virtual display, and the captured content is resized. float xScale = 0.7f; @@ -197,13 +266,14 @@ public class ContentRecorderTests extends WindowTestsBase { Rect displayAreaBounds = new Rect(0, 0, Math.round(sSurfaceSize.x * xScale), Math.round(sSurfaceSize.y * yScale)); mContentRecorder.updateMirroredSurface(mTransaction, displayAreaBounds, sSurfaceSize); + assertThat(mContentRecorder.isCurrentlyRecording()).isTrue(); // THEN content in the captured DisplayArea is scaled to fit the surface size. verify(mTransaction, atLeastOnce()).setMatrix(mRecordedSurface, 1.0f / yScale, 0, 0, 1.0f / yScale); // THEN captured content is positioned in the centre of the output surface. - float scaledWidth = displayAreaBounds.width() / xScale; - float xInset = (sSurfaceSize.x - scaledWidth) / 2; + int scaledWidth = Math.round((float) displayAreaBounds.width() / xScale); + int xInset = (sSurfaceSize.x - scaledWidth) / 2; verify(mTransaction, atLeastOnce()).setPosition(mRecordedSurface, xInset, 0); } @@ -222,6 +292,18 @@ public class ContentRecorderTests extends WindowTestsBase { } /** + * Creates a {@link android.window.WindowContainerToken} associated with a task, in order for + * that task to be recorded. + */ + private IBinder setUpTaskWindowContainerToken(DisplayContent displayContent) { + final Task rootTask = createTask(displayContent); + final Task task = createTaskInRootTask(rootTask, 0 /* userId */); + // Ensure the task is not empty. + createActivityRecord(displayContent, task); + return task.getTaskInfo().token.asBinder(); + } + + /** * SurfaceControl successfully creates a mirrored surface of the given size. */ private SurfaceControl surfaceControlMirrors(Point surfaceSize) { @@ -236,4 +318,13 @@ public class ContentRecorderTests extends WindowTestsBase { anyInt()); return mirroredSurface; } + + private class ConfigListener implements DeviceConfig.OnPropertiesChangedListener { + @Override + public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) { + if (mLatch != null && properties.getKeyset().contains(KEY_RECORD_TASK_FEATURE)) { + mLatch.countDown(); + } + } + } } diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index b9abbf09e74d..93c03865dc3f 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -2015,7 +2015,7 @@ public class UsageStatsService extends SystemService implements == PackageManager.PERMISSION_GRANTED; } - private boolean hasPermissions(String callingPackage, String... permissions) { + private boolean hasPermissions(String... permissions) { final int callingUid = Binder.getCallingUid(); if (callingUid == Process.SYSTEM_UID) { // Caller is the system, so proceed. @@ -2578,7 +2578,7 @@ public class UsageStatsService extends SystemService implements String callingPackage) { final int callingUid = Binder.getCallingUid(); final DevicePolicyManagerInternal dpmInternal = getDpmInternal(); - if (!hasPermissions(callingPackage, + if (!hasPermissions( Manifest.permission.SUSPEND_APPS, Manifest.permission.OBSERVE_APP_USAGE) && (dpmInternal == null || !dpmInternal.isActiveSupervisionApp(callingUid))) { throw new SecurityException("Caller must be the active supervision app or " @@ -2605,7 +2605,7 @@ public class UsageStatsService extends SystemService implements public void unregisterAppUsageLimitObserver(int observerId, String callingPackage) { final int callingUid = Binder.getCallingUid(); final DevicePolicyManagerInternal dpmInternal = getDpmInternal(); - if (!hasPermissions(callingPackage, + if (!hasPermissions( Manifest.permission.SUSPEND_APPS, Manifest.permission.OBSERVE_APP_USAGE) && (dpmInternal == null || !dpmInternal.isActiveSupervisionApp(callingUid))) { throw new SecurityException("Caller must be the active supervision app or " @@ -2703,8 +2703,7 @@ public class UsageStatsService extends SystemService implements @Override public long getLastTimeAnyComponentUsed(String packageName, String callingPackage) { - if (!hasPermissions( - callingPackage, android.Manifest.permission.INTERACT_ACROSS_USERS)) { + if (!hasPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS)) { throw new SecurityException("Caller doesn't have INTERACT_ACROSS_USERS permission"); } if (!hasPermission(callingPackage)) { @@ -2787,6 +2786,17 @@ public class UsageStatsService extends SystemService implements "clearBroadcastResponseStats" /* name */, callingPackage); mResponseStatsTracker.clearBroadcastEvents(callingUid, userId); } + + @Override + @Nullable + public String getAppStandbyConstant(@NonNull String key) { + Objects.requireNonNull(key); + + if (!hasPermissions(Manifest.permission.READ_DEVICE_CONFIG)) { + throw new SecurityException("Caller doesn't have READ_DEVICE_CONFIG permission"); + } + return mAppStandby.getAppStandbyConstant(key); + } } void registerAppUsageObserver(int callingUid, int observerId, String[] packages, diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java index 24ce7e76a49c..c8bcb83cae74 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java @@ -84,6 +84,9 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { private static final int INVALID_VALUE = Integer.MIN_VALUE; + /** Maximum time to wait for a model stop confirmation before giving up. */ + private static final long STOP_TIMEOUT_MS = 5000; + /** The {@link ModuleProperties} for the system, or null if none exists. */ final ModuleProperties mModuleProperties; @@ -831,7 +834,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } if (!event.recognitionStillActive) { - model.setStopped(); + model.setStoppedLocked(); } try { @@ -918,7 +921,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { MetricsLogger.count(mContext, "sth_recognition_aborted", 1); ModelData modelData = getModelDataForLocked(event.soundModelHandle); if (modelData != null && modelData.isModelStarted()) { - modelData.setStopped(); + modelData.setStoppedLocked(); try { modelData.getCallback().onRecognitionPaused(); } catch (DeadObjectException e) { @@ -972,7 +975,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } if (!event.recognitionStillActive) { - modelData.setStopped(); + modelData.setStoppedLocked(); } try { @@ -1200,7 +1203,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { if (modelData.isModelStarted()) { Slog.d(TAG, "Stopping previously started dangling model " + modelData.getHandle()); if (mModule.stopRecognition(modelData.getHandle()) == STATUS_OK) { - modelData.setStopped(); + modelData.setStoppedLocked(); modelData.setRequested(false); } else { Slog.e(TAG, "Failed to stop model " + modelData.getHandle()); @@ -1249,7 +1252,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { private ModelData getOrCreateGenericModelDataLocked(UUID modelId) { ModelData modelData = mModelDataMap.get(modelId); if (modelData == null) { - modelData = ModelData.createGenericModelData(modelId); + modelData = createGenericModelData(modelId); mModelDataMap.put(modelId, modelData); } else if (!modelData.isGenericModel()) { Slog.e(TAG, "UUID already used for non-generic model."); @@ -1281,7 +1284,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { mKeyphraseUuidMap.remove(keyphraseId); mModelDataMap.remove(modelId); mKeyphraseUuidMap.put(keyphraseId, modelId); - ModelData modelData = ModelData.createKeyphraseModelData(modelId); + ModelData modelData = createKeyphraseModelData(modelId); mModelDataMap.put(modelId, modelData); return modelData; } @@ -1413,18 +1416,26 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { Slog.w(TAG, "RemoteException in onError", e); } } - } else { - modelData.setStopped(); - MetricsLogger.count(mContext, "sth_stop_recognition_success", 1); - // Notify of pause if needed. - if (notify) { - try { - callback.onRecognitionPaused(); - } catch (DeadObjectException e) { - forceStopAndUnloadModelLocked(modelData, e); - } catch (RemoteException e) { - Slog.w(TAG, "RemoteException in onRecognitionPaused", e); - } + return status; + } + + // Wait for model to be stopped. + try { + modelData.waitStoppedLocked(STOP_TIMEOUT_MS); + } catch (InterruptedException e) { + Slog.e(TAG, "Didn't receive model stop callback"); + return SoundTrigger.STATUS_ERROR; + } + + MetricsLogger.count(mContext, "sth_stop_recognition_success", 1); + // Notify of pause if needed. + if (notify) { + try { + callback.onRecognitionPaused(); + } catch (DeadObjectException e) { + forceStopAndUnloadModelLocked(modelData, e); + } catch (RemoteException e) { + Slog.w(TAG, "RemoteException in onRecognitionPaused", e); } } if (DBG) { @@ -1459,7 +1470,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { // This class encapsulates the callbacks, state, handles and any other information that // represents a model. - private static class ModelData { + private class ModelData { // Model not loaded (and hence not started). static final int MODEL_NOTLOADED = 0; @@ -1516,17 +1527,9 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { mModelType = modelType; } - static ModelData createKeyphraseModelData(UUID modelId) { - return new ModelData(modelId, SoundModel.TYPE_KEYPHRASE); - } - - static ModelData createGenericModelData(UUID modelId) { - return new ModelData(modelId, SoundModel.TYPE_GENERIC_SOUND); - } - // Note that most of the functionality in this Java class will not work for // SoundModel.TYPE_UNKNOWN nevertheless we have it since lower layers support it. - static ModelData createModelDataOfUnknownType(UUID modelId) { + ModelData createModelDataOfUnknownType(UUID modelId) { return new ModelData(modelId, SoundModel.TYPE_UNKNOWN); } @@ -1550,8 +1553,20 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { mModelState = MODEL_STARTED; } - synchronized void setStopped() { + synchronized void setStoppedLocked() { mModelState = MODEL_LOADED; + mLock.notifyAll(); + } + + void waitStoppedLocked(long timeoutMs) throws InterruptedException { + long deadline = System.currentTimeMillis() + timeoutMs; + while (mModelState == MODEL_STARTED) { + long waitTime = deadline - System.currentTimeMillis(); + if (waitTime <= 0) { + throw new InterruptedException(); + } + mLock.wait(waitTime); + } } synchronized void setLoaded() { @@ -1571,6 +1586,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { mRecognitionConfig = null; mRequested = false; mCallback = null; + notifyAll(); } synchronized void clearCallback() { @@ -1675,4 +1691,12 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { return "Model type: " + type + "\n"; } } + + ModelData createKeyphraseModelData(UUID modelId) { + return new ModelData(modelId, SoundModel.TYPE_KEYPHRASE); + } + + ModelData createGenericModelData(UUID modelId) { + return new ModelData(modelId, SoundModel.TYPE_GENERIC_SOUND); + } } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java index 398889213ce5..d527a230a97b 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java @@ -20,6 +20,7 @@ import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD; import static android.Manifest.permission.RECORD_AUDIO; import static android.service.voice.HotwordDetectionService.AUDIO_SOURCE_EXTERNAL; import static android.service.voice.HotwordDetectionService.AUDIO_SOURCE_MICROPHONE; +import static android.service.voice.HotwordDetectionService.INITIALIZATION_STATUS_SUCCESS; import static android.service.voice.HotwordDetectionService.INITIALIZATION_STATUS_UNKNOWN; import static android.service.voice.HotwordDetectionService.KEY_INITIALIZATION_STATUS; @@ -330,15 +331,16 @@ final class HotwordDetectionConnection { return new Pair<>(INITIALIZATION_STATUS_UNKNOWN, METRICS_INIT_UNKNOWN_NO_VALUE); } int status = bundle.getInt(KEY_INITIALIZATION_STATUS, INITIALIZATION_STATUS_UNKNOWN); - if (status > HotwordDetectionService.getMaxCustomInitializationStatus() - && status != INITIALIZATION_STATUS_UNKNOWN) { + if (status > HotwordDetectionService.getMaxCustomInitializationStatus()) { return new Pair<>(INITIALIZATION_STATUS_UNKNOWN, - METRICS_INIT_UNKNOWN_OVER_MAX_CUSTOM_VALUE); + status == INITIALIZATION_STATUS_UNKNOWN + ? METRICS_INIT_UNKNOWN_NO_VALUE + : METRICS_INIT_UNKNOWN_OVER_MAX_CUSTOM_VALUE); } // TODO: should guard against negative here - int metricsResult = status == INITIALIZATION_STATUS_UNKNOWN - ? METRICS_INIT_CALLBACK_STATE_ERROR - : METRICS_INIT_CALLBACK_STATE_SUCCESS; + int metricsResult = status == INITIALIZATION_STATUS_SUCCESS + ? METRICS_INIT_CALLBACK_STATE_SUCCESS + : METRICS_INIT_CALLBACK_STATE_ERROR; return new Pair<>(status, metricsResult); } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 3f430ab77df4..f3139a70eade 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -9815,15 +9815,7 @@ public class TelephonyManager { @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public @Nullable String getCarrierServicePackageName() { - // TODO(b/205736323) plumb this through to CarrierPrivilegesTracker, which will cache the - // value instead of re-querying every time. - List<String> carrierServicePackages = - getCarrierPackageNamesForIntent( - new Intent(CarrierService.CARRIER_SERVICE_INTERFACE)); - if (carrierServicePackages != null && !carrierServicePackages.isEmpty()) { - return carrierServicePackages.get(0); - } - return null; + return getCarrierServicePackageNameForLogicalSlot(getPhoneId()); } /** @@ -9840,13 +9832,15 @@ public class TelephonyManager { @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public @Nullable String getCarrierServicePackageNameForLogicalSlot(int logicalSlotIndex) { - // TODO(b/205736323) plumb this through to CarrierPrivilegesTracker, which will cache the - // value instead of re-querying every time. - List<String> carrierServicePackages = - getCarrierPackageNamesForIntentAndPhone( - new Intent(CarrierService.CARRIER_SERVICE_INTERFACE), logicalSlotIndex); - if (carrierServicePackages != null && !carrierServicePackages.isEmpty()) { - return carrierServicePackages.get(0); + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getCarrierServicePackageNameForLogicalSlot(logicalSlotIndex); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "getCarrierServicePackageNameForLogicalSlot RemoteException", ex); + } catch (NullPointerException ex) { + Rlog.e(TAG, "getCarrierServicePackageNameForLogicalSlot NPE", ex); } return null; } diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java index 532679cb6c43..8143da5ad431 100644 --- a/telephony/java/android/telephony/data/ApnSetting.java +++ b/telephony/java/android/telephony/data/ApnSetting.java @@ -549,7 +549,6 @@ public class ApnSetting implements Parcelable { * Returns the profile id to which the APN saved in modem. * * @return the profile id of the APN - * @hide */ public int getProfileId() { return mProfileId; @@ -558,8 +557,7 @@ public class ApnSetting implements Parcelable { /** * Returns if the APN setting is persistent on the modem. * - * @return is the APN setting to be set in modem - * @hide + * @return {@code true} if the APN setting is persistent on the modem. */ public boolean isPersistent() { return mPersistent; diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index a5e2c1f83939..e3ebb9a23950 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -2541,4 +2541,15 @@ interface ITelephony { * PhoneAccount#CAPABILITY_VOICE_CALLING_AVAILABLE. */ void setVoiceServiceStateOverride(int subId, boolean hasService, String callingPackage); + + /** + * Returns the package name that provides the {@link CarrierService} implementation for the + * specified {@code logicalSlotIndex}, or {@code null} if no package with carrier privileges + * declares one. + * + * @param logicalSlotIndex The slot index to fetch the {@link CarrierService} package for + * @return The system-selected package that provides the {@link CarrierService} implementation + * for the slot, or {@code null} if none is resolved + */ + String getCarrierServicePackageNameForLogicalSlot(int logicalSlotIndex); } diff --git a/tests/BandwidthTests/src/com/android/tests/bandwidthenforcement/BandwidthEnforcementTestService.java b/tests/BandwidthTests/src/com/android/tests/bandwidthenforcement/BandwidthEnforcementTestService.java index 35f1e585931b..644d450a7a88 100644 --- a/tests/BandwidthTests/src/com/android/tests/bandwidthenforcement/BandwidthEnforcementTestService.java +++ b/tests/BandwidthTests/src/com/android/tests/bandwidthenforcement/BandwidthEnforcementTestService.java @@ -24,6 +24,8 @@ import android.net.SntpClient; import android.os.Environment; import android.util.Log; +import libcore.io.Streams; + import java.io.BufferedWriter; import java.io.ByteArrayOutputStream; import java.io.File; @@ -37,8 +39,6 @@ import java.net.InetAddress; import java.net.URL; import java.util.Random; -import libcore.io.Streams; - /* * Test Service that tries to connect to the web via different methods and outputs the results to * the log and a output file. @@ -146,7 +146,7 @@ public class BandwidthEnforcementTestService extends IntentService { final ConnectivityManager mCM = context.getSystemService(ConnectivityManager.class); final Network network = mCM.getActiveNetwork(); - if (client.requestTime("0.pool.ntp.org", 10000, network)) { + if (client.requestTime("0.pool.ntp.org", SntpClient.STANDARD_NTP_PORT, 10000, network)) { return true; } return false; diff --git a/tests/TrustTests/AndroidManifest.xml b/tests/TrustTests/AndroidManifest.xml index 68bc1f69628f..8b4cbfd0e44b 100644 --- a/tests/TrustTests/AndroidManifest.xml +++ b/tests/TrustTests/AndroidManifest.xml @@ -68,6 +68,16 @@ <action android:name="android.service.trust.TrustAgentService" /> </intent-filter> </service> + + <service + android:name=".TemporaryAndRenewableTrustAgent" + android:exported="true" + android:label="Test Agent" + android:permission="android.permission.BIND_TRUST_AGENT"> + <intent-filter> + <action android:name="android.service.trust.TrustAgentService" /> + </intent-filter> + </service> </application> <!-- self-instrumenting test package. --> diff --git a/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt b/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt index 790afd389152..af7a98c22ad1 100644 --- a/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt +++ b/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt @@ -60,7 +60,6 @@ class GrantAndRevokeTrustTest { @Test fun sleepingDeviceWithoutGrantLocksDevice() { uiDevice.sleep() - await() lockStateTrackingRule.assertLocked() } @@ -69,7 +68,6 @@ class GrantAndRevokeTrustTest { fun grantKeepsDeviceUnlocked() { trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 10000, 0) uiDevice.sleep() - await() lockStateTrackingRule.assertUnlocked() } @@ -80,7 +78,6 @@ class GrantAndRevokeTrustTest { await() uiDevice.sleep() trustAgentRule.agent.revokeTrust() - await() lockStateTrackingRule.assertLocked() } diff --git a/tests/TrustTests/src/android/trust/test/LockUserTest.kt b/tests/TrustTests/src/android/trust/test/LockUserTest.kt index 8f200a64450e..a7dd41ad2e98 100644 --- a/tests/TrustTests/src/android/trust/test/LockUserTest.kt +++ b/tests/TrustTests/src/android/trust/test/LockUserTest.kt @@ -24,7 +24,6 @@ import android.trust.test.lib.TrustAgentRule import android.util.Log import androidx.test.ext.junit.rules.ActivityScenarioRule import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.google.common.truth.Truth.assertThat import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain @@ -52,9 +51,8 @@ class LockUserTest { fun lockUser_locksTheDevice() { Log.i(TAG, "Locking user") trustAgentRule.agent.lockUser() - await() - assertThat(lockStateTrackingRule.lockState.locked).isTrue() + lockStateTrackingRule.assertLocked() } companion object { diff --git a/tests/TrustTests/src/android/trust/test/TemporaryAndRenewableTrustTest.kt b/tests/TrustTests/src/android/trust/test/TemporaryAndRenewableTrustTest.kt new file mode 100644 index 000000000000..14c227b1f678 --- /dev/null +++ b/tests/TrustTests/src/android/trust/test/TemporaryAndRenewableTrustTest.kt @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.trust.test + +import android.service.trust.TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE +import android.trust.BaseTrustAgentService +import android.trust.TrustTestActivity +import android.trust.test.lib.LockStateTrackingRule +import android.trust.test.lib.ScreenLockRule +import android.trust.test.lib.TrustAgentRule +import androidx.test.ext.junit.rules.ActivityScenarioRule +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation +import androidx.test.uiautomator.UiDevice +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.RuleChain +import org.junit.runner.RunWith + +/** + * Test for testing revokeTrust & grantTrust for renewable trust. + * + * atest TrustTests:TemporaryAndRenewableTrustTest + */ +@RunWith(AndroidJUnit4::class) +class TemporaryAndRenewableTrustTest { + private val uiDevice = UiDevice.getInstance(getInstrumentation()) + private val activityScenarioRule = ActivityScenarioRule(TrustTestActivity::class.java) + private val lockStateTrackingRule = LockStateTrackingRule() + private val trustAgentRule = TrustAgentRule<TemporaryAndRenewableTrustAgent>() + + @get:Rule + val rule: RuleChain = RuleChain + .outerRule(activityScenarioRule) + .around(ScreenLockRule()) + .around(lockStateTrackingRule) + .around(trustAgentRule) + + @Before + fun manageTrust() { + trustAgentRule.agent.setManagingTrust(true) + } + + // This test serves a baseline for Grant tests, verifying that the default behavior of the + // device is to lock when put to sleep + @Test + fun sleepingDeviceWithoutGrantLocksDevice() { + uiDevice.sleep() + + lockStateTrackingRule.assertLocked() + } + + @Test + fun grantTrustLockedDevice_deviceStaysLocked() { + uiDevice.sleep() + lockStateTrackingRule.assertLocked() + + trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) + uiDevice.wakeUp() + + lockStateTrackingRule.assertLocked() + } + + @Test + fun grantTrustUnlockedDevice_deviceLocksOnScreenOff() { + trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) + uiDevice.sleep() + + lockStateTrackingRule.assertLocked() + } + + @Test + fun grantTrustLockedDevice_grantTrustOnLockedDeviceUnlocksDevice() { + trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) + uiDevice.sleep() + + lockStateTrackingRule.assertLocked() + + trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) + uiDevice.wakeUp() + + lockStateTrackingRule.assertUnlocked() + } + + @Test + fun grantTrustLockedDevice_revokeTrustPreventsSubsequentUnlock() { + trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) + uiDevice.sleep() + + lockStateTrackingRule.assertLocked() + + trustAgentRule.agent.revokeTrust() + await(500) + uiDevice.wakeUp() + await(500) + + trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) + + lockStateTrackingRule.assertLocked() + } + + companion object { + private const val TAG = "TemporaryAndRenewableTrustTest" + private const val GRANT_MESSAGE = "granted by test" + private fun await(millis: Long) = Thread.sleep(millis) + } +} + +class TemporaryAndRenewableTrustAgent : BaseTrustAgentService() diff --git a/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt b/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt index 0023af8893e2..834f2122a21b 100644 --- a/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt +++ b/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt @@ -52,8 +52,29 @@ class LockStateTrackingRule : TestRule { } } - fun assertLocked() = assertThat(lockState.locked).isTrue() - fun assertUnlocked() = assertThat(lockState.locked).isFalse() + fun assertLocked() { + val maxWaits = 50 + var waitCount = 0 + + while ((lockState.locked == false) && waitCount < maxWaits) { + Log.i(TAG, "phone still locked, wait 50ms more ($waitCount)") + Thread.sleep(50) + waitCount++ + } + assertThat(lockState.locked).isTrue() + } + + fun assertUnlocked() { + val maxWaits = 50 + var waitCount = 0 + + while ((lockState.locked == true) && waitCount < maxWaits) { + Log.i(TAG, "phone still unlocked, wait 50ms more ($waitCount)") + Thread.sleep(50) + waitCount++ + } + assertThat(lockState.locked).isFalse() + } inner class Listener : TrustListener { override fun onTrustChanged( |