diff options
19 files changed, 277 insertions, 53 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java index 5ef6855151cc..30eacf3c7c70 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java @@ -68,6 +68,11 @@ public final class BatteryController extends RestrictingController { */ private final ArraySet<JobStatus> mChangedJobs = new ArraySet<>(); + @GuardedBy("mLock") + private Boolean mLastReportedStatsdBatteryNotLow = null; + @GuardedBy("mLock") + private Boolean mLastReportedStatsdStablePower = null; + public BatteryController(JobSchedulerService service) { super(service); mPowerTracker = new PowerTracker(); @@ -173,6 +178,19 @@ public final class BatteryController extends RestrictingController { Slog.d(TAG, "maybeReportNewChargingStateLocked: " + powerConnected + "/" + stablePower + "/" + batteryNotLow); } + + if (mLastReportedStatsdStablePower == null + || mLastReportedStatsdStablePower != stablePower) { + logDeviceWideConstraintStateToStatsd(JobStatus.CONSTRAINT_CHARGING, stablePower); + mLastReportedStatsdStablePower = stablePower; + } + if (mLastReportedStatsdBatteryNotLow == null + || mLastReportedStatsdBatteryNotLow != stablePower) { + logDeviceWideConstraintStateToStatsd(JobStatus.CONSTRAINT_BATTERY_NOT_LOW, + batteryNotLow); + mLastReportedStatsdBatteryNotLow = batteryNotLow; + } + final long nowElapsed = sElapsedRealtimeClock.millis(); for (int i = mTrackedTasks.size() - 1; i >= 0; i--) { final JobStatus ts = mTrackedTasks.valueAt(i); diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java index f6de109d7ec9..abbe177c5d49 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java @@ -153,6 +153,8 @@ public final class DeviceIdleJobsController extends StateController { changed = true; } mDeviceIdleMode = enabled; + logDeviceWideConstraintStateToStatsd(JobStatus.CONSTRAINT_DEVICE_NOT_DOZING, + !mDeviceIdleMode); if (DEBUG) Slog.d(TAG, "mDeviceIdleMode=" + mDeviceIdleMode); mDeviceIdleUpdateFunctor.prepare(); if (enabled) { diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java index a6fae2c28898..8311dc38f87d 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java @@ -93,6 +93,8 @@ public final class IdleController extends RestrictingController implements Idlen @Override public void reportNewIdleState(boolean isIdle) { synchronized (mLock) { + logDeviceWideConstraintStateToStatsd(JobStatus.CONSTRAINT_IDLE, isIdle); + final long nowElapsed = sElapsedRealtimeClock.millis(); for (int i = mTrackedTasks.size()-1; i >= 0; i--) { mTrackedTasks.valueAt(i).setIdleConstraintSatisfied(nowElapsed, isIdle); 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 866dc41d8dfb..0d85dfd3b951 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 @@ -151,13 +151,12 @@ public final class JobStatus { */ private static final int STATSD_CONSTRAINTS_TO_LOG = CONSTRAINT_CONTENT_TRIGGER | CONSTRAINT_DEADLINE - | CONSTRAINT_IDLE | CONSTRAINT_PREFETCH | CONSTRAINT_TARE_WEALTH | CONSTRAINT_TIMING_DELAY | CONSTRAINT_WITHIN_QUOTA; - // TODO(b/129954980) + // TODO(b/129954980): ensure this doesn't spam statsd, especially at boot private static final boolean STATS_LOG_ENABLED = false; // No override. @@ -1864,7 +1863,7 @@ public final class JobStatus { } /** Returns a {@link JobServerProtoEnums.Constraint} enum value for the given constraint. */ - private int getProtoConstraint(int constraint) { + static int getProtoConstraint(int constraint) { switch (constraint) { case CONSTRAINT_BACKGROUND_NOT_RESTRICTED: return JobServerProtoEnums.CONSTRAINT_BACKGROUND_NOT_RESTRICTED; @@ -1882,8 +1881,12 @@ public final class JobStatus { return JobServerProtoEnums.CONSTRAINT_DEVICE_NOT_DOZING; case CONSTRAINT_IDLE: return JobServerProtoEnums.CONSTRAINT_IDLE; + case CONSTRAINT_PREFETCH: + return JobServerProtoEnums.CONSTRAINT_PREFETCH; case CONSTRAINT_STORAGE_NOT_LOW: return JobServerProtoEnums.CONSTRAINT_STORAGE_NOT_LOW; + case CONSTRAINT_TARE_WEALTH: + return JobServerProtoEnums.CONSTRAINT_TARE_WEALTH; case CONSTRAINT_TIMING_DELAY: return JobServerProtoEnums.CONSTRAINT_TIMING_DELAY; case CONSTRAINT_WITHIN_QUOTA: diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java index 2a2d602b24bf..8453e53782ca 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java @@ -26,6 +26,7 @@ import android.util.Slog; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.FrameworkStatsLog; import com.android.server.job.JobSchedulerService; import com.android.server.job.JobSchedulerService.Constants; import com.android.server.job.StateChangedListener; @@ -165,6 +166,15 @@ public abstract class StateController { return mService.areComponentsInPlaceLocked(jobStatus); } + protected void logDeviceWideConstraintStateToStatsd(int constraint, boolean satisfied) { + FrameworkStatsLog.write( + FrameworkStatsLog.DEVICE_WIDE_JOB_CONSTRAINT_CHANGED, + JobStatus.getProtoConstraint(constraint), + satisfied + ? FrameworkStatsLog.DEVICE_WIDE_JOB_CONSTRAINT_CHANGED__STATE__SATISFIED + : FrameworkStatsLog.DEVICE_WIDE_JOB_CONSTRAINT_CHANGED__STATE__UNSATISFIED); + } + public abstract void dumpControllerStateLocked(IndentingPrintWriter pw, Predicate<JobStatus> predicate); public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, diff --git a/core/api/test-current.txt b/core/api/test-current.txt index f5ee4672b560..93c0c4d7a024 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -3406,6 +3406,7 @@ package android.window { method @NonNull public android.window.WindowContainerTransaction createTaskFragment(@NonNull android.window.TaskFragmentCreationParams); method @NonNull public android.window.WindowContainerTransaction deleteTaskFragment(@NonNull android.window.WindowContainerToken); method public int describeContents(); + method @NonNull public android.window.WindowContainerTransaction finishActivity(@NonNull android.os.IBinder); method @NonNull public android.window.WindowContainerTransaction removeTask(@NonNull android.window.WindowContainerToken); method @NonNull public android.window.WindowContainerTransaction reorder(@NonNull android.window.WindowContainerToken, boolean); method @NonNull public android.window.WindowContainerTransaction reparent(@NonNull android.window.WindowContainerToken, @Nullable android.window.WindowContainerToken, boolean); diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java index ffbdf08e99dc..cfad1afe1b5b 100644 --- a/core/java/android/window/WindowContainerTransaction.java +++ b/core/java/android/window/WindowContainerTransaction.java @@ -706,6 +706,23 @@ public final class WindowContainerTransaction implements Parcelable { } /** + * Finishes the Activity. + * Comparing to directly calling {@link android.app.Activity#finish()}, calling this can make + * sure the finishing happens in the same transaction with other operations. + * @param activityToken activity to be finished. + */ + @NonNull + public WindowContainerTransaction finishActivity(@NonNull IBinder activityToken) { + final HierarchyOp hierarchyOp = + new HierarchyOp.Builder( + HierarchyOp.HIERARCHY_OP_TYPE_FINISH_ACTIVITY) + .setContainer(activityToken) + .build(); + mHierarchyOps.add(hierarchyOp); + return this; + } + + /** * Sets/removes the always on top flag for this {@code windowContainer}. See * {@link com.android.server.wm.ConfigurationContainer#setAlwaysOnTop(boolean)}. * Please note that this method is only intended to be used for a @@ -1163,6 +1180,7 @@ public final class WindowContainerTransaction implements Parcelable { public static final int HIERARCHY_OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT = 18; public static final int HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP = 19; public static final int HIERARCHY_OP_TYPE_REMOVE_TASK = 20; + public static final int HIERARCHY_OP_TYPE_FINISH_ACTIVITY = 21; // The following key(s) are for use with mLaunchOptions: // When launching a task (eg. from recents), this is the taskId to be launched. @@ -1484,6 +1502,8 @@ public final class WindowContainerTransaction implements Parcelable { + " alwaysOnTop=" + mAlwaysOnTop + "}"; case HIERARCHY_OP_TYPE_REMOVE_TASK: return "{RemoveTask: task=" + mContainer + "}"; + case HIERARCHY_OP_TYPE_FINISH_ACTIVITY: + return "{finishActivity: activity=" + mContainer + "}"; default: return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent + " mToTop=" + mToTop diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java index 87e8ac1be611..72b9cd272d02 100644 --- a/core/java/com/android/internal/app/procstats/ProcessState.java +++ b/core/java/com/android/internal/app/procstats/ProcessState.java @@ -473,7 +473,10 @@ public final class ProcessState { } } mCurCombinedState = state; - mStats.mUidStates.get(mUid).updateCombinedState(state, now); + final UidState uidState = mStats.mUidStates.get(mUid); + if (uidState != null) { + uidState.updateCombinedState(state, now); + } } } diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java index d3f9e0a2729e..8fcb6d5514d4 100644 --- a/core/java/com/android/internal/util/LatencyTracker.java +++ b/core/java/com/android/internal/util/LatencyTracker.java @@ -29,6 +29,7 @@ import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPOR import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_SENSOR; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_BACK_ARROW; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_SELECTION_TOOLBAR; +import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_VOICE_INTERACTION; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_START_RECENTS_ANIMATION; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SWITCH_DISPLAY_UNFOLD; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_TOGGLE_RECENTS; @@ -174,6 +175,12 @@ public class LatencyTracker { */ public static final int ACTION_FOLD_TO_AOD = 18; + /** + * Time it takes to show the {@link android.service.voice.VoiceInteractionSession} system UI + * after a {@link android.hardware.soundtrigger3.ISoundTriggerHw} voice trigger. + */ + public static final int ACTION_SHOW_VOICE_INTERACTION = 19; + private static final int[] ACTIONS_ALL = { ACTION_EXPAND_PANEL, ACTION_TOGGLE_RECENTS, @@ -194,6 +201,7 @@ public class LatencyTracker { ACTION_LOAD_SHARE_SHEET, ACTION_SHOW_SELECTION_TOOLBAR, ACTION_FOLD_TO_AOD, + ACTION_SHOW_VOICE_INTERACTION, }; /** @hide */ @@ -217,6 +225,7 @@ public class LatencyTracker { ACTION_LOAD_SHARE_SHEET, ACTION_SHOW_SELECTION_TOOLBAR, ACTION_FOLD_TO_AOD, + ACTION_SHOW_VOICE_INTERACTION, }) @Retention(RetentionPolicy.SOURCE) public @interface Action { @@ -243,6 +252,7 @@ public class LatencyTracker { UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOAD_SHARE_SHEET, UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_SELECTION_TOOLBAR, UIACTION_LATENCY_REPORTED__ACTION__ACTION_FOLD_TO_AOD, + UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_VOICE_INTERACTION, }; private static LatencyTracker sLatencyTracker; @@ -340,6 +350,8 @@ public class LatencyTracker { return "ACTION_SHOW_SELECTION_TOOLBAR"; case UIACTION_LATENCY_REPORTED__ACTION__ACTION_FOLD_TO_AOD: return "ACTION_FOLD_TO_AOD"; + case UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_VOICE_INTERACTION: + return "ACTION_SHOW_VOICE_INTERACTION"; default: throw new IllegalArgumentException("Invalid action"); } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java index 626e0d990a6d..18712aed1be6 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java @@ -432,7 +432,7 @@ class TaskFragmentContainer { // In case we have requested to reparent the activity to another container (as // pendingAppeared), we don't want to finish it with this container. && mController.getContainerWithActivity(activity) == this) { - activity.finish(); + wct.finishActivity(activity.getActivityToken()); } } @@ -457,7 +457,7 @@ class TaskFragmentContainer { || controller.shouldRetainAssociatedActivity(this, activity)) { continue; } - activity.finish(); + wct.finishActivity(activity.getActivityToken()); } mActivitiesToFinishOnExit.clear(); } diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java index 179696a063b1..25d034756265 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java @@ -207,7 +207,7 @@ public class SplitControllerTest { verify(mSplitPresenter, never()).deleteTaskFragment(any(), any()); verify(mSplitController).removeContainer(tf); - verify(mActivity, never()).finish(); + verify(mTransaction, never()).finishActivity(any()); } @Test @@ -1004,9 +1004,9 @@ public class SplitControllerTest { assertTrue(primaryContainer.isFinished()); assertTrue(secondaryContainer0.isFinished()); assertTrue(secondaryContainer1.isFinished()); - verify(mActivity).finish(); - verify(secondaryActivity0).finish(); - verify(secondaryActivity1).finish(); + verify(mTransaction).finishActivity(mActivity.getActivityToken()); + verify(mTransaction).finishActivity(secondaryActivity0.getActivityToken()); + verify(mTransaction).finishActivity(secondaryActivity1.getActivityToken()); assertTrue(taskContainer.mContainers.isEmpty()); assertTrue(taskContainer.mSplitContainers.isEmpty()); } diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java index 73428a2dc800..35415d816d8b 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java @@ -107,30 +107,29 @@ public class TaskFragmentContainerTest { final TaskFragmentContainer container = new TaskFragmentContainer(mActivity, null /* pendingAppearedIntent */, taskContainer, mController); doReturn(container).when(mController).getContainerWithActivity(mActivity); - final WindowContainerTransaction wct = new WindowContainerTransaction(); // Only remove the activity, but not clear the reference until appeared. - container.finish(true /* shouldFinishDependent */, mPresenter, wct, mController); + container.finish(true /* shouldFinishDependent */, mPresenter, mTransaction, mController); - verify(mActivity).finish(); + verify(mTransaction).finishActivity(mActivity.getActivityToken()); verify(mPresenter, never()).deleteTaskFragment(any(), any()); verify(mController, never()).removeContainer(any()); // Calling twice should not finish activity again. - clearInvocations(mActivity); - container.finish(true /* shouldFinishDependent */, mPresenter, wct, mController); + clearInvocations(mTransaction); + container.finish(true /* shouldFinishDependent */, mPresenter, mTransaction, mController); - verify(mActivity, never()).finish(); + verify(mTransaction, never()).finishActivity(any()); verify(mPresenter, never()).deleteTaskFragment(any(), any()); verify(mController, never()).removeContainer(any()); // Remove all references after the container has appeared in server. doReturn(new ArrayList<>()).when(mInfo).getActivities(); container.setInfo(mTransaction, mInfo); - container.finish(true /* shouldFinishDependent */, mPresenter, wct, mController); + container.finish(true /* shouldFinishDependent */, mPresenter, mTransaction, mController); - verify(mActivity, never()).finish(); - verify(mPresenter).deleteTaskFragment(wct, container.getTaskFragmentToken()); + verify(mTransaction, never()).finishActivity(any()); + verify(mPresenter).deleteTaskFragment(mTransaction, container.getTaskFragmentToken()); verify(mController).removeContainer(container); } @@ -150,7 +149,7 @@ public class TaskFragmentContainerTest { // The activity is requested to be reparented, so don't finish it. container0.finish(true /* shouldFinishDependent */, mPresenter, wct, mController); - verify(mActivity, never()).finish(); + verify(mTransaction, never()).finishActivity(any()); verify(mPresenter).deleteTaskFragment(wct, container0.getTaskFragmentToken()); verify(mController).removeContainer(container0); } diff --git a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java index b5fdaca838df..6bb19ce05813 100644 --- a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java +++ b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java @@ -32,6 +32,8 @@ import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_ import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__UNKNOWN_AUTOFILL_DISPLAY_PRESENTATION_TYPE; import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__ANY_SHOWN; import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_ACTIVITY_FINISHED; +import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_FILL_REQUEST_FAILED; +import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_NO_FOCUS; import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_REQUEST_TIMEOUT; import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_SESSION_COMMITTED_PREMATURELY; import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_UNKNOWN_REASON; @@ -72,19 +74,32 @@ public final class PresentationStatsEventLogger { NOT_SHOWN_REASON_VIEW_CHANGED, NOT_SHOWN_REASON_ACTIVITY_FINISHED, NOT_SHOWN_REASON_REQUEST_TIMEOUT, + NOT_SHOWN_REASON_REQUEST_FAILED, + NOT_SHOWN_REASON_NO_FOCUS, NOT_SHOWN_REASON_SESSION_COMMITTED_PREMATURELY, NOT_SHOWN_REASON_UNKNOWN }) @Retention(RetentionPolicy.SOURCE) public @interface NotShownReason {} - public static final int NOT_SHOWN_REASON_ANY_SHOWN = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__ANY_SHOWN; - public static final int NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_FOCUS_CHANGED; - public static final int NOT_SHOWN_REASON_VIEW_CHANGED = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_CHANGED; - public static final int NOT_SHOWN_REASON_ACTIVITY_FINISHED = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_ACTIVITY_FINISHED; - public static final int NOT_SHOWN_REASON_REQUEST_TIMEOUT = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_REQUEST_TIMEOUT; - public static final int NOT_SHOWN_REASON_SESSION_COMMITTED_PREMATURELY = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_SESSION_COMMITTED_PREMATURELY; - public static final int NOT_SHOWN_REASON_UNKNOWN = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_UNKNOWN_REASON; + public static final int NOT_SHOWN_REASON_ANY_SHOWN = + AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__ANY_SHOWN; + public static final int NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED = + AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_FOCUS_CHANGED; + public static final int NOT_SHOWN_REASON_VIEW_CHANGED = + AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_CHANGED; + public static final int NOT_SHOWN_REASON_ACTIVITY_FINISHED = + AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_ACTIVITY_FINISHED; + public static final int NOT_SHOWN_REASON_REQUEST_TIMEOUT = + AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_REQUEST_TIMEOUT; + public static final int NOT_SHOWN_REASON_REQUEST_FAILED = + AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_FILL_REQUEST_FAILED; + public static final int NOT_SHOWN_REASON_NO_FOCUS = + AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_NO_FOCUS; + public static final int NOT_SHOWN_REASON_SESSION_COMMITTED_PREMATURELY = + AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_SESSION_COMMITTED_PREMATURELY; + public static final int NOT_SHOWN_REASON_UNKNOWN = + AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_UNKNOWN_REASON; private final int mSessionId; private Optional<PresentationStatsEventInternal> mEventInternal; @@ -118,6 +133,14 @@ public final class PresentationStatsEventLogger { }); } + public void maybeSetNoPresentationEventReasonIfNoReasonExists(@NotShownReason int reason) { + mEventInternal.ifPresent(event -> { + if (event.mCountShown == 0 && event.mNoPresentationReason == NOT_SHOWN_REASON_UNKNOWN) { + event.mNoPresentationReason = reason; + } + }); + } + public void maybeSetAvailableCount(@Nullable List<Dataset> datasetList, AutofillId currentViewId) { mEventInternal.ifPresent(event -> { @@ -180,7 +203,8 @@ public final class PresentationStatsEventLogger { public void maybeSetInlinePresentationAndSuggestionHostUid(Context context, int userId) { mEventInternal.ifPresent(event -> { - event.mDisplayPresentationType = UI_TYPE_INLINE; + event.mDisplayPresentationType = + AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__INLINE; String imeString = Settings.Secure.getStringForUser(context.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD, userId); if (TextUtils.isEmpty(imeString)) { diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 3d955b7718d0..5c11e2c2327e 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -43,6 +43,8 @@ import static com.android.server.autofill.Helper.getNumericValue; import static com.android.server.autofill.Helper.sDebug; import static com.android.server.autofill.Helper.sVerbose; import static com.android.server.autofill.Helper.toArray; +import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_NO_FOCUS; +import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_REQUEST_FAILED; import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_REQUEST_TIMEOUT; import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_VIEW_CHANGED; import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED; @@ -402,6 +404,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private PresentationStatsEventLogger mPresentationStatsEventLogger; + /** + * Fill dialog request would likely be sent slightly later. + */ + @NonNull + @GuardedBy("mLock") + private boolean mStartedLogEventWithoutFocus; + void onSwitchInputMethodLocked() { // One caveat is that for the case where the focus is on a field for which regular autofill // returns null, and augmented autofill is triggered, and then the user switches the input @@ -522,6 +531,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } mLastFillRequest = mPendingFillRequest; + mPresentationStatsEventLogger.maybeSetIsNewRequest(true); mRemoteFillService.onFillRequest(mPendingFillRequest); mPendingInlineSuggestionsRequest = null; mWaitForInlineRequest = false; @@ -1128,6 +1138,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @Override public void onFillRequestSuccess(int requestId, @Nullable FillResponse response, @NonNull String servicePackageName, int requestFlags) { + final AutofillId[] fieldClassificationIds; final LogMaker requestLog; @@ -1285,9 +1296,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - // TODO(b/234185326): Add separate reason for failures. mPresentationStatsEventLogger.maybeSetNoPresentationEventReason( - NOT_SHOWN_REASON_REQUEST_TIMEOUT); + timedOut ? NOT_SHOWN_REASON_REQUEST_TIMEOUT : NOT_SHOWN_REASON_REQUEST_FAILED); mPresentationStatsEventLogger.logAndEndEvent(); } notifyUnavailableToClient(AutofillManager.STATE_UNKNOWN_FAILED, @@ -2816,6 +2826,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private boolean requestNewFillResponseOnViewEnteredIfNecessaryLocked(@NonNull AutofillId id, @NonNull ViewState viewState, int flags) { + // Force new response for manual request if ((flags & FLAG_MANUAL_REQUEST) != 0) { mSessionFlags.mAugmentedAutofillOnly = false; if (sDebug) Slog.d(TAG, "Re-starting session on view " + id + " and flags " + flags); @@ -2823,7 +2834,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return true; } - // If it's not, then check if it it should start a partition. + // If it's not, then check if it should start a partition. if (shouldStartNewPartitionLocked(id)) { if (sDebug) { Slog.d(TAG, "Starting partition or augmented request for view id " + id + ": " @@ -2922,7 +2933,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (sDebug) { Slog.d(TAG, "Set the response has expired."); } - mPresentationStatsEventLogger.maybeSetNoPresentationEventReason( + mPresentationStatsEventLogger.maybeSetNoPresentationEventReasonIfNoReasonExists( NOT_SHOWN_REASON_VIEW_CHANGED); mPresentationStatsEventLogger.logAndEndEvent(); return; @@ -2967,11 +2978,19 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // View is triggering autofill. mCurrentViewId = viewState.id; viewState.update(value, virtualBounds, flags); - if (!isRequestSupportFillDialog(flags)) { - mSessionFlags.mFillDialogDisabled = true; - } mPresentationStatsEventLogger.startNewEvent(); mPresentationStatsEventLogger.maybeSetAutofillServiceUid(getAutofillServiceUid()); + if (isRequestSupportFillDialog(flags)) { + // Set the default reason for now if the user doesn't trigger any focus event + // on the autofillable view. This can be changed downstream when more + // information is available or session is committed. + mPresentationStatsEventLogger.maybeSetNoPresentationEventReason( + NOT_SHOWN_REASON_NO_FOCUS); + mStartedLogEventWithoutFocus = true; + } else { + mSessionFlags.mFillDialogDisabled = true; + mStartedLogEventWithoutFocus = false; + } requestNewFillResponseLocked(viewState, ViewState.STATE_STARTED_SESSION, flags); break; case ACTION_VALUE_CHANGED: @@ -3014,6 +3033,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } break; case ACTION_VIEW_ENTERED: + boolean startedEventWithoutFocus = mStartedLogEventWithoutFocus; + mStartedLogEventWithoutFocus = false; if (sVerbose && virtualBounds != null) { Slog.v(TAG, "entered on virtual child " + id + ": " + virtualBounds); } @@ -3030,9 +3051,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } - mPresentationStatsEventLogger.maybeSetNoPresentationEventReason( - NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED); - mPresentationStatsEventLogger.logAndEndEvent(); + // Previously, fill request will only start whenever a view is entered. + // With Fill Dialog, request starts prior to view getting entered. So, we can't end + // the event at this moment, otherwise we will be wrongly attributing fill dialog + // event as concluded. + if (!startedEventWithoutFocus) { + mPresentationStatsEventLogger.maybeSetNoPresentationEventReason( + NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED); + mPresentationStatsEventLogger.logAndEndEvent(); + } if ((flags & FLAG_MANUAL_REQUEST) == 0) { // Not a manual request @@ -3060,9 +3087,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - mPresentationStatsEventLogger.startNewEvent(); - mPresentationStatsEventLogger.maybeSetAutofillServiceUid(getAutofillServiceUid()); + if (!startedEventWithoutFocus) { + mPresentationStatsEventLogger.startNewEvent(); + mPresentationStatsEventLogger.maybeSetAutofillServiceUid( + getAutofillServiceUid()); + } if (requestNewFillResponseOnViewEnteredIfNecessaryLocked(id, viewState, flags)) { + // If a new request was issued even if previously it was fill dialog request, + // we should end the log event, and start a new one. However, it leaves us + // susceptible to race condition. But since mPresentationStatsEventLogger is + // lock guarded, we should be safe. + if (startedEventWithoutFocus) { + mPresentationStatsEventLogger.logAndEndEvent(); + mPresentationStatsEventLogger.startNewEvent(); + mPresentationStatsEventLogger.maybeSetAutofillServiceUid( + getAutofillServiceUid()); + } return; } @@ -3289,7 +3329,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (requestShowInlineSuggestionsLocked(response, filterText)) { final ViewState currentView = mViewStates.get(mCurrentViewId); currentView.setState(ViewState.STATE_INLINE_SHOWN); - // TODO(b/137800469): Fix it to log showed only when IME asks for inflation, + // TODO(b/248378401): Fix it to log showed only when IME asks for inflation, // rather than here where framework sends back the response. mService.logDatasetShown(id, mClientState, UI_TYPE_INLINE); @@ -3310,7 +3350,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState synchronized (mLock) { mService.logDatasetShown(id, mClientState, UI_TYPE_MENU); - mPresentationStatsEventLogger.maybeSetCountShown( response.getDatasets(), mCurrentViewId); mPresentationStatsEventLogger.maybeSetDisplayPresentationType(UI_TYPE_MENU); diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java index dc4bdaaa8158..ce1157e1d80c 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java @@ -18,6 +18,7 @@ package com.android.server.soundtrigger_middleware; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.Context; import android.media.permission.Identity; import android.media.permission.IdentityContext; import android.media.soundtrigger.ModelParameterRange; @@ -33,6 +34,8 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Log; +import com.android.internal.util.LatencyTracker; + import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.Date; @@ -65,9 +68,12 @@ import java.util.Objects; public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInternal, Dumpable { private static final String TAG = "SoundTriggerMiddlewareLogging"; private final @NonNull ISoundTriggerMiddlewareInternal mDelegate; + private final @NonNull Context mContext; - public SoundTriggerMiddlewareLogging(@NonNull ISoundTriggerMiddlewareInternal delegate) { + public SoundTriggerMiddlewareLogging(@NonNull Context context, + @NonNull ISoundTriggerMiddlewareInternal delegate) { mDelegate = delegate; + mContext = context; } @Override @@ -298,6 +304,7 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInt int captureSession) throws RemoteException { try { + startKeyphraseEventLatencyTracking(event); mCallbackDelegate.onPhraseRecognition(modelHandle, event, captureSession); logVoidReturn("onPhraseRecognition", modelHandle, event); } catch (Exception e) { @@ -347,6 +354,26 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInt logVoidReturnWithObject(this, mOriginatorIdentity, methodName, args); } + /** + * Starts the latency tracking log for keyphrase hotword invocation. + * The measurement covers from when the SoundTrigger HAL emits an event to when the + * {@link android.service.voice.VoiceInteractionSession} system UI view is shown. + */ + private void startKeyphraseEventLatencyTracking(PhraseRecognitionEvent event) { + String latencyTrackerTag = null; + if (event.phraseExtras.length > 0) { + latencyTrackerTag = "KeyphraseId=" + event.phraseExtras[0].id; + } + LatencyTracker latencyTracker = LatencyTracker.getInstance(mContext); + // To avoid adding cancel to all of the different failure modes between here and + // showing the system UI, we defensively cancel once. + // Either we hit the LatencyTracker timeout of 15 seconds or we defensively cancel + // here if any error occurs. + latencyTracker.onActionCancel(LatencyTracker.ACTION_SHOW_VOICE_INTERACTION); + latencyTracker.onActionStart(LatencyTracker.ACTION_SHOW_VOICE_INTERACTION, + latencyTrackerTag); + } + @Override public IBinder asBinder() { return mCallbackDelegate.asBinder(); diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java index 1995e5497e55..807ed14e85ce 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java @@ -20,14 +20,14 @@ import static android.Manifest.permission.SOUNDTRIGGER_DELEGATE_IDENTITY; import android.annotation.NonNull; import android.content.Context; -import android.media.soundtrigger.ModelParameterRange; -import android.media.soundtrigger.PhraseSoundModel; -import android.media.soundtrigger.RecognitionConfig; -import android.media.soundtrigger.SoundModel; import android.media.permission.ClearCallingIdentityContext; import android.media.permission.Identity; import android.media.permission.PermissionUtil; import android.media.permission.SafeCloseable; +import android.media.soundtrigger.ModelParameterRange; +import android.media.soundtrigger.PhraseSoundModel; +import android.media.soundtrigger.RecognitionConfig; +import android.media.soundtrigger.SoundModel; import android.media.soundtrigger_middleware.ISoundTriggerCallback; import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService; import android.media.soundtrigger_middleware.ISoundTriggerModule; @@ -226,12 +226,13 @@ public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareServic HalFactory[] factories = new HalFactory[]{new DefaultHalFactory()}; publishBinderService(Context.SOUND_TRIGGER_MIDDLEWARE_SERVICE, - new SoundTriggerMiddlewareService(new SoundTriggerMiddlewareLogging( - new SoundTriggerMiddlewarePermission( - new SoundTriggerMiddlewareValidation( - new SoundTriggerMiddlewareImpl(factories, - new AudioSessionProviderImpl())), - getContext())), getContext())); + new SoundTriggerMiddlewareService( + new SoundTriggerMiddlewareLogging(getContext(), + new SoundTriggerMiddlewarePermission( + new SoundTriggerMiddlewareValidation( + new SoundTriggerMiddlewareImpl(factories, + new AudioSessionProviderImpl())), + getContext())), getContext())); } } } diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 9456f0fcced7..2e1477ddf0f1 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -25,6 +25,7 @@ import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_FINISH_ACTIVITY; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_LAUNCH_TASK; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_INSETS_PROVIDER; @@ -1007,6 +1008,20 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub isInLockTaskMode); break; } + case HIERARCHY_OP_TYPE_FINISH_ACTIVITY: { + final ActivityRecord activity = ActivityRecord.forTokenLocked(hop.getContainer()); + if (activity == null || activity.finishing) { + break; + } + if (activity.isVisible()) { + // Prevent the transition from being executed too early if the activity is + // visible. + activity.finishIfPossible("finish-activity-op", false /* oomAdj */); + } else { + activity.destroyIfPossible("finish-activity-op"); + } + break; + } case HIERARCHY_OP_TYPE_LAUNCH_TASK: { mService.mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS, "launchTask HierarchyOp"); @@ -1620,6 +1635,9 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub organizer); } break; + case HIERARCHY_OP_TYPE_FINISH_ACTIVITY: + // Allow finish activity if it has the activity token. + break; default: // Other types of hierarchy changes are not allowed. String msg = "Permission Denial: " + func + " from pid=" diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java index 61cf8cc76d83..1404de253476 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java @@ -730,6 +730,16 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { } @Test + public void testApplyTransaction_finishActivity() { + final ActivityRecord activity = createActivityRecord(mDisplayContent); + + mTransaction.finishActivity(activity.token); + assertApplyTransactionAllowed(mTransaction); + + assertTrue(activity.finishing); + } + + @Test public void testApplyTransaction_skipTransactionForUnregisterOrganizer() { mController.unregisterOrganizer(mIOrganizer); final ActivityRecord ownerActivity = createActivityRecord(mDisplayContent); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 0ce0265c3dc5..3da47110fb49 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -95,6 +95,7 @@ import com.android.internal.app.IVoiceInteractor; import com.android.internal.content.PackageMonitor; import com.android.internal.os.BackgroundThread; import com.android.internal.util.DumpUtils; +import com.android.internal.util.LatencyTracker; import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.SystemService; @@ -191,6 +192,8 @@ public class VoiceInteractionManagerService extends SystemService { mSoundTriggerInternal = LocalServices.getService(SoundTriggerInternal.class); } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { mServiceStub.systemRunning(isSafeMode()); + } else if (phase == PHASE_BOOT_COMPLETED) { + mServiceStub.registerVoiceInteractionSessionListener(mLatencyLoggingListener); } } @@ -2334,4 +2337,36 @@ public class VoiceInteractionManagerService extends SystemService { } }; } + + /** + * End the latency tracking log for keyphrase hotword invocation. + * The measurement covers from when the SoundTrigger HAL emits an event, captured in + * {@link com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareLogging} + * to when the {@link android.service.voice.VoiceInteractionSession} system UI view is shown. + */ + private final IVoiceInteractionSessionListener mLatencyLoggingListener = + new IVoiceInteractionSessionListener.Stub() { + @Override + public void onVoiceSessionShown() throws RemoteException {} + + @Override + public void onVoiceSessionHidden() throws RemoteException {} + + @Override + public void onVoiceSessionWindowVisibilityChanged(boolean visible) + throws RemoteException { + if (visible) { + LatencyTracker.getInstance(mContext) + .onActionEnd(LatencyTracker.ACTION_SHOW_VOICE_INTERACTION); + } + } + + @Override + public void onSetUiHints(Bundle args) throws RemoteException {} + + @Override + public IBinder asBinder() { + return mServiceStub; + } + }; } |