Merge "Monitor package change to update AttributeCache for non-system server." into udc-qpr-dev
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index b6dc32a..0883727 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -1246,6 +1246,40 @@
return "TRANSPORT_IS_NULL";
case BackupManagerMonitor.LOG_EVENT_ID_AGENT_LOGGING_RESULTS:
return "AGENT_LOGGING_RESULTS";
+ case BackupManagerMonitor.LOG_EVENT_ID_START_SYSTEM_RESTORE:
+ return "START_SYSTEM_RESTORE";
+ case BackupManagerMonitor.LOG_EVENT_ID_START_RESTORE_AT_INSTALL:
+ return "START_RESTORE_AT_INSTALL";
+ case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_DURING_START_RESTORE:
+ return "TRANSPORT_ERROR_DURING_START_RESTORE";
+ case BackupManagerMonitor.LOG_EVENT_ID_CANNOT_GET_NEXT_PKG_NAME:
+ return "CANNOT_GET_NEXT_PKG_NAME";
+ case BackupManagerMonitor.LOG_EVENT_ID_UNKNOWN_RESTORE_TYPE:
+ return "UNKNOWN_RESTORE_TYPE";
+ case BackupManagerMonitor.LOG_EVENT_ID_KV_RESTORE:
+ return "KV_RESTORE";
+ case BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE:
+ return "FULL_RESTORE";
+ case BackupManagerMonitor.LOG_EVENT_ID_NO_NEXT_RESTORE_TARGET:
+ return "NO_NEXT_RESTORE_TARGET";
+ case BackupManagerMonitor.LOG_EVENT_ID_KV_AGENT_ERROR:
+ return "KV_AGENT_ERROR";
+ case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_RESTORE_FINISHED:
+ return "PACKAGE_RESTORE_FINISHED";
+ case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_KV_RESTORE:
+ return "TRANSPORT_ERROR_KV_RESTORE";
+ case BackupManagerMonitor.LOG_EVENT_ID_NO_FEEDER_THREAD:
+ return "NO_FEEDER_THREAD";
+ case BackupManagerMonitor.LOG_EVENT_ID_FULL_AGENT_ERROR:
+ return "FULL_AGENT_ERROR";
+ case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_FULL_RESTORE:
+ return "TRANSPORT_ERROR_FULL_RESTORE";
+ case BackupManagerMonitor.LOG_EVENT_ID_RESTORE_COMPLETE:
+ return "RESTORE_COMPLETE";
+ case BackupManagerMonitor.LOG_EVENT_ID_START_PACKAGE_RESTORE:
+ return "START_PACKAGE_RESTORE";
+ case BackupManagerMonitor.LOG_EVENT_ID_AGENT_FAILURE:
+ return "AGENT_FAILURE";
default:
return "UNKNOWN_ID";
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index ff0f437..66d04a3 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -5167,11 +5167,21 @@
* @hide
*/
public static void broadcastStickyIntent(Intent intent, int appOp, Bundle options, int userId) {
+ broadcastStickyIntent(intent, null, appOp, options, userId);
+ }
+
+ /**
+ * Convenience for sending a sticky broadcast. For internal use only.
+ *
+ * @hide
+ */
+ public static void broadcastStickyIntent(Intent intent, String[] excludedPackages,
+ int appOp, Bundle options, int userId) {
try {
getService().broadcastIntentWithFeature(
null, null, intent, null, null, Activity.RESULT_OK, null, null,
null /*requiredPermissions*/, null /*excludedPermissions*/,
- null /*excludedPackages*/, appOp, options, false, true, userId);
+ excludedPackages, appOp, options, false, true, userId);
} catch (RemoteException ex) {
}
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 1571fdd..f4caef0 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -255,6 +255,7 @@
import java.util.TimeZone;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
/**
* This manages the execution of the main thread in an
@@ -370,6 +371,11 @@
@GuardedBy("mAppThread")
private int mLastProcessState = PROCESS_STATE_UNKNOWN;
ArrayList<WeakReference<AssistStructure>> mLastAssistStructures = new ArrayList<>();
+
+ @NonNull
+ private final ConfigurationChangedListenerController mConfigurationChangedListenerController =
+ new ConfigurationChangedListenerController();
+
private int mLastSessionId;
// Holds the value of the last reported device ID value from the server for the top activity.
int mLastReportedDeviceId;
@@ -3540,6 +3546,21 @@
return mConfigurationController.getConfiguration();
}
+ /**
+ * @hide
+ */
+ public void addConfigurationChangedListener(Executor executor,
+ Consumer<IBinder> consumer) {
+ mConfigurationChangedListenerController.addListener(executor, consumer);
+ }
+
+ /**
+ * @hide
+ */
+ public void removeConfigurationChangedListener(Consumer<IBinder> consumer) {
+ mConfigurationChangedListenerController.removeListener(consumer);
+ }
+
@Override
public void updatePendingConfiguration(Configuration config) {
final Configuration updatedConfig =
@@ -6098,6 +6119,8 @@
" did not call through to super.onConfigurationChanged()");
}
}
+ mConfigurationChangedListenerController
+ .dispatchOnConfigurationChanged(activity.getActivityToken());
return configToReport;
}
diff --git a/core/java/android/app/ConfigurationChangedListenerController.java b/core/java/android/app/ConfigurationChangedListenerController.java
new file mode 100644
index 0000000..c644d57
--- /dev/null
+++ b/core/java/android/app/ConfigurationChangedListenerController.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2023 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.app;
+
+import android.annotation.NonNull;
+import android.os.IBinder;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * Manages listeners for unfiltered configuration changes.
+ * @hide
+ */
+class ConfigurationChangedListenerController {
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private final List<ListenerContainer> mListenerContainers = new ArrayList<>();
+
+ /**
+ * Adds a listener to receive updates when they are dispatched. This only dispatches updates and
+ * does not relay the last emitted value. If called with the same listener then this method does
+ * not have any effect.
+ * @param executor an executor that is used to dispatch the updates.
+ * @param consumer a listener interested in receiving updates.
+ */
+ void addListener(@NonNull Executor executor,
+ @NonNull Consumer<IBinder> consumer) {
+ synchronized (mLock) {
+ if (indexOf(consumer) > -1) {
+ return;
+ }
+ mListenerContainers.add(new ListenerContainer(executor, consumer));
+ }
+ }
+
+ /**
+ * Removes the listener that was previously registered. If the listener was not registered this
+ * method does not have any effect.
+ */
+ void removeListener(@NonNull Consumer<IBinder> consumer) {
+ synchronized (mLock) {
+ final int index = indexOf(consumer);
+ if (index > -1) {
+ mListenerContainers.remove(index);
+ }
+ }
+ }
+
+ /**
+ * Dispatches the update to all registered listeners
+ * @param activityToken a token for the {@link Activity} that received a configuration update.
+ */
+ void dispatchOnConfigurationChanged(@NonNull IBinder activityToken) {
+ final List<ListenerContainer> consumers;
+ synchronized (mLock) {
+ consumers = new ArrayList<>(mListenerContainers);
+ }
+ for (int i = 0; i < consumers.size(); i++) {
+ consumers.get(i).accept(activityToken);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private int indexOf(Consumer<IBinder> consumer) {
+ for (int i = 0; i < mListenerContainers.size(); i++) {
+ if (mListenerContainers.get(i).isMatch(consumer)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private static final class ListenerContainer {
+
+ @NonNull
+ private final Executor mExecutor;
+ @NonNull
+ private final Consumer<IBinder> mConsumer;
+
+ ListenerContainer(@NonNull Executor executor,
+ @NonNull Consumer<IBinder> consumer) {
+ mExecutor = executor;
+ mConsumer = consumer;
+ }
+
+ public boolean isMatch(@NonNull Consumer<IBinder> consumer) {
+ return mConsumer.equals(consumer);
+ }
+
+ public void accept(@NonNull IBinder activityToken) {
+ mExecutor.execute(() -> mConsumer.accept(activityToken));
+ }
+
+ }
+}
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 634089b..5f8b765 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -255,6 +255,12 @@
public boolean isUserFullscreenOverrideEnabled;
/**
+ * Whether the top activity fillsParent() is false
+ * @hide
+ */
+ public boolean isTopActivityTransparent;
+
+ /**
* Hint about the letterbox state of the top activity.
* @hide
*/
@@ -551,7 +557,8 @@
&& Objects.equals(mTopActivityLocusId, that.mTopActivityLocusId)
&& parentTaskId == that.parentTaskId
&& Objects.equals(topActivity, that.topActivity)
- && isUserFullscreenOverrideEnabled == that.isUserFullscreenOverrideEnabled;
+ && isUserFullscreenOverrideEnabled == that.isUserFullscreenOverrideEnabled
+ && isTopActivityTransparent == that.isTopActivityTransparent;
}
/**
@@ -583,7 +590,9 @@
== that.configuration.getLayoutDirection())
&& (!hasCompatUI() || configuration.uiMode == that.configuration.uiMode)
&& (!hasCompatUI() || isVisible == that.isVisible)
- && isUserFullscreenOverrideEnabled == that.isUserFullscreenOverrideEnabled;
+ && isFocused == that.isFocused
+ && isUserFullscreenOverrideEnabled == that.isUserFullscreenOverrideEnabled
+ && isTopActivityTransparent == that.isTopActivityTransparent;
}
/**
@@ -640,6 +649,7 @@
topActivityLetterboxWidth = source.readInt();
topActivityLetterboxHeight = source.readInt();
isUserFullscreenOverrideEnabled = source.readBoolean();
+ isTopActivityTransparent = source.readBoolean();
}
/**
@@ -697,6 +707,7 @@
dest.writeInt(topActivityLetterboxWidth);
dest.writeInt(topActivityLetterboxHeight);
dest.writeBoolean(isUserFullscreenOverrideEnabled);
+ dest.writeBoolean(isTopActivityTransparent);
}
@Override
@@ -744,6 +755,7 @@
+ " topActivityLetterboxWidth=" + topActivityLetterboxWidth
+ " topActivityLetterboxHeight=" + topActivityLetterboxHeight
+ " isUserFullscreenOverrideEnabled=" + isUserFullscreenOverrideEnabled
+ + " isTopActivityTransparent=" + isTopActivityTransparent
+ " locusId=" + mTopActivityLocusId
+ " displayAreaFeatureId=" + displayAreaFeatureId
+ " cameraCompatControlState="
diff --git a/core/java/android/app/backup/BackupManagerMonitor.java b/core/java/android/app/backup/BackupManagerMonitor.java
index f73366b..812bf8e 100644
--- a/core/java/android/app/backup/BackupManagerMonitor.java
+++ b/core/java/android/app/backup/BackupManagerMonitor.java
@@ -18,6 +18,7 @@
import android.annotation.SystemApi;
import android.app.backup.BackupAnnotations.OperationType;
+import android.content.pm.PackageInfo;
import android.os.Bundle;
/**
@@ -190,6 +191,56 @@
public static final int LOG_EVENT_ID_TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED = 51;
public static final int LOG_EVENT_ID_AGENT_LOGGING_RESULTS = 52;
+ /** @hide */
+ public static final int LOG_EVENT_ID_START_SYSTEM_RESTORE = 53;
+ /** @hide */
+ public static final int LOG_EVENT_ID_START_RESTORE_AT_INSTALL = 54;
+ /** A transport error happened during {@link PerformUnifiedRestoreTask#startRestore()}
+ @hide */
+ public static final int LOG_EVENT_ID_TRANSPORT_ERROR_DURING_START_RESTORE = 55;
+ /** Unable to get the name of the next package in the queue during a restore operation
+ @hide */
+ public static final int LOG_EVENT_ID_CANNOT_GET_NEXT_PKG_NAME = 56;
+ /** Attempting a restore operation that is neither KV nor full
+ @hide */
+ public static final int LOG_EVENT_ID_UNKNOWN_RESTORE_TYPE = 57;
+ /** The package is part of KeyValue restore
+ @hide */
+ public static final int LOG_EVENT_ID_KV_RESTORE = 58;
+ /** The package is part of Full restore
+ @hide */
+ public static final int LOG_EVENT_ID_FULL_RESTORE = 59;
+ /** Unable to fetch the nest restore target in the queue
+ @hide */
+ public static final int LOG_EVENT_ID_NO_NEXT_RESTORE_TARGET = 60;
+ /** An error occurred while attempting KeyValueRestore
+ @hide */
+ public static final int LOG_EVENT_ID_KV_AGENT_ERROR = 61;
+ /** Restore operation finished for the given package
+ @hide */
+ public static final int LOG_EVENT_ID_PACKAGE_RESTORE_FINISHED= 62;
+ /** A transport error happened during
+ * {@link PerformUnifiedRestoreTask#initiateOneRestore(PackageInfo, long)}
+ @hide */
+ public static final int LOG_EVENT_ID_TRANSPORT_ERROR_KV_RESTORE = 63;
+ /** Unable to instantiate the feeder thread in full restore
+ @hide */
+ public static final int LOG_EVENT_ID_NO_FEEDER_THREAD = 64;
+ /** An error occurred while attempting Full restore
+ @hide */
+ public static final int LOG_EVENT_ID_FULL_AGENT_ERROR = 65;
+ /** A transport error happened during a full restore
+ @hide */
+ public static final int LOG_EVENT_ID_TRANSPORT_ERROR_FULL_RESTORE = 66;
+ /** Start restore operation for a given package
+ @hide */
+ public static final int LOG_EVENT_ID_START_PACKAGE_RESTORE = 67;
+ /** Whole restore operation is complete
+ @hide */
+ public static final int LOG_EVENT_ID_RESTORE_COMPLETE = 68;
+ /** Agent error during {@link PerformUnifiedRestoreTask#restoreFinished()}
+ @hide */
+ public static final int LOG_EVENT_ID_AGENT_FAILURE = 69;
/**
* This method will be called each time something important happens on BackupManager.
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 9f9c222..cd6acfe 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1555,7 +1555,7 @@
String attestProp = getString(
TextUtils.formatSimple("ro.product.%s_for_attestation", property));
return attestProp.equals(UNKNOWN)
- ? getString(TextUtils.formatSimple("ro.product.vendor.%s", property)) : UNKNOWN;
+ ? getString(TextUtils.formatSimple("ro.product.vendor.%s", property)) : attestProp;
}
private static String[] getStringList(String property, String separator) {
diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java
index ecf1770..dc02470 100644
--- a/core/java/android/printservice/PrintService.java
+++ b/core/java/android/printservice/PrintService.java
@@ -178,7 +178,7 @@
* <pre> <print-service
* android:vendor="SomeVendor"
* android:settingsActivity="foo.bar.MySettingsActivity"
- * andorid:addPrintersActivity="foo.bar.MyAddPrintersActivity."
+ * android:addPrintersActivity="foo.bar.MyAddPrintersActivity."
* . . .
* /></pre>
* <p>
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index c2afb4b..cd2d36c 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -587,6 +587,9 @@
@NonNull Display mDisplay;
final String mBasePackageName;
+ // If we would like to keep a particular eye on the corresponding package.
+ final boolean mExtraDisplayListenerLogging;
+
final int[] mTmpLocation = new int[2];
final TypedValue mTmpValue = new TypedValue();
@@ -1136,6 +1139,8 @@
mWindowLayout = windowLayout;
mDisplay = display;
mBasePackageName = context.getBasePackageName();
+ final String name = DisplayProperties.debug_vri_package().orElse(null);
+ mExtraDisplayListenerLogging = !TextUtils.isEmpty(name) && name.equals(mBasePackageName);
mThread = Thread.currentThread();
mLocation = new WindowLeaked(null);
mLocation.fillInStackTrace();
@@ -1577,6 +1582,10 @@
// We should update mAttachInfo.mDisplayState after registerDisplayListener
// because displayState might be changed before registerDisplayListener.
mAttachInfo.mDisplayState = mDisplay.getState();
+ if (mExtraDisplayListenerLogging) {
+ Slog.i(mTag, "(" + mBasePackageName + ") Initial DisplayState: "
+ + mAttachInfo.mDisplayState, new Throwable());
+ }
if ((res & WindowManagerGlobal.ADD_FLAG_USE_BLAST) != 0) {
mUseBLASTAdapter = true;
}
@@ -1661,6 +1670,9 @@
* Register any kind of listeners if setView was success.
*/
private void registerListeners() {
+ if (mExtraDisplayListenerLogging) {
+ Slog.i(mTag, "Register listeners: " + mBasePackageName);
+ }
mAccessibilityManager.addAccessibilityStateChangeListener(
mAccessibilityInteractionConnectionManager, mHandler);
mAccessibilityManager.addHighTextContrastStateChangeListener(
@@ -1686,6 +1698,9 @@
DisplayManagerGlobal
.getInstance()
.unregisterDisplayListener(mDisplayListener);
+ if (mExtraDisplayListenerLogging) {
+ Slog.w(mTag, "Unregister listeners: " + mBasePackageName, new Throwable());
+ }
}
private void setTag() {
@@ -2093,9 +2108,16 @@
private final DisplayListener mDisplayListener = new DisplayListener() {
@Override
public void onDisplayChanged(int displayId) {
+ if (mExtraDisplayListenerLogging) {
+ Slog.i(mTag, "Received onDisplayChanged - " + mView);
+ }
if (mView != null && mDisplay.getDisplayId() == displayId) {
final int oldDisplayState = mAttachInfo.mDisplayState;
final int newDisplayState = mDisplay.getState();
+ if (mExtraDisplayListenerLogging) {
+ Slog.i(mTag, "DisplayState - old: " + oldDisplayState
+ + ", new: " + newDisplayState);
+ }
if (oldDisplayState != newDisplayState) {
mAttachInfo.mDisplayState = newDisplayState;
pokeDrawLockIfNeeded();
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index d729d49..a7fbaf6 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1436,7 +1436,7 @@
}
for (int i = 0; i < infos.size(); i++) {
final VirtualViewFillInfo info = infos.valueAt(i);
- final int virtualId = infos.indexOfKey(i);
+ final int virtualId = infos.keyAt(i);
notifyViewReadyInner(getAutofillId(view, virtualId),
(info == null) ? null : info.getAutofillHints());
}
@@ -1450,9 +1450,6 @@
* @hide
*/
public void notifyViewEnteredForFillDialog(View v) {
- if (sDebug) {
- Log.d(TAG, "notifyViewEnteredForFillDialog:" + v.getAutofillId());
- }
if (v.isCredential()
&& mIsFillAndSaveDialogDisabledForCredentialManager) {
if (sDebug) {
@@ -1465,11 +1462,14 @@
notifyViewReadyInner(v.getAutofillId(), v.getAutofillHints());
}
- private void notifyViewReadyInner(AutofillId id, String[] autofillHints) {
+ private void notifyViewReadyInner(AutofillId id, @Nullable String[] autofillHints) {
+ if (sDebug) {
+ Log.d(TAG, "notifyViewReadyInner:" + id);
+ }
+
if (!hasAutofillFeature()) {
return;
}
-
synchronized (mLock) {
if (mAllTrackedViews.contains(id)) {
// The id is tracked and will not trigger pre-fill request again.
@@ -1505,26 +1505,38 @@
final boolean clientAdded = tryAddServiceClientIfNeededLocked();
if (clientAdded) {
startSessionLocked(/* id= */ AutofillId.NO_AUTOFILL_ID, /* bounds= */ null,
- /* value= */ null, /* flags= */ FLAG_PCC_DETECTION);
+ /* value= */ null, /* flags= */ FLAG_PCC_DETECTION);
} else {
if (sVerbose) {
Log.v(TAG, "not starting session: no service client");
}
}
-
}
}
}
- if (mIsFillDialogEnabled
- || ArrayUtils.containsAny(autofillHints, mFillDialogEnabledHints)) {
+ // Check if framework should send pre-fill request for fill dialog
+ boolean shouldSendPreFillRequestForFillDialog = false;
+ if (mIsFillDialogEnabled) {
+ shouldSendPreFillRequestForFillDialog = true;
+ } else if (autofillHints != null) {
+ // check if supported autofill hint is present
+ for (String autofillHint : autofillHints) {
+ for (String filldialogEnabledHint : mFillDialogEnabledHints) {
+ if (filldialogEnabledHint.equalsIgnoreCase(autofillHint)) {
+ shouldSendPreFillRequestForFillDialog = true;
+ break;
+ }
+ }
+ if (shouldSendPreFillRequestForFillDialog) break;
+ }
+ }
+ if (shouldSendPreFillRequestForFillDialog) {
if (sDebug) {
Log.d(TAG, "Triggering pre-emptive request for fill dialog.");
}
-
int flags = FLAG_SUPPORTS_FILL_DIALOG;
flags |= FLAG_VIEW_NOT_FOCUSED;
-
synchronized (mLock) {
// To match the id of the IME served view, used AutofillId.NO_AUTOFILL_ID on prefill
// request, because IME will reset the id of IME served view to 0 when activity
@@ -1532,9 +1544,10 @@
// not match the IME served view's, Autofill will be blocking to wait inline
// request from the IME.
notifyViewEnteredLocked(/* view= */ null, AutofillId.NO_AUTOFILL_ID,
- /* bounds= */ null, /* value= */ null, flags);
+ /* bounds= */ null, /* value= */ null, flags);
}
}
+ return;
}
private boolean hasFillDialogUiFeature() {
diff --git a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
index 3ad49af..7694754 100644
--- a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
@@ -451,7 +451,9 @@
if (mBeamer == null) {
return;
}
- mBeamer.forget(token);
+ dispatch(() -> {
+ mBeamer.forget(token);
+ });
}
@Override
diff --git a/core/res/res/layout/autofill_fill_dialog.xml b/core/res/res/layout/autofill_fill_dialog.xml
index 37d2fa0..196af6d 100644
--- a/core/res/res/layout/autofill_fill_dialog.xml
+++ b/core/res/res/layout/autofill_fill_dialog.xml
@@ -27,7 +27,7 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
- android:layout_marginTop="@dimen/autofill_save_outer_top_margin"
+ android:layout_marginTop="@dimen/autofill_save_outer_margin"
android:layout_marginBottom="24dp"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
diff --git a/core/res/res/layout/autofill_save.xml b/core/res/res/layout/autofill_save.xml
index bed19a8..8b6c901 100644
--- a/core/res/res/layout/autofill_save.xml
+++ b/core/res/res/layout/autofill_save.xml
@@ -22,59 +22,66 @@
android:background="@drawable/autofill_bottomsheet_background"
android:orientation="vertical">
- <LinearLayout
+ <com.android.server.autofill.ui.BottomSheetLayout
android:id="@+id/autofill_save"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/autofill_save_outer_top_margin"
- android:layout_marginStart="24dp"
- android:layout_marginEnd="24dp"
+ android:layout_marginTop="@dimen/autofill_save_outer_margin"
android:background="?android:attr/colorSurface"
android:gravity="center_horizontal"
android:orientation="vertical">
-
- <LinearLayout
+ <ScrollView
android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <ImageView
- android:id="@+id/autofill_save_icon"
- android:scaleType="fitCenter"
- android:layout_gravity="center"
- android:layout_height="@dimen/autofill_save_icon_max_height"
- android:layout_width="fill_parent"/>
-
- <TextView
- android:id="@+id/autofill_save_title"
+ android:layout_height="0dp"
+ android:fillViewport="true"
+ android:layout_weight="1"
+ android:layout_marginBottom="8dp">
+ <LinearLayout
+ android:layout_marginStart="@dimen/autofill_save_outer_margin"
+ android:layout_marginEnd="@dimen/autofill_save_outer_margin"
android:layout_width="fill_parent"
+ android:orientation="vertical"
android:layout_height="wrap_content"
- android:text="@string/autofill_save_title"
- android:layout_marginTop="16dp"
- android:paddingBottom="24dp"
- android:gravity="center"
- android:textAppearance="@style/AutofillSaveUiTitle">
- </TextView>
+ >
+ <ImageView
+ android:id="@+id/autofill_save_icon"
+ android:scaleType="fitCenter"
+ android:layout_gravity="center"
+ android:layout_height="@dimen/autofill_save_icon_max_height"
+ android:layout_width="fill_parent"/>
- <com.android.server.autofill.ui.CustomScrollView
- android:id="@+id/autofill_save_custom_subtitle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:visibility="gone"/>
+ <TextView
+ android:id="@+id/autofill_save_title"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/autofill_save_title"
+ android:layout_marginTop="16dp"
+ android:paddingBottom="24dp"
+ android:gravity="center"
+ android:textAppearance="@style/AutofillSaveUiTitle">
+ </TextView>
+ <LinearLayout
+ android:id="@+id/autofill_save_custom_subtitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone"/>
- </LinearLayout>
+ </LinearLayout>
+ </ScrollView>
<com.android.internal.widget.ButtonBarLayout
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_gravity="end"
android:clipToPadding="false"
- android:layout_marginTop="32dp"
- android:layout_marginBottom="18dp"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="8dp"
android:theme="@style/Theme.DeviceDefault.AutofillHalfScreenDialogButton"
android:orientation="horizontal"
- android:gravity="center_vertical">
-
+ android:gravity="center_vertical"
+ android:layout_marginStart="@dimen/autofill_save_outer_margin"
+ android:layout_marginEnd="@dimen/autofill_save_outer_margin"
+ >
<Button
android:id="@+id/autofill_save_no"
android:layout_width="wrap_content"
@@ -106,6 +113,5 @@
</com.android.internal.widget.ButtonBarLayout>
- </LinearLayout>
-
-</LinearLayout>
+ </com.android.server.autofill.ui.BottomSheetLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/core/res/res/values-sw640dp/dimens.xml b/core/res/res/values-sw640dp/dimens.xml
new file mode 100644
index 0000000..c632176
--- /dev/null
+++ b/core/res/res/values-sw640dp/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+<resources>
+ <!-- Top and side margins for autofill dialog on tablets -->
+ <dimen name="autofill_save_outer_margin">32dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/core/res/res/values-w640dp/bools.xml b/core/res/res/values-w640dp/bools.xml
new file mode 100644
index 0000000..64b20f7
--- /dev/null
+++ b/core/res/res/values-w640dp/bools.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<resources>
+ <!-- Whether or not to include horizontal space around the dialog -->
+ <bool name="autofill_dialog_horizontal_space_included">true</bool>
+</resources>
\ No newline at end of file
diff --git a/core/res/res/values-w640dp/dimens.xml b/core/res/res/values-w640dp/dimens.xml
new file mode 100644
index 0000000..1f0c0b8
--- /dev/null
+++ b/core/res/res/values-w640dp/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+<resources>
+ <!-- How much extra space should be left around the autofill dialog -->
+ <dimen name="autofill_dialog_offset">56dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml
index fe296c7..b097a61 100644
--- a/core/res/res/values/bools.xml
+++ b/core/res/res/values/bools.xml
@@ -30,4 +30,6 @@
lockscreen, setting this to true should come with customized drawables. -->
<bool name="use_lock_pattern_drawable">false</bool>
<bool name="resolver_landscape_phone">true</bool>
+ <!-- Whether or not to include horizontal space around the dialog -->
+ <bool name="autofill_dialog_horizontal_space_included">false</bool>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 24dbc5e..e77d853 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1803,6 +1803,9 @@
<array name="config_screenThresholdLevels">
</array>
+ <!-- Allow normal brightness controller feature. -->
+ <bool name="config_allowNormalBrightnessControllerFeature">false</bool>
+
<!-- Array of hysteresis constraint values for brightening, represented as tenths of a
percent. The length of this array is assumed to be one greater than
config_screenThresholdLevels. The brightening threshold is calculated as
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index b129321..190b7a6 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -871,7 +871,7 @@
<dimen name="autofill_dataset_picker_max_height">90%</dimen>
<!-- Autofill save dialog padding -->
- <dimen name="autofill_save_outer_top_margin">24dp</dimen>
+ <dimen name="autofill_save_outer_margin">24dp</dimen>
<dimen name="autofill_save_outer_top_padding">16dp</dimen>
<dimen name="autofill_elevation">32dp</dimen>
<dimen name="autofill_save_inner_padding">16dp</dimen>
@@ -882,6 +882,9 @@
<dimen name="autofill_save_button_bar_padding">16dp</dimen>
<dimen name="autofill_dialog_corner_radius">24dp</dimen>
+ <!-- How much extra space should be left around the autofill dialog -->
+ <dimen name="autofill_dialog_offset">72dp</dimen>
+
<!-- Max height of the the autofill save custom subtitle as a fraction of the screen width/height -->
<dimen name="autofill_save_custom_subtitle_max_height">20%</dimen>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index be43a4f..bee5799 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1948,6 +1948,7 @@
<java-symbol type="bool" name="config_usbChargingMessage" />
<java-symbol type="bool" name="config_skipScreenOnBrightnessRamp" />
<java-symbol type="bool" name="config_allowAutoBrightnessWhileDozing" />
+ <java-symbol type="bool" name="config_allowNormalBrightnessControllerFeature" />
<java-symbol type="bool" name="config_allowTheaterModeWakeFromUnplug" />
<java-symbol type="bool" name="config_allowTheaterModeWakeFromGesture" />
<java-symbol type="bool" name="config_allowTheaterModeWakeFromCameraLens" />
@@ -3629,6 +3630,7 @@
<java-symbol type="layout" name="autofill_dataset_picker_fullscreen"/>
<java-symbol type="layout" name="autofill_dataset_picker_header_footer"/>
<java-symbol type="layout" name="autofill_fill_dialog"/>
+ <java-symbol type="id" name="autofill_save_icon"/>
<java-symbol type="id" name="autofill" />
<java-symbol type="id" name="autofill_dataset_footer"/>
<java-symbol type="id" name="autofill_dataset_header"/>
@@ -3685,6 +3687,10 @@
<java-symbol type="dimen" name="autofill_save_custom_subtitle_max_height"/>
<java-symbol type="integer" name="autofill_max_visible_datasets" />
<java-symbol type="dimen" name="autofill_dialog_max_width" />
+ <java-symbol type="dimen" name="autofill_dialog_offset"/>
+ <java-symbol type="dimen" name="autofill_save_outer_margin"/>
+
+ <java-symbol type="bool" name="autofill_dialog_horizontal_space_included"/>
<java-symbol type="style" name="Theme.DeviceDefault.Autofill" />
<java-symbol type="style" name="Theme.DeviceDefault.Light.Autofill" />
diff --git a/core/tests/GameManagerTests/src/android/app/GameManagerTests.java b/core/tests/GameManagerTests/src/android/app/GameManagerTests.java
index fac3a0e..d34c91e 100644
--- a/core/tests/GameManagerTests/src/android/app/GameManagerTests.java
+++ b/core/tests/GameManagerTests/src/android/app/GameManagerTests.java
@@ -86,16 +86,6 @@
GameModeInfo gameModeInfo = mGameManager.getGameModeInfo(mPackageName);
assertNotNull(gameModeInfo);
assertNull(gameModeInfo.getGameModeConfiguration(GameManager.GAME_MODE_CUSTOM));
- GameModeConfiguration unsupportedFpsConfig =
- new GameModeConfiguration.Builder().setFpsOverride(
- 70).setScalingFactor(0.5f).build();
- mGameManager.updateCustomGameModeConfiguration(mPackageName, unsupportedFpsConfig);
- gameModeInfo = mGameManager.getGameModeInfo(mPackageName);
- assertNotNull(gameModeInfo);
- // TODO(b/243448953): update to non-zero FPS when matching is implemented
- assertEquals(new GameModeConfiguration.Builder().setFpsOverride(
- GameModeConfiguration.FPS_OVERRIDE_NONE).setScalingFactor(0.5f).build(),
- gameModeInfo.getGameModeConfiguration(GameManager.GAME_MODE_CUSTOM));
GameModeConfiguration supportedFpsConfig =
new GameModeConfiguration.Builder().setFpsOverride(
diff --git a/core/tests/coretests/src/android/content/BroadcastReceiverTests.java b/core/tests/coretests/src/android/content/BroadcastReceiverTests.java
index 5dbeac2..407c6c3 100644
--- a/core/tests/coretests/src/android/content/BroadcastReceiverTests.java
+++ b/core/tests/coretests/src/android/content/BroadcastReceiverTests.java
@@ -26,6 +26,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+import java.util.List;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
public class BroadcastReceiverTests {
@@ -47,15 +50,22 @@
@Test
public void testReceiverLimit() {
final IntentFilter mockFilter = new IntentFilter("android.content.tests.TestAction");
+ final List<EmptyReceiver> receivers = new ArrayList<>(RECEIVER_LIMIT_PER_APP);
try {
for (int i = 0; i < RECEIVER_LIMIT_PER_APP + 1; i++) {
- mContext.registerReceiver(new EmptyReceiver(), mockFilter,
+ final EmptyReceiver receiver = new EmptyReceiver();
+ mContext.registerReceiver(receiver, mockFilter,
Context.RECEIVER_EXPORTED_UNAUDITED);
+ receivers.add(receiver);
}
fail("No exception thrown when registering "
+ (RECEIVER_LIMIT_PER_APP + 1) + " receivers");
} catch (IllegalStateException ise) {
// Expected
+ } finally {
+ for (int i = receivers.size() - 1; i >= 0; i--) {
+ mContext.unregisterReceiver(receivers.remove(i));
+ }
}
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryInputSuspendTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryInputSuspendTest.java
index e870d60..8d825e4 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryInputSuspendTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryInputSuspendTest.java
@@ -99,6 +99,7 @@
if (isCharging(intent) == mExpectedChargingState) {
mReady.open();
}
+ context.unregisterReceiver(this);
}
}, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 7d5c06c..13cf82e 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -2047,12 +2047,6 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS_MIN",
"at": "com\/android\/server\/wm\/TransitionController.java"
},
- "-262984451": {
- "message": "Relaunch failed %s",
- "level": "INFO",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"-251259736": {
"message": "No longer freezing: %s",
"level": "VERBOSE",
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
index 55eabb0..9da6c10 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
@@ -19,7 +19,6 @@
import android.app.ActivityThread;
import android.app.Application;
import android.content.Context;
-import android.window.TaskFragmentOrganizer;
import androidx.annotation.NonNull;
import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
@@ -81,13 +80,7 @@
Context context = getApplication();
DeviceStateManagerFoldingFeatureProducer producer =
getFoldingFeatureProducer();
- // TODO(b/263263909) Use the organizer to tell if an Activity is embededed.
- // Need to improve our Dependency Injection and centralize the logic.
- TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(command -> {
- throw new RuntimeException("Not allowed!");
- });
- mWindowLayoutComponent = new WindowLayoutComponentImpl(context, organizer,
- producer);
+ mWindowLayoutComponent = new WindowLayoutComponentImpl(context, producer);
}
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
index 6db2ae4..a1fe7f7 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
@@ -24,7 +24,7 @@
import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect;
import android.app.Activity;
-import android.app.ActivityClient;
+import android.app.ActivityThread;
import android.app.Application;
import android.app.WindowConfiguration;
import android.content.ComponentCallbacks;
@@ -34,8 +34,7 @@
import android.os.Bundle;
import android.os.IBinder;
import android.util.ArrayMap;
-import android.view.WindowManager;
-import android.window.TaskFragmentOrganizer;
+import android.util.Log;
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
@@ -51,7 +50,6 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.Set;
/**
@@ -63,7 +61,7 @@
* Please refer to {@link androidx.window.sidecar.SampleSidecarImpl} instead.
*/
public class WindowLayoutComponentImpl implements WindowLayoutComponent {
- private static final String TAG = "SampleExtension";
+ private static final String TAG = WindowLayoutComponentImpl.class.getSimpleName();
private final Object mLock = new Object();
@@ -85,16 +83,15 @@
private final Map<java.util.function.Consumer<WindowLayoutInfo>, Consumer<WindowLayoutInfo>>
mJavaToExtConsumers = new ArrayMap<>();
- private final TaskFragmentOrganizer mTaskFragmentOrganizer;
+ private final RawConfigurationChangedListener mRawConfigurationChangedListener =
+ new RawConfigurationChangedListener();
public WindowLayoutComponentImpl(@NonNull Context context,
- @NonNull TaskFragmentOrganizer taskFragmentOrganizer,
@NonNull DeviceStateManagerFoldingFeatureProducer foldingFeatureProducer) {
((Application) context.getApplicationContext())
.registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged());
mFoldingFeatureProducer = foldingFeatureProducer;
mFoldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
- mTaskFragmentOrganizer = taskFragmentOrganizer;
}
/** Registers to listen to {@link CommonFoldingFeature} changes */
@@ -117,6 +114,7 @@
final Consumer<WindowLayoutInfo> extConsumer = consumer::accept;
synchronized (mLock) {
mJavaToExtConsumers.put(consumer, extConsumer);
+ updateListenerRegistrations();
}
addWindowLayoutInfoListener(activity, extConsumer);
}
@@ -170,6 +168,7 @@
final Consumer<WindowLayoutInfo> extConsumer;
synchronized (mLock) {
extConsumer = mJavaToExtConsumers.remove(consumer);
+ updateListenerRegistrations();
}
if (extConsumer != null) {
removeWindowLayoutInfoListener(extConsumer);
@@ -200,6 +199,17 @@
}
@GuardedBy("mLock")
+ private void updateListenerRegistrations() {
+ ActivityThread currentThread = ActivityThread.currentActivityThread();
+ if (mJavaToExtConsumers.isEmpty()) {
+ currentThread.removeConfigurationChangedListener(mRawConfigurationChangedListener);
+ } else {
+ currentThread.addConfigurationChangedListener(Runnable::run,
+ mRawConfigurationChangedListener);
+ }
+ }
+
+ @GuardedBy("mLock")
@NonNull
private Set<Context> getContextsListeningForLayoutChanges() {
return mWindowLayoutChangeListeners.keySet();
@@ -344,25 +354,28 @@
continue;
}
if (featureRect.left != 0 && featureRect.top != 0) {
- throw new IllegalArgumentException("Bounding rectangle must start at the top or "
+ Log.wtf(TAG, "Bounding rectangle must start at the top or "
+ "left of the window. BaseFeatureRect: " + baseFeature.getRect()
+ ", FeatureRect: " + featureRect
+ ", WindowConfiguration: " + windowConfiguration);
+ continue;
}
if (featureRect.left == 0
&& featureRect.width() != windowConfiguration.getBounds().width()) {
- throw new IllegalArgumentException("Horizontal FoldingFeature must have full width."
+ Log.wtf(TAG, "Horizontal FoldingFeature must have full width."
+ " BaseFeatureRect: " + baseFeature.getRect()
+ ", FeatureRect: " + featureRect
+ ", WindowConfiguration: " + windowConfiguration);
+ continue;
}
if (featureRect.top == 0
&& featureRect.height() != windowConfiguration.getBounds().height()) {
- throw new IllegalArgumentException("Vertical FoldingFeature must have full height."
+ Log.wtf(TAG, "Vertical FoldingFeature must have full height."
+ " BaseFeatureRect: " + baseFeature.getRect()
+ ", FeatureRect: " + featureRect
+ ", WindowConfiguration: " + windowConfiguration);
+ continue;
}
features.add(new FoldingFeature(featureRect, baseFeature.getType(), state));
}
@@ -382,38 +395,11 @@
// Display features are not supported on secondary displays.
return false;
}
- final int windowingMode;
- IBinder activityToken = context.getActivityToken();
- if (activityToken != null) {
- final Configuration taskConfig = ActivityClient.getInstance().getTaskConfiguration(
- activityToken);
- if (taskConfig == null) {
- // If we cannot determine the task configuration for any reason, it is likely that
- // we won't be able to determine its position correctly as well. DisplayFeatures'
- // bounds in this case can't be computed correctly, so we should skip.
- return false;
- }
- final Rect taskBounds = taskConfig.windowConfiguration.getBounds();
- final WindowManager windowManager = Objects.requireNonNull(
- context.getSystemService(WindowManager.class));
- final Rect maxBounds = windowManager.getMaximumWindowMetrics().getBounds();
- boolean isTaskExpanded = maxBounds.equals(taskBounds);
- /*
- * We need to proxy being in full screen because when a user enters PiP and exits PiP
- * the task windowingMode will report multi-window/pinned until the transition is
- * finished in WM Shell.
- * maxBounds == taskWindowBounds is a proxy check to verify the window is full screen
- */
- return isTaskExpanded;
- } else {
- // TODO(b/242674941): use task windowing mode for window context that associates with
- // activity.
- windowingMode = context.getResources().getConfiguration().windowConfiguration
- .getWindowingMode();
- }
- // It is recommended not to report any display features in multi-window mode, since it
- // won't be possible to synchronize the display feature positions with window movement.
- return !WindowConfiguration.inMultiWindowMode(windowingMode);
+
+ // We do not report folding features for Activities in PiP because the bounds are
+ // not updated fast enough and the window is too small for the UI to adapt.
+ return context.getResources().getConfiguration().windowConfiguration
+ .getWindowingMode() != WindowConfiguration.WINDOWING_MODE_PINNED;
}
@GuardedBy("mLock")
@@ -442,6 +428,16 @@
}
}
+ private final class RawConfigurationChangedListener implements
+ java.util.function.Consumer<IBinder> {
+ @Override
+ public void accept(IBinder activityToken) {
+ synchronized (mLock) {
+ onDisplayFeaturesChangedIfListening(activityToken);
+ }
+ }
+ }
+
private final class ConfigurationChangeListener implements ComponentCallbacks {
final IBinder mToken;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index c111ce6..953efa7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -22,10 +22,13 @@
import android.annotation.Nullable;
import android.app.TaskInfo;
import android.app.TaskInfo.CameraCompatControlState;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.hardware.display.DisplayManager;
+import android.net.Uri;
+import android.os.UserHandle;
import android.provider.Settings;
import android.util.ArraySet;
import android.util.Log;
@@ -173,6 +176,18 @@
// be shown.
private boolean mKeyguardShowing;
+ /**
+ * The id of the task for the application we're currently attempting to show the user aspect
+ * ratio settings button for, or have most recently shown the button for.
+ */
+ private int mTopActivityTaskId;
+
+ /**
+ * Whether the user aspect ratio settings button has been shown for the current application
+ * associated with the task id stored in {@link CompatUIController#mTopActivityTaskId}.
+ */
+ private boolean mHasShownUserAspectRatioSettingsButton = false;
+
public CompatUIController(@NonNull Context context,
@NonNull ShellInit shellInit,
@NonNull ShellController shellController,
@@ -227,6 +242,11 @@
if (taskInfo != null && !taskInfo.topActivityInSizeCompat) {
mSetOfTaskIdsShowingRestartDialog.remove(taskInfo.taskId);
}
+
+ if (taskInfo != null && taskListener != null) {
+ updateActiveTaskInfo(taskInfo);
+ }
+
if (taskInfo.configuration == null || taskListener == null) {
// Null token means the current foreground activity is not in compatibility mode.
removeLayouts(taskInfo.taskId);
@@ -319,6 +339,46 @@
forAllLayouts(layout -> layout.updateVisibility(showOnDisplay(layout.getDisplayId())));
}
+ /**
+ * Invoked when a new task is created or the info of an existing task has changed. Updates the
+ * shown status of the user aspect ratio settings button and the task id it relates to.
+ */
+ void updateActiveTaskInfo(@NonNull TaskInfo taskInfo) {
+ // If the activity belongs to the task we are currently tracking, don't update any variables
+ // as they are still relevant. Else, if the activity is visible and focused (the one the
+ // user can see and is using), the user aspect ratio button can potentially be displayed so
+ // start tracking the buttons visibility for this task.
+ if (mTopActivityTaskId != taskInfo.taskId && !taskInfo.isTopActivityTransparent
+ && taskInfo.isVisible && taskInfo.isFocused) {
+ mTopActivityTaskId = taskInfo.taskId;
+ setHasShownUserAspectRatioSettingsButton(false);
+ }
+ }
+
+ /**
+ * Informs the system that the user aspect ratio button has been displayed for the application
+ * associated with the task id in {@link CompatUIController#mTopActivityTaskId}.
+ */
+ void setHasShownUserAspectRatioSettingsButton(boolean state) {
+ mHasShownUserAspectRatioSettingsButton = state;
+ }
+
+ /**
+ * Returns whether the user aspect ratio settings button has been show for the application
+ * associated with the task id in {@link CompatUIController#mTopActivityTaskId}.
+ */
+ boolean hasShownUserAspectRatioSettingsButton() {
+ return mHasShownUserAspectRatioSettingsButton;
+ }
+
+ /**
+ * Returns the task id of the application we are currently attempting to show, of have most
+ * recently shown, the user aspect ratio settings button for.
+ */
+ int getTopActivityTaskId() {
+ return mTopActivityTaskId;
+ }
+
private boolean showOnDisplay(int displayId) {
return !mKeyguardShowing && !isImeShowingOnDisplay(displayId);
}
@@ -569,7 +629,8 @@
return new UserAspectRatioSettingsWindowManager(context, taskInfo, mSyncQueue,
taskListener, mDisplayController.getDisplayLayout(taskInfo.displayId),
mCompatUIHintsState, this::launchUserAspectRatioSettings, mMainExecutor,
- mDisappearTimeSupplier);
+ mDisappearTimeSupplier, this::hasShownUserAspectRatioSettingsButton,
+ this::setHasShownUserAspectRatioSettingsButton);
}
private void launchUserAspectRatioSettings(
@@ -577,7 +638,13 @@
final Intent intent = new Intent(Settings.ACTION_MANAGE_USER_ASPECT_RATIO_SETTINGS);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
- mContext.startActivity(intent);
+ final ComponentName appComponent = taskInfo.topActivity;
+ if (appComponent != null) {
+ final Uri packageUri = Uri.parse("package:" + appComponent.getPackageName());
+ intent.setData(packageUri);
+ }
+ final UserHandle userHandle = UserHandle.of(taskInfo.userId);
+ mContext.startActivityAsUser(intent, userHandle);
}
private void removeLayouts(int taskId) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
index 77aefc8..c2dec62 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
@@ -37,7 +37,9 @@
import com.android.wm.shell.compatui.CompatUIController.CompatUIHintsState;
import java.util.function.BiConsumer;
+import java.util.function.Consumer;
import java.util.function.Function;
+import java.util.function.Supplier;
/**
* Window manager for the user aspect ratio settings button which allows users to go to
@@ -55,6 +57,12 @@
private final ShellExecutor mShellExecutor;
+ @NonNull
+ private final Supplier<Boolean> mUserAspectRatioButtonShownChecker;
+
+ @NonNull
+ private final Consumer<Boolean> mUserAspectRatioButtonStateConsumer;
+
@VisibleForTesting
@NonNull
final CompatUIHintsState mCompatUIHintsState;
@@ -72,9 +80,13 @@
@NonNull DisplayLayout displayLayout, @NonNull CompatUIHintsState compatUIHintsState,
@NonNull BiConsumer<TaskInfo, ShellTaskOrganizer.TaskListener> onButtonClicked,
@NonNull ShellExecutor shellExecutor,
- @NonNull Function<Integer, Integer> disappearTimeSupplier) {
+ @NonNull Function<Integer, Integer> disappearTimeSupplier,
+ @NonNull Supplier<Boolean> userAspectRatioButtonStateChecker,
+ @NonNull Consumer<Boolean> userAspectRatioButtonShownConsumer) {
super(context, taskInfo, syncQueue, taskListener, displayLayout);
mShellExecutor = shellExecutor;
+ mUserAspectRatioButtonShownChecker = userAspectRatioButtonStateChecker;
+ mUserAspectRatioButtonStateConsumer = userAspectRatioButtonShownConsumer;
mHasUserAspectRatioSettingsButton = getHasUserAspectRatioSettingsButton(taskInfo);
mCompatUIHintsState = compatUIHintsState;
mOnButtonClicked = onButtonClicked;
@@ -180,11 +192,18 @@
}
}
+ @VisibleForTesting
+ boolean isShowingButton() {
+ return (mUserAspectRatioButtonShownChecker.get()
+ && !isHideDelayReached(mNextButtonHideTimeMs));
+ }
+
private void showUserAspectRatioButton() {
if (mLayout == null) {
return;
}
mLayout.setUserAspectRatioButtonVisibility(true);
+ mUserAspectRatioButtonStateConsumer.accept(true);
// Only show by default for the first time.
if (!mCompatUIHintsState.mHasShownUserAspectRatioSettingsButtonHint) {
mLayout.setUserAspectRatioSettingsHintVisibility(/* show= */ true);
@@ -210,7 +229,8 @@
private boolean getHasUserAspectRatioSettingsButton(@NonNull TaskInfo taskInfo) {
return taskInfo.topActivityEligibleForUserAspectRatioButton
&& (taskInfo.topActivityBoundsLetterboxed
- || taskInfo.isUserFullscreenOverrideEnabled);
+ || taskInfo.isUserFullscreenOverrideEnabled)
+ && (!mUserAspectRatioButtonShownChecker.get() || isShowingButton());
}
private long getDisappearTimeMs() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
index 13c0ac4..b71c48e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
@@ -76,6 +76,10 @@
private IRemoteTransition mOccludeByDreamTransition = null;
private IRemoteTransition mUnoccludeTransition = null;
+ // While set true, Keyguard has created a remote animation runner to handle the open app
+ // transition.
+ private boolean mIsLaunchingActivityOverLockscreen;
+
private final class StartedTransition {
final TransitionInfo mInfo;
final SurfaceControl.Transaction mFinishT;
@@ -120,7 +124,7 @@
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull TransitionFinishCallback finishCallback) {
- if (!handles(info)) {
+ if (!handles(info) || mIsLaunchingActivityOverLockscreen) {
return false;
}
@@ -313,5 +317,11 @@
mUnoccludeTransition = unoccludeTransition;
});
}
+
+ @Override
+ public void setLaunchingActivityOverLockscreen(boolean isLaunchingActivityOverLockscreen) {
+ mMainExecutor.execute(() ->
+ mIsLaunchingActivityOverLockscreen = isLaunchingActivityOverLockscreen);
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitions.java
index b4b327f..33c299f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitions.java
@@ -38,4 +38,9 @@
@NonNull IRemoteTransition occludeTransition,
@NonNull IRemoteTransition occludeByDreamTransition,
@NonNull IRemoteTransition unoccludeTransition) {}
+
+ /**
+ * Notify whether keyguard has created a remote animation runner for next app launch.
+ */
+ default void setLaunchingActivityOverLockscreen(boolean isLaunchingActivityOverLockscreen) {}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 94fa485..8efdfd6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -1551,6 +1551,11 @@
@Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition,
boolean resizeAnim) {
onSplitScreenEnter();
+ // Preemptively reset the reparenting behavior if we know that we are entering, as starting
+ // split tasks with activity trampolines can inadvertently trigger the task to be
+ // reparented out of the split root mid-launch
+ wct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token,
+ false /* setReparentLeafTaskIfRelaunch */);
if (isSplitActive()) {
prepareBringSplit(wct, taskInfo, startPosition, resizeAnim);
} else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 986560b..32cc6f8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -113,6 +113,14 @@
WindowContainerTransaction mFinishWCT = null;
/**
+ * Whether the transition has request for remote transition while mLeftoversHandler
+ * isn't remote transition handler.
+ * If true and the mLeftoversHandler can handle the transition, need to notify remote
+ * transition handler to consume the transition.
+ */
+ boolean mHasRequestToRemote;
+
+ /**
* Mixed transitions are made up of multiple "parts". This keeps track of how many
* parts are currently animating.
*/
@@ -204,6 +212,10 @@
MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE, transition);
mixed.mLeftoversHandler = handler.first;
mActiveTransitions.add(mixed);
+ if (mixed.mLeftoversHandler != mPlayer.getRemoteTransitionHandler()) {
+ mixed.mHasRequestToRemote = true;
+ mPlayer.getRemoteTransitionHandler().handleRequest(transition, request);
+ }
return handler.second;
} else if (mSplitHandler.isSplitScreenVisible()
&& isOpeningType(request.getType())
@@ -315,12 +327,22 @@
// the time of handleRequest, but we need more information than is available at that time.
if (KeyguardTransitionHandler.handles(info)) {
if (mixed != null && mixed.mType != MixedTransition.TYPE_KEYGUARD) {
- ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
- "Converting mixed transition into a keyguard transition");
- onTransitionConsumed(transition, false, null);
+ final MixedTransition keyguardMixed =
+ new MixedTransition(MixedTransition.TYPE_KEYGUARD, transition);
+ mActiveTransitions.add(keyguardMixed);
+ final boolean hasAnimateKeyguard = animateKeyguard(keyguardMixed, info,
+ startTransaction, finishTransaction, finishCallback);
+ if (hasAnimateKeyguard) {
+ ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ "Converting mixed transition into a keyguard transition");
+ // Consume the original mixed transition
+ onTransitionConsumed(transition, false, null);
+ return true;
+ } else {
+ // Keyguard handler cannot handle it, process through original mixed
+ mActiveTransitions.remove(keyguardMixed);
+ }
}
- mixed = new MixedTransition(MixedTransition.TYPE_KEYGUARD, transition);
- mActiveTransitions.add(mixed);
}
if (mixed == null) return false;
@@ -331,8 +353,17 @@
} else if (mixed.mType == MixedTransition.TYPE_DISPLAY_AND_SPLIT_CHANGE) {
return false;
} else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) {
- return animateOpenIntentWithRemoteAndPip(mixed, info, startTransaction,
- finishTransaction, finishCallback);
+ final boolean handledToPip = animateOpenIntentWithRemoteAndPip(mixed, info,
+ startTransaction, finishTransaction, finishCallback);
+ // Consume the transition on remote handler if the leftover handler already handle this
+ // transition. And if it cannot, the transition will be handled by remote handler, so
+ // don't consume here.
+ // Need to check leftOverHandler as it may change in #animateOpenIntentWithRemoteAndPip
+ if (handledToPip && mixed.mHasRequestToRemote
+ && mixed.mLeftoversHandler != mPlayer.getRemoteTransitionHandler()) {
+ mPlayer.getRemoteTransitionHandler().onTransitionConsumed(transition, false, null);
+ }
+ return handledToPip;
} else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) {
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
@@ -804,5 +835,8 @@
} else if (mixed.mType == MixedTransition.TYPE_UNFOLD) {
mUnfoldHandler.onTransitionConsumed(transition, aborted, finishT);
}
+ if (mixed.mHasRequestToRemote) {
+ mPlayer.getRemoteTransitionHandler().onTransitionConsumed(transition, aborted, finishT);
+ }
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index 9b9600e..f85d707 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -61,6 +61,7 @@
import dagger.Lazy;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -523,13 +524,175 @@
.createLayout(anyBoolean());
}
+ @Test
+ public void testUpdateActiveTaskInfo_newTask_visibleAndFocused_updated() {
+ // Simulate user aspect ratio button being shown for previous task
+ mController.setHasShownUserAspectRatioSettingsButton(true);
+ Assert.assertTrue(mController.hasShownUserAspectRatioSettingsButton());
+
+ // Create new task
+ final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID,
+ /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN, /* isVisible */ true,
+ /* isFocused */ true);
+
+ // Simulate new task being shown
+ mController.updateActiveTaskInfo(taskInfo);
+
+ // Check topActivityTaskId is updated to the taskId of the new task and
+ // hasShownUserAspectRatioSettingsButton has been reset to false
+ Assert.assertEquals(TASK_ID, mController.getTopActivityTaskId());
+ Assert.assertFalse(mController.hasShownUserAspectRatioSettingsButton());
+ }
+
+ @Test
+ public void testUpdateActiveTaskInfo_newTask_notVisibleOrFocused_notUpdated() {
+ // Create new task
+ final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID,
+ /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN, /* isVisible */ true,
+ /* isFocused */ true);
+
+ // Simulate task being shown
+ mController.updateActiveTaskInfo(taskInfo);
+
+ // Check topActivityTaskId is updated to the taskId of the new task and
+ // hasShownUserAspectRatioSettingsButton has been reset to false
+ Assert.assertEquals(TASK_ID, mController.getTopActivityTaskId());
+ Assert.assertFalse(mController.hasShownUserAspectRatioSettingsButton());
+
+ // Simulate user aspect ratio button being shown
+ mController.setHasShownUserAspectRatioSettingsButton(true);
+ Assert.assertTrue(mController.hasShownUserAspectRatioSettingsButton());
+
+ final int newTaskId = TASK_ID + 1;
+
+ // Create visible but NOT focused task
+ final TaskInfo taskInfo1 = createTaskInfo(DISPLAY_ID, newTaskId,
+ /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN, /* isVisible */ true,
+ /* isFocused */ false);
+
+ // Simulate new task being shown
+ mController.updateActiveTaskInfo(taskInfo1);
+
+ // Check topActivityTaskId is NOT updated and hasShownUserAspectRatioSettingsButton
+ // remains true
+ Assert.assertEquals(TASK_ID, mController.getTopActivityTaskId());
+ Assert.assertTrue(mController.hasShownUserAspectRatioSettingsButton());
+
+ // Create focused but NOT visible task
+ final TaskInfo taskInfo2 = createTaskInfo(DISPLAY_ID, newTaskId,
+ /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN, /* isVisible */ false,
+ /* isFocused */ true);
+
+ // Simulate new task being shown
+ mController.updateActiveTaskInfo(taskInfo2);
+
+ // Check topActivityTaskId is NOT updated and hasShownUserAspectRatioSettingsButton
+ // remains true
+ Assert.assertEquals(TASK_ID, mController.getTopActivityTaskId());
+ Assert.assertTrue(mController.hasShownUserAspectRatioSettingsButton());
+
+ // Create NOT focused but NOT visible task
+ final TaskInfo taskInfo3 = createTaskInfo(DISPLAY_ID, newTaskId,
+ /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN, /* isVisible */ false,
+ /* isFocused */ false);
+
+ // Simulate new task being shown
+ mController.updateActiveTaskInfo(taskInfo3);
+
+ // Check topActivityTaskId is NOT updated and hasShownUserAspectRatioSettingsButton
+ // remains true
+ Assert.assertEquals(TASK_ID, mController.getTopActivityTaskId());
+ Assert.assertTrue(mController.hasShownUserAspectRatioSettingsButton());
+ }
+
+ @Test
+ public void testUpdateActiveTaskInfo_sameTask_notUpdated() {
+ // Create new task
+ final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID,
+ /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN, /* isVisible */ true,
+ /* isFocused */ true);
+
+ // Simulate new task being shown
+ mController.updateActiveTaskInfo(taskInfo);
+
+ // Check topActivityTaskId is updated to the taskId of the new task and
+ // hasShownUserAspectRatioSettingsButton has been reset to false
+ Assert.assertEquals(TASK_ID, mController.getTopActivityTaskId());
+ Assert.assertFalse(mController.hasShownUserAspectRatioSettingsButton());
+
+ // Simulate user aspect ratio button being shown
+ mController.setHasShownUserAspectRatioSettingsButton(true);
+ Assert.assertTrue(mController.hasShownUserAspectRatioSettingsButton());
+
+ // Simulate same task being re-shown
+ mController.updateActiveTaskInfo(taskInfo);
+
+ // Check topActivityTaskId is NOT updated and hasShownUserAspectRatioSettingsButton
+ // remains true
+ Assert.assertEquals(TASK_ID, mController.getTopActivityTaskId());
+ Assert.assertTrue(mController.hasShownUserAspectRatioSettingsButton());
+ }
+
+ @Test
+ public void testUpdateActiveTaskInfo_transparentTask_notUpdated() {
+ // Create new task
+ final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID,
+ /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN, /* isVisible */ true,
+ /* isFocused */ true);
+
+ // Simulate new task being shown
+ mController.updateActiveTaskInfo(taskInfo);
+
+ // Check topActivityTaskId is updated to the taskId of the new task and
+ // hasShownUserAspectRatioSettingsButton has been reset to false
+ Assert.assertEquals(TASK_ID, mController.getTopActivityTaskId());
+ Assert.assertFalse(mController.hasShownUserAspectRatioSettingsButton());
+
+ // Simulate user aspect ratio button being shown
+ mController.setHasShownUserAspectRatioSettingsButton(true);
+ Assert.assertTrue(mController.hasShownUserAspectRatioSettingsButton());
+
+ final int newTaskId = TASK_ID + 1;
+
+ // Create transparent task
+ final TaskInfo taskInfo1 = createTaskInfo(DISPLAY_ID, newTaskId,
+ /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN, /* isVisible */ true,
+ /* isFocused */ true, /* isTopActivityTransparent */ true);
+
+ // Simulate new task being shown
+ mController.updateActiveTaskInfo(taskInfo1);
+
+ // Check topActivityTaskId is NOT updated and hasShownUserAspectRatioSettingsButton
+ // remains true
+ Assert.assertEquals(TASK_ID, mController.getTopActivityTaskId());
+ Assert.assertTrue(mController.hasShownUserAspectRatioSettingsButton());
+ }
+
private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat,
@CameraCompatControlState int cameraCompatControlState) {
+ return createTaskInfo(displayId, taskId, hasSizeCompat, cameraCompatControlState,
+ /* isVisible */ false, /* isFocused */ false,
+ /* isTopActivityTransparent */ false);
+ }
+
+ private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat,
+ @CameraCompatControlState int cameraCompatControlState, boolean isVisible,
+ boolean isFocused) {
+ return createTaskInfo(displayId, taskId, hasSizeCompat, cameraCompatControlState,
+ isVisible, isFocused, /* isTopActivityTransparent */ false);
+ }
+
+ private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat,
+ @CameraCompatControlState int cameraCompatControlState, boolean isVisible,
+ boolean isFocused, boolean isTopActivityTransparent) {
RunningTaskInfo taskInfo = new RunningTaskInfo();
taskInfo.taskId = taskId;
taskInfo.displayId = displayId;
taskInfo.topActivityInSizeCompat = hasSizeCompat;
taskInfo.cameraCompatControlState = cameraCompatControlState;
+ taskInfo.isVisible = isVisible;
+ taskInfo.isFocused = isFocused;
+ taskInfo.isTopActivityTransparent = isTopActivityTransparent;
return taskInfo;
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java
index ce1290b..f460d1b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayoutTest.java
@@ -93,7 +93,8 @@
mWindowManager = new UserAspectRatioSettingsWindowManager(mContext, mTaskInfo,
mSyncTransactionQueue, mTaskListener, new DisplayLayout(),
new CompatUIController.CompatUIHintsState(),
- mOnUserAspectRatioSettingsButtonClicked, new TestShellExecutor(), flags -> 0);
+ mOnUserAspectRatioSettingsButtonClicked, new TestShellExecutor(), flags -> 0,
+ () -> false, s -> {});
mLayout = (UserAspectRatioSettingsLayout) LayoutInflater.from(mContext).inflate(
R.layout.user_aspect_ratio_settings_layout, null);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
index 08cc2f7..5a4d6c8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
@@ -36,6 +36,7 @@
import android.content.res.Configuration;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
import android.util.Pair;
import android.view.DisplayInfo;
import android.view.InsetsSource;
@@ -66,6 +67,7 @@
import java.util.HashSet;
import java.util.Set;
import java.util.function.BiConsumer;
+import java.util.function.Supplier;
/**
* Tests for {@link UserAspectRatioSettingsWindowManager}.
@@ -74,6 +76,7 @@
* atest WMShellUnitTests:UserAspectRatioSettingsWindowManagerTest
*/
@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
@SmallTest
public class UserAspectRatioSettingsWindowManagerTest extends ShellTestCase {
@@ -81,6 +84,8 @@
@Mock private SyncTransactionQueue mSyncTransactionQueue;
@Mock
+ private Supplier<Boolean> mUserAspectRatioButtonShownChecker;
+ @Mock
private BiConsumer<TaskInfo, ShellTaskOrganizer.TaskListener>
mOnUserAspectRatioSettingsButtonClicked;
@Mock private ShellTaskOrganizer.TaskListener mTaskListener;
@@ -106,10 +111,12 @@
false, /* topActivityBoundsLetterboxed */ true);
mWindowManager = new UserAspectRatioSettingsWindowManager(mContext, mTaskInfo,
mSyncTransactionQueue, mTaskListener, new DisplayLayout(), new CompatUIHintsState(),
- mOnUserAspectRatioSettingsButtonClicked, mExecutor, flags -> 0);
+ mOnUserAspectRatioSettingsButtonClicked, mExecutor, flags -> 0,
+ mUserAspectRatioButtonShownChecker, s -> {});
spyOn(mWindowManager);
doReturn(mLayout).when(mWindowManager).inflateLayout();
doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost();
+ doReturn(false).when(mUserAspectRatioButtonShownChecker).get();
}
@Test
@@ -293,6 +300,39 @@
}
@Test
+ public void testLayoutHasUserAspectRatioSettingsButton() {
+ clearInvocations(mWindowManager);
+ spyOn(mWindowManager);
+ TaskInfo taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */
+ true, /* topActivityBoundsLetterboxed */ true);
+
+ // User aspect ratio settings button has not yet been shown.
+ doReturn(false).when(mUserAspectRatioButtonShownChecker).get();
+
+ // Check the layout has the user aspect ratio settings button.
+ mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true);
+ assertTrue(mWindowManager.mHasUserAspectRatioSettingsButton);
+
+ // User aspect ratio settings button has been shown and is still visible.
+ spyOn(mWindowManager);
+ doReturn(true).when(mWindowManager).isShowingButton();
+ doReturn(true).when(mUserAspectRatioButtonShownChecker).get();
+
+ // Check the layout still has the user aspect ratio settings button.
+ mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true);
+ assertTrue(mWindowManager.mHasUserAspectRatioSettingsButton);
+
+ // User aspect ratio settings button has been shown and has timed out so is no longer
+ // visible.
+ doReturn(false).when(mWindowManager).isShowingButton();
+ doReturn(true).when(mUserAspectRatioButtonShownChecker).get();
+
+ // Check the layout no longer has the user aspect ratio button.
+ mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true);
+ assertFalse(mWindowManager.mHasUserAspectRatioSettingsButton);
+ }
+
+ @Test
public void testAttachToParentSurface() {
final SurfaceControl.Builder b = new SurfaceControl.Builder();
mWindowManager.attachToParentSurface(b);
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index db58147..c87c156 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -717,6 +717,7 @@
"tests/unit/EglManagerTests.cpp",
"tests/unit/FatVectorTests.cpp",
"tests/unit/GraphicsStatsServiceTests.cpp",
+ "tests/unit/HintSessionWrapperTests.cpp",
"tests/unit/JankTrackerTests.cpp",
"tests/unit/FrameMetricsReporterTests.cpp",
"tests/unit/LayerUpdateQueueTests.cpp",
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index f5b3ca6..310e39e 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -123,7 +123,7 @@
, mProfiler(mJankTracker.frames(), thread.timeLord().frameIntervalNanos())
, mContentDrawBounds(0, 0, 0, 0)
, mRenderPipeline(std::move(renderPipeline))
- , mHintSessionWrapper(uiThreadId, renderThreadId) {
+ , mHintSessionWrapper(std::make_shared<HintSessionWrapper>(uiThreadId, renderThreadId)) {
mRenderThread.cacheManager().registerCanvasContext(this);
rootRenderNode->makeRoot();
mRenderNodes.emplace_back(rootRenderNode);
@@ -160,6 +160,7 @@
destroyHardwareResources();
mAnimationContext->destroy();
mRenderThread.cacheManager().onContextStopped(this);
+ mHintSessionWrapper->delayedDestroy(mRenderThread, 2_s, mHintSessionWrapper);
}
static void setBufferCount(ANativeWindow* window) {
@@ -739,7 +740,7 @@
int64_t frameDeadline = mCurrentFrameInfo->get(FrameInfoIndex::FrameDeadline);
int64_t dequeueBufferDuration = mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration);
- mHintSessionWrapper.updateTargetWorkDuration(frameDeadline - intendedVsync);
+ mHintSessionWrapper->updateTargetWorkDuration(frameDeadline - intendedVsync);
if (didDraw) {
int64_t frameStartTime = mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime);
@@ -747,7 +748,7 @@
int64_t actualDuration = frameDuration -
(std::min(syncDelayDuration, mLastDequeueBufferDuration)) -
dequeueBufferDuration - idleDuration;
- mHintSessionWrapper.reportActualWorkDuration(actualDuration);
+ mHintSessionWrapper->reportActualWorkDuration(actualDuration);
}
mLastDequeueBufferDuration = dequeueBufferDuration;
@@ -1081,11 +1082,11 @@
}
void CanvasContext::sendLoadResetHint() {
- mHintSessionWrapper.sendLoadResetHint();
+ mHintSessionWrapper->sendLoadResetHint();
}
void CanvasContext::sendLoadIncreaseHint() {
- mHintSessionWrapper.sendLoadIncreaseHint();
+ mHintSessionWrapper->sendLoadIncreaseHint();
}
void CanvasContext::setSyncDelayDuration(nsecs_t duration) {
@@ -1093,7 +1094,7 @@
}
void CanvasContext::startHintSession() {
- mHintSessionWrapper.init();
+ mHintSessionWrapper->init();
}
bool CanvasContext::shouldDither() {
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 32ac5af..10a4afb 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -359,7 +359,7 @@
std::function<bool(int64_t, int64_t, int64_t)> mASurfaceTransactionCallback;
std::function<void()> mPrepareSurfaceControlForWebviewCallback;
- HintSessionWrapper mHintSessionWrapper;
+ std::shared_ptr<HintSessionWrapper> mHintSessionWrapper;
nsecs_t mLastDequeueBufferDuration = 0;
nsecs_t mSyncDelayDuration = 0;
nsecs_t mIdleDuration = 0;
diff --git a/libs/hwui/renderthread/HintSessionWrapper.cpp b/libs/hwui/renderthread/HintSessionWrapper.cpp
index 1f338ee..d1ebe6d 100644
--- a/libs/hwui/renderthread/HintSessionWrapper.cpp
+++ b/libs/hwui/renderthread/HintSessionWrapper.cpp
@@ -24,6 +24,7 @@
#include <vector>
#include "../Properties.h"
+#include "RenderThread.h"
#include "thread/CommonPool.h"
using namespace std::chrono_literals;
@@ -32,76 +33,42 @@
namespace uirenderer {
namespace renderthread {
-namespace {
+#define BIND_APH_METHOD(name) \
+ name = (decltype(name))dlsym(handle_, "APerformanceHint_" #name); \
+ LOG_ALWAYS_FATAL_IF(name == nullptr, "Failed to find required symbol APerformanceHint_" #name)
-typedef APerformanceHintManager* (*APH_getManager)();
-typedef APerformanceHintSession* (*APH_createSession)(APerformanceHintManager*, const int32_t*,
- size_t, int64_t);
-typedef void (*APH_closeSession)(APerformanceHintSession* session);
-typedef void (*APH_updateTargetWorkDuration)(APerformanceHintSession*, int64_t);
-typedef void (*APH_reportActualWorkDuration)(APerformanceHintSession*, int64_t);
-typedef void (*APH_sendHint)(APerformanceHintSession* session, int32_t);
-
-bool gAPerformanceHintBindingInitialized = false;
-APH_getManager gAPH_getManagerFn = nullptr;
-APH_createSession gAPH_createSessionFn = nullptr;
-APH_closeSession gAPH_closeSessionFn = nullptr;
-APH_updateTargetWorkDuration gAPH_updateTargetWorkDurationFn = nullptr;
-APH_reportActualWorkDuration gAPH_reportActualWorkDurationFn = nullptr;
-APH_sendHint gAPH_sendHintFn = nullptr;
-
-void ensureAPerformanceHintBindingInitialized() {
- if (gAPerformanceHintBindingInitialized) return;
+void HintSessionWrapper::HintSessionBinding::init() {
+ if (mInitialized) return;
void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
LOG_ALWAYS_FATAL_IF(handle_ == nullptr, "Failed to dlopen libandroid.so!");
- gAPH_getManagerFn = (APH_getManager)dlsym(handle_, "APerformanceHint_getManager");
- LOG_ALWAYS_FATAL_IF(gAPH_getManagerFn == nullptr,
- "Failed to find required symbol APerformanceHint_getManager!");
+ BIND_APH_METHOD(getManager);
+ BIND_APH_METHOD(createSession);
+ BIND_APH_METHOD(closeSession);
+ BIND_APH_METHOD(updateTargetWorkDuration);
+ BIND_APH_METHOD(reportActualWorkDuration);
+ BIND_APH_METHOD(sendHint);
- gAPH_createSessionFn = (APH_createSession)dlsym(handle_, "APerformanceHint_createSession");
- LOG_ALWAYS_FATAL_IF(gAPH_createSessionFn == nullptr,
- "Failed to find required symbol APerformanceHint_createSession!");
-
- gAPH_closeSessionFn = (APH_closeSession)dlsym(handle_, "APerformanceHint_closeSession");
- LOG_ALWAYS_FATAL_IF(gAPH_closeSessionFn == nullptr,
- "Failed to find required symbol APerformanceHint_closeSession!");
-
- gAPH_updateTargetWorkDurationFn = (APH_updateTargetWorkDuration)dlsym(
- handle_, "APerformanceHint_updateTargetWorkDuration");
- LOG_ALWAYS_FATAL_IF(
- gAPH_updateTargetWorkDurationFn == nullptr,
- "Failed to find required symbol APerformanceHint_updateTargetWorkDuration!");
-
- gAPH_reportActualWorkDurationFn = (APH_reportActualWorkDuration)dlsym(
- handle_, "APerformanceHint_reportActualWorkDuration");
- LOG_ALWAYS_FATAL_IF(
- gAPH_reportActualWorkDurationFn == nullptr,
- "Failed to find required symbol APerformanceHint_reportActualWorkDuration!");
-
- gAPH_sendHintFn = (APH_sendHint)dlsym(handle_, "APerformanceHint_sendHint");
- LOG_ALWAYS_FATAL_IF(gAPH_sendHintFn == nullptr,
- "Failed to find required symbol APerformanceHint_sendHint!");
-
- gAPerformanceHintBindingInitialized = true;
+ mInitialized = true;
}
-} // namespace
-
HintSessionWrapper::HintSessionWrapper(pid_t uiThreadId, pid_t renderThreadId)
- : mUiThreadId(uiThreadId), mRenderThreadId(renderThreadId) {}
+ : mUiThreadId(uiThreadId)
+ , mRenderThreadId(renderThreadId)
+ , mBinding(std::make_shared<HintSessionBinding>()) {}
HintSessionWrapper::~HintSessionWrapper() {
destroy();
}
void HintSessionWrapper::destroy() {
- if (mHintSessionFuture.valid()) {
- mHintSession = mHintSessionFuture.get();
+ if (mHintSessionFuture.has_value()) {
+ mHintSession = mHintSessionFuture->get();
+ mHintSessionFuture = std::nullopt;
}
if (mHintSession) {
- gAPH_closeSessionFn(mHintSession);
+ mBinding->closeSession(mHintSession);
mSessionValid = true;
mHintSession = nullptr;
}
@@ -109,12 +76,12 @@
bool HintSessionWrapper::init() {
if (mHintSession != nullptr) return true;
-
// If we're waiting for the session
- if (mHintSessionFuture.valid()) {
+ if (mHintSessionFuture.has_value()) {
// If the session is here
- if (mHintSessionFuture.wait_for(0s) == std::future_status::ready) {
- mHintSession = mHintSessionFuture.get();
+ if (mHintSessionFuture->wait_for(0s) == std::future_status::ready) {
+ mHintSession = mHintSessionFuture->get();
+ mHintSessionFuture = std::nullopt;
if (mHintSession != nullptr) {
mSessionValid = true;
return true;
@@ -133,9 +100,9 @@
// Assume that if we return before the end, it broke
mSessionValid = false;
- ensureAPerformanceHintBindingInitialized();
+ mBinding->init();
- APerformanceHintManager* manager = gAPH_getManagerFn();
+ APerformanceHintManager* manager = mBinding->getManager();
if (!manager) return false;
std::vector<pid_t> tids = CommonPool::getThreadIds();
@@ -145,8 +112,9 @@
// Use a placeholder target value to initialize,
// this will always be replaced elsewhere before it gets used
int64_t defaultTargetDurationNanos = 16666667;
- mHintSessionFuture = CommonPool::async([=, tids = std::move(tids)] {
- return gAPH_createSessionFn(manager, tids.data(), tids.size(), defaultTargetDurationNanos);
+ mHintSessionFuture = CommonPool::async([=, this, tids = std::move(tids)] {
+ return mBinding->createSession(manager, tids.data(), tids.size(),
+ defaultTargetDurationNanos);
});
return false;
}
@@ -158,7 +126,7 @@
targetWorkDurationNanos > kSanityCheckLowerBound &&
targetWorkDurationNanos < kSanityCheckUpperBound) {
mLastTargetWorkDuration = targetWorkDurationNanos;
- gAPH_updateTargetWorkDurationFn(mHintSession, targetWorkDurationNanos);
+ mBinding->updateTargetWorkDuration(mHintSession, targetWorkDurationNanos);
}
mLastFrameNotification = systemTime();
}
@@ -168,8 +136,9 @@
mResetsSinceLastReport = 0;
if (actualDurationNanos > kSanityCheckLowerBound &&
actualDurationNanos < kSanityCheckUpperBound) {
- gAPH_reportActualWorkDurationFn(mHintSession, actualDurationNanos);
+ mBinding->reportActualWorkDuration(mHintSession, actualDurationNanos);
}
+ mLastFrameNotification = systemTime();
}
void HintSessionWrapper::sendLoadResetHint() {
@@ -179,14 +148,36 @@
if (now - mLastFrameNotification > kResetHintTimeout &&
mResetsSinceLastReport <= kMaxResetsSinceLastReport) {
++mResetsSinceLastReport;
- gAPH_sendHintFn(mHintSession, static_cast<int>(SessionHint::CPU_LOAD_RESET));
+ mBinding->sendHint(mHintSession, static_cast<int32_t>(SessionHint::CPU_LOAD_RESET));
}
mLastFrameNotification = now;
}
void HintSessionWrapper::sendLoadIncreaseHint() {
if (!init()) return;
- gAPH_sendHintFn(mHintSession, static_cast<int>(SessionHint::CPU_LOAD_UP));
+ mBinding->sendHint(mHintSession, static_cast<int32_t>(SessionHint::CPU_LOAD_UP));
+ mLastFrameNotification = systemTime();
+}
+
+bool HintSessionWrapper::alive() {
+ return mHintSession != nullptr;
+}
+
+nsecs_t HintSessionWrapper::getLastUpdate() {
+ return mLastFrameNotification;
+}
+
+// Requires passing in its shared_ptr since it shouldn't own a shared_ptr to itself
+void HintSessionWrapper::delayedDestroy(RenderThread& rt, nsecs_t delay,
+ std::shared_ptr<HintSessionWrapper> wrapperPtr) {
+ nsecs_t lastUpdate = wrapperPtr->getLastUpdate();
+ rt.queue().postDelayed(delay, [lastUpdate = lastUpdate, wrapper = wrapperPtr]() mutable {
+ if (wrapper->getLastUpdate() == lastUpdate) {
+ wrapper->destroy();
+ }
+ // Ensure the shared_ptr is killed at the end of the method
+ wrapper = nullptr;
+ });
}
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/HintSessionWrapper.h b/libs/hwui/renderthread/HintSessionWrapper.h
index bdb9959..36e91ea 100644
--- a/libs/hwui/renderthread/HintSessionWrapper.h
+++ b/libs/hwui/renderthread/HintSessionWrapper.h
@@ -19,6 +19,7 @@
#include <android/performance_hint.h>
#include <future>
+#include <optional>
#include "utils/TimeUtils.h"
@@ -27,8 +28,12 @@
namespace renderthread {
+class RenderThread;
+
class HintSessionWrapper {
public:
+ friend class HintSessionWrapperTests;
+
HintSessionWrapper(pid_t uiThreadId, pid_t renderThreadId);
~HintSessionWrapper();
@@ -38,10 +43,15 @@
void sendLoadIncreaseHint();
bool init();
void destroy();
+ bool alive();
+ nsecs_t getLastUpdate();
+ void delayedDestroy(renderthread::RenderThread& rt, nsecs_t delay,
+ std::shared_ptr<HintSessionWrapper> wrapperPtr);
private:
APerformanceHintSession* mHintSession = nullptr;
- std::future<APerformanceHintSession*> mHintSessionFuture;
+ // This needs to work concurrently for testing
+ std::optional<std::shared_future<APerformanceHintSession*>> mHintSessionFuture;
int mResetsSinceLastReport = 0;
nsecs_t mLastFrameNotification = 0;
@@ -55,6 +65,28 @@
static constexpr nsecs_t kResetHintTimeout = 100_ms;
static constexpr int64_t kSanityCheckLowerBound = 100_us;
static constexpr int64_t kSanityCheckUpperBound = 10_s;
+
+ // Allows easier stub when testing
+ class HintSessionBinding {
+ public:
+ virtual ~HintSessionBinding() = default;
+ virtual void init();
+ APerformanceHintManager* (*getManager)();
+ APerformanceHintSession* (*createSession)(APerformanceHintManager* manager,
+ const int32_t* tids, size_t tidCount,
+ int64_t defaultTarget) = nullptr;
+ void (*closeSession)(APerformanceHintSession* session) = nullptr;
+ void (*updateTargetWorkDuration)(APerformanceHintSession* session,
+ int64_t targetDuration) = nullptr;
+ void (*reportActualWorkDuration)(APerformanceHintSession* session,
+ int64_t actualDuration) = nullptr;
+ void (*sendHint)(APerformanceHintSession* session, int32_t hintId) = nullptr;
+
+ private:
+ bool mInitialized = false;
+ };
+
+ std::shared_ptr<HintSessionBinding> mBinding;
};
} /* namespace renderthread */
diff --git a/libs/hwui/tests/unit/HintSessionWrapperTests.cpp b/libs/hwui/tests/unit/HintSessionWrapperTests.cpp
new file mode 100644
index 0000000..5a10f4f
--- /dev/null
+++ b/libs/hwui/tests/unit/HintSessionWrapperTests.cpp
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <private/performance_hint_private.h>
+#include <renderthread/HintSessionWrapper.h>
+#include <utils/Log.h>
+
+#include <chrono>
+
+#include "Properties.h"
+#include "tests/common/TestUtils.h"
+
+using namespace testing;
+using namespace std::chrono_literals;
+using namespace android::uirenderer::renderthread;
+
+APerformanceHintManager* managerPtr = reinterpret_cast<APerformanceHintManager*>(123);
+APerformanceHintSession* sessionPtr = reinterpret_cast<APerformanceHintSession*>(456);
+int uiThreadId = 1;
+int renderThreadId = 2;
+
+namespace android::uirenderer::renderthread {
+
+class HintSessionWrapperTests : public testing::Test {
+public:
+ void SetUp() override;
+ void TearDown() override;
+
+protected:
+ std::shared_ptr<HintSessionWrapper> mWrapper;
+
+ std::promise<int> blockDestroyCallUntil;
+ std::promise<int> waitForDestroyFinished;
+
+ class MockHintSessionBinding : public HintSessionWrapper::HintSessionBinding {
+ public:
+ void init() override;
+
+ MOCK_METHOD(APerformanceHintManager*, fakeGetManager, ());
+ MOCK_METHOD(APerformanceHintSession*, fakeCreateSession,
+ (APerformanceHintManager*, const int32_t*, size_t, int64_t));
+ MOCK_METHOD(void, fakeCloseSession, (APerformanceHintSession*));
+ MOCK_METHOD(void, fakeUpdateTargetWorkDuration, (APerformanceHintSession*, int64_t));
+ MOCK_METHOD(void, fakeReportActualWorkDuration, (APerformanceHintSession*, int64_t));
+ MOCK_METHOD(void, fakeSendHint, (APerformanceHintSession*, int32_t));
+ // Needs to be on the binding so it can be accessed from static methods
+ std::promise<int> allowCreationToFinish;
+ };
+
+ // Must be static so it can have function pointers we can point to with static methods
+ static std::shared_ptr<MockHintSessionBinding> sMockBinding;
+
+ static void allowCreationToFinish() { sMockBinding->allowCreationToFinish.set_value(1); }
+ void allowDelayedDestructionToStart() { blockDestroyCallUntil.set_value(1); }
+ void waitForDelayedDestructionToFinish() { waitForDestroyFinished.get_future().wait(); }
+
+ // Must be static so we can point to them as normal fn pointers with HintSessionBinding
+ static APerformanceHintManager* stubGetManager() { return sMockBinding->fakeGetManager(); };
+ static APerformanceHintSession* stubCreateSession(APerformanceHintManager* manager,
+ const int32_t* ids, size_t idsSize,
+ int64_t initialTarget) {
+ return sMockBinding->fakeCreateSession(manager, ids, idsSize, initialTarget);
+ }
+ static APerformanceHintSession* stubManagedCreateSession(APerformanceHintManager* manager,
+ const int32_t* ids, size_t idsSize,
+ int64_t initialTarget) {
+ sMockBinding->allowCreationToFinish.get_future().wait();
+ return sMockBinding->fakeCreateSession(manager, ids, idsSize, initialTarget);
+ }
+ static APerformanceHintSession* stubSlowCreateSession(APerformanceHintManager* manager,
+ const int32_t* ids, size_t idsSize,
+ int64_t initialTarget) {
+ std::this_thread::sleep_for(50ms);
+ return sMockBinding->fakeCreateSession(manager, ids, idsSize, initialTarget);
+ }
+ static void stubCloseSession(APerformanceHintSession* session) {
+ sMockBinding->fakeCloseSession(session);
+ };
+ static void stubUpdateTargetWorkDuration(APerformanceHintSession* session,
+ int64_t workDuration) {
+ sMockBinding->fakeUpdateTargetWorkDuration(session, workDuration);
+ }
+ static void stubReportActualWorkDuration(APerformanceHintSession* session,
+ int64_t workDuration) {
+ sMockBinding->fakeReportActualWorkDuration(session, workDuration);
+ }
+ static void stubSendHint(APerformanceHintSession* session, int32_t hintId) {
+ sMockBinding->fakeSendHint(session, hintId);
+ };
+ void waitForWrapperReady() {
+ if (mWrapper->mHintSessionFuture.has_value()) {
+ mWrapper->mHintSessionFuture->wait();
+ }
+ }
+ void scheduleDelayedDestroyManaged() {
+ TestUtils::runOnRenderThread([&](renderthread::RenderThread& rt) {
+ // Guaranteed to be scheduled first, allows destruction to start
+ rt.queue().postDelayed(0_ms, [&] { blockDestroyCallUntil.get_future().wait(); });
+ // Guaranteed to be scheduled second, destroys the session
+ mWrapper->delayedDestroy(rt, 1_ms, mWrapper);
+ // This is guaranteed to be queued after the destroy, signals that destruction is done
+ rt.queue().postDelayed(1_ms, [&] { waitForDestroyFinished.set_value(1); });
+ });
+ }
+};
+
+std::shared_ptr<HintSessionWrapperTests::MockHintSessionBinding>
+ HintSessionWrapperTests::sMockBinding;
+
+void HintSessionWrapperTests::SetUp() {
+ // Pretend it's supported even if we're in an emulator
+ Properties::useHintManager = true;
+ sMockBinding = std::make_shared<NiceMock<MockHintSessionBinding>>();
+ mWrapper = std::make_shared<HintSessionWrapper>(uiThreadId, renderThreadId);
+ mWrapper->mBinding = sMockBinding;
+ EXPECT_CALL(*sMockBinding, fakeGetManager).WillOnce(Return(managerPtr));
+ ON_CALL(*sMockBinding, fakeCreateSession).WillByDefault(Return(sessionPtr));
+}
+
+void HintSessionWrapperTests::MockHintSessionBinding::init() {
+ sMockBinding->getManager = &stubGetManager;
+ if (sMockBinding->createSession == nullptr) {
+ sMockBinding->createSession = &stubCreateSession;
+ }
+ sMockBinding->closeSession = &stubCloseSession;
+ sMockBinding->updateTargetWorkDuration = &stubUpdateTargetWorkDuration;
+ sMockBinding->reportActualWorkDuration = &stubReportActualWorkDuration;
+ sMockBinding->sendHint = &stubSendHint;
+}
+
+void HintSessionWrapperTests::TearDown() {
+ // Ensure that anything running on RT is completely finished
+ mWrapper = nullptr;
+ sMockBinding = nullptr;
+}
+
+TEST_F(HintSessionWrapperTests, destructorClosesBackgroundSession) {
+ EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
+ sMockBinding->createSession = stubSlowCreateSession;
+ mWrapper->init();
+ mWrapper = nullptr;
+ Mock::VerifyAndClearExpectations(sMockBinding.get());
+}
+
+TEST_F(HintSessionWrapperTests, sessionInitializesCorrectly) {
+ EXPECT_CALL(*sMockBinding, fakeCreateSession(managerPtr, _, Gt(1), _)).Times(1);
+ mWrapper->init();
+ waitForWrapperReady();
+}
+
+TEST_F(HintSessionWrapperTests, loadUpHintsSendCorrectly) {
+ EXPECT_CALL(*sMockBinding,
+ fakeSendHint(sessionPtr, static_cast<int32_t>(SessionHint::CPU_LOAD_UP)))
+ .Times(1);
+ mWrapper->init();
+ waitForWrapperReady();
+ mWrapper->sendLoadIncreaseHint();
+}
+
+TEST_F(HintSessionWrapperTests, loadResetHintsSendCorrectly) {
+ EXPECT_CALL(*sMockBinding,
+ fakeSendHint(sessionPtr, static_cast<int32_t>(SessionHint::CPU_LOAD_RESET)))
+ .Times(1);
+ mWrapper->init();
+ waitForWrapperReady();
+ mWrapper->sendLoadResetHint();
+}
+
+TEST_F(HintSessionWrapperTests, delayedDeletionWorksCorrectlyAndOnlyClosesOnce) {
+ EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
+ mWrapper->init();
+ waitForWrapperReady();
+ // Init a second time just to ensure the wrapper grabs the promise value
+ mWrapper->init();
+
+ EXPECT_EQ(mWrapper->alive(), true);
+
+ // Schedule delayed destruction, allow it to run, and check when it's done
+ scheduleDelayedDestroyManaged();
+ allowDelayedDestructionToStart();
+ waitForDelayedDestructionToFinish();
+
+ // Ensure it closed within the timeframe of the test
+ Mock::VerifyAndClearExpectations(sMockBinding.get());
+ EXPECT_EQ(mWrapper->alive(), false);
+ // If we then delete the wrapper, it shouldn't close the session again
+ EXPECT_CALL(*sMockBinding, fakeCloseSession(_)).Times(0);
+ mWrapper = nullptr;
+}
+
+TEST_F(HintSessionWrapperTests, delayedDeletionResolvesBeforeAsyncCreationFinishes) {
+ // Here we test whether queueing delayedDestroy works while creation is still happening, if
+ // creation happens after
+ EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
+ sMockBinding->createSession = &stubManagedCreateSession;
+
+ // Start creating the session and destroying it at the same time
+ mWrapper->init();
+ scheduleDelayedDestroyManaged();
+
+ // Allow destruction to happen first
+ allowDelayedDestructionToStart();
+
+ // Make sure destruction has had time to happen
+ std::this_thread::sleep_for(50ms);
+
+ // Then, allow creation to finish after delayed destroy runs
+ allowCreationToFinish();
+
+ // Wait for destruction to finish
+ waitForDelayedDestructionToFinish();
+
+ // Ensure it closed within the timeframe of the test
+ Mock::VerifyAndClearExpectations(sMockBinding.get());
+ EXPECT_EQ(mWrapper->alive(), false);
+}
+
+TEST_F(HintSessionWrapperTests, delayedDeletionResolvesAfterAsyncCreationFinishes) {
+ // Here we test whether queueing delayedDestroy works while creation is still happening, if
+ // creation happens before
+ EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(1);
+ sMockBinding->createSession = &stubManagedCreateSession;
+
+ // Start creating the session and destroying it at the same time
+ mWrapper->init();
+ scheduleDelayedDestroyManaged();
+
+ // Allow creation to happen first
+ allowCreationToFinish();
+
+ // Make sure creation has had time to happen
+ waitForWrapperReady();
+
+ // Then allow destruction to happen after creation is done
+ allowDelayedDestructionToStart();
+
+ // Wait for it to finish
+ waitForDelayedDestructionToFinish();
+
+ // Ensure it closed within the timeframe of the test
+ Mock::VerifyAndClearExpectations(sMockBinding.get());
+ EXPECT_EQ(mWrapper->alive(), false);
+}
+
+TEST_F(HintSessionWrapperTests, delayedDeletionDoesNotKillReusedSession) {
+ EXPECT_CALL(*sMockBinding, fakeCloseSession(sessionPtr)).Times(0);
+ EXPECT_CALL(*sMockBinding,
+ fakeSendHint(sessionPtr, static_cast<int32_t>(SessionHint::CPU_LOAD_UP)))
+ .Times(1);
+
+ mWrapper->init();
+ waitForWrapperReady();
+ // Init a second time just to grab the wrapper from the promise
+ mWrapper->init();
+ EXPECT_EQ(mWrapper->alive(), true);
+
+ // First schedule the deletion
+ scheduleDelayedDestroyManaged();
+
+ // Then, send a hint to update the timestamp
+ mWrapper->sendLoadIncreaseHint();
+
+ // Then, run the delayed deletion after sending the update
+ allowDelayedDestructionToStart();
+ waitForDelayedDestructionToFinish();
+
+ // Ensure it didn't close within the timeframe of the test
+ Mock::VerifyAndClearExpectations(sMockBinding.get());
+ EXPECT_EQ(mWrapper->alive(), true);
+}
+
+} // namespace android::uirenderer::renderthread
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index a95ab55..76ea857 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -25,6 +25,7 @@
import android.graphics.Rect
import android.graphics.RectF
import android.os.Build
+import android.os.Handler
import android.os.Looper
import android.os.RemoteException
import android.util.Log
@@ -58,7 +59,14 @@
/** The animator used when animating a Dialog into an app. */
// TODO(b/218989950): Remove this animator and instead set the duration of the dim fade out to
// TIMINGS.contentBeforeFadeOutDuration.
- private val dialogToAppAnimator: LaunchAnimator = DEFAULT_DIALOG_TO_APP_ANIMATOR
+ private val dialogToAppAnimator: LaunchAnimator = DEFAULT_DIALOG_TO_APP_ANIMATOR,
+
+ /**
+ * Whether we should disable the WindowManager timeout. This should be set to true in tests
+ * only.
+ */
+ // TODO(b/301385865): Remove this flag.
+ private val disableWmTimeout: Boolean = false,
) {
companion object {
/** The timings when animating a View into an app. */
@@ -252,7 +260,7 @@
Log.d(
TAG,
"Calling controller.onIntentStarted(willAnimate=$willAnimate) " +
- "[controller=$this]"
+ "[controller=$this]"
)
}
this.onIntentStarted(willAnimate)
@@ -432,7 +440,8 @@
internal val delegate: AnimationDelegate
init {
- delegate = AnimationDelegate(controller, callback, listener, launchAnimator)
+ delegate =
+ AnimationDelegate(controller, callback, listener, launchAnimator, disableWmTimeout)
}
@BinderThread
@@ -462,13 +471,26 @@
/** Listener for animation lifecycle events. */
private val listener: Listener? = null,
/** The animator to use to animate the window launch. */
- private val launchAnimator: LaunchAnimator = DEFAULT_LAUNCH_ANIMATOR
+ private val launchAnimator: LaunchAnimator = DEFAULT_LAUNCH_ANIMATOR,
+
+ /**
+ * Whether we should disable the WindowManager timeout. This should be set to true in tests
+ * only.
+ */
+ // TODO(b/301385865): Remove this flag.
+ disableWmTimeout: Boolean = false,
) : RemoteAnimationDelegate<IRemoteAnimationFinishedCallback> {
private val launchContainer = controller.launchContainer
private val context = launchContainer.context
private val transactionApplierView =
controller.openingWindowSyncView ?: controller.launchContainer
private val transactionApplier = SyncRtSurfaceTransactionApplier(transactionApplierView)
+ private val timeoutHandler =
+ if (!disableWmTimeout) {
+ Handler(Looper.getMainLooper())
+ } else {
+ null
+ }
private val matrix = Matrix()
private val invertMatrix = Matrix()
@@ -488,11 +510,11 @@
@UiThread
internal fun postTimeout() {
- launchContainer.postDelayed(onTimeout, LAUNCH_TIMEOUT)
+ timeoutHandler?.postDelayed(onTimeout, LAUNCH_TIMEOUT)
}
private fun removeTimeout() {
- launchContainer.removeCallbacks(onTimeout)
+ timeoutHandler?.removeCallbacks(onTimeout)
}
@UiThread
@@ -606,12 +628,28 @@
object : Controller by delegate {
override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
listener?.onLaunchAnimationStart()
+
+ if (DEBUG_LAUNCH_ANIMATION) {
+ Log.d(
+ TAG,
+ "Calling controller.onLaunchAnimationStart(isExpandingFullyAbove=" +
+ "$isExpandingFullyAbove) [controller=$delegate]"
+ )
+ }
delegate.onLaunchAnimationStart(isExpandingFullyAbove)
}
override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
listener?.onLaunchAnimationEnd()
iCallback?.invoke()
+
+ if (DEBUG_LAUNCH_ANIMATION) {
+ Log.d(
+ TAG,
+ "Calling controller.onLaunchAnimationEnd(isExpandingFullyAbove=" +
+ "$isExpandingFullyAbove) [controller=$delegate]"
+ )
+ }
delegate.onLaunchAnimationEnd(isExpandingFullyAbove)
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index fd4a723..ecc0dba 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -148,7 +148,7 @@
override fun onPluginAttached(
manager: PluginLifecycleManager<ClockProviderPlugin>
): Boolean {
- manager.isDebug = true
+ manager.isDebug = !keepAllLoaded
if (keepAllLoaded) {
// Always load new plugins if requested
@@ -511,6 +511,12 @@
fun verifyLoadedProviders() {
val shouldSchedule = isVerifying.compareAndSet(false, true)
if (!shouldSchedule) {
+ logger.tryLog(
+ TAG,
+ LogLevel.VERBOSE,
+ {},
+ { "verifyLoadedProviders: shouldSchedule=false" }
+ )
return
}
@@ -670,6 +676,7 @@
{ str1 = clockId },
{ "Clock $str1 not loaded; using default" }
)
+ verifyLoadedProviders()
} else {
logger.tryLog(
TAG,
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java
index 7719e95..f9f2c63 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java
@@ -73,7 +73,7 @@
mComponentName = componentName;
mPluginFactory = pluginFactory;
mPlugin = plugin;
- mTag = TAG + mComponentName.toShortString()
+ mTag = TAG + "[" + mComponentName.getShortClassName() + "]"
+ '@' + Integer.toHexString(hashCode());
if (mPlugin != null) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 450010c..8eab31e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -108,6 +108,8 @@
private int mWeatherClockSmartspaceTranslateY = 0;
private int mDrawAlpha = 255;
+ private int mStatusBarHeight = 0;
+
/**
* Maintain state so that a newly connected plugin can be initialized.
*/
@@ -150,6 +152,8 @@
R.dimen.weather_clock_smartspace_translateX);
mWeatherClockSmartspaceTranslateY = mContext.getResources().getDimensionPixelSize(
R.dimen.weather_clock_smartspace_translateY);
+ mStatusBarHeight = mContext.getResources().getDimensionPixelSize(
+ R.dimen.status_bar_height);
updateStatusArea(/* animate= */false);
}
@@ -295,6 +299,8 @@
mStatusAreaAnim = null;
View in, out;
+ // statusAreaYTranslation uses for the translation for both mStatusArea and mSmallClockFrame
+ // statusAreaClockTranslateY only uses for mStatusArea
float statusAreaYTranslation, statusAreaClockScale = 1f;
float statusAreaClockTranslateX = 0f, statusAreaClockTranslateY = 0f;
float clockInYTranslation, clockOutYTranslation;
@@ -309,10 +315,21 @@
&& mClock.getLargeClock().getConfig().getHasCustomWeatherDataDisplay()) {
statusAreaClockScale = mWeatherClockSmartspaceScaling;
statusAreaClockTranslateX = mWeatherClockSmartspaceTranslateX;
- statusAreaClockTranslateY = mWeatherClockSmartspaceTranslateY - mSmartspaceTop;
if (mSplitShadeCentered) {
statusAreaClockTranslateX *= SMARTSPACE_TRANSLATION_CENTER_MULTIPLIER;
}
+
+ // On large weather clock,
+ // top padding for time is status bar height from top of the screen.
+ // On small one,
+ // it's screenOffsetYPadding (translationY for KeyguardStatusView),
+ // Cause smartspace is positioned according to the smallClockFrame
+ // we need to translate the difference between bottom of large clock and small clock
+ // Also, we need to counter offset the empty date weather view, mSmartspaceTop
+ // mWeatherClockSmartspaceTranslateY is only for Felix
+ statusAreaClockTranslateY = mStatusBarHeight - 0.6F * mSmallClockFrame.getHeight()
+ - mSmartspaceTop - screenOffsetYPadding
+ - statusAreaYTranslation + mWeatherClockSmartspaceTranslateY;
}
clockInYTranslation = 0;
clockOutYTranslation = 0; // Small clock translation is handled with statusArea
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
index bb799fc..d7019b5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
@@ -27,7 +27,7 @@
override var userId: Int = 0,
override var listening: Boolean = false,
// keep sorted
- var allowedDisplayState: Boolean = false,
+ var allowedDisplayStateWhileAwake: Boolean = false,
var alternateBouncerShowing: Boolean = false,
var authInterruptActive: Boolean = false,
var biometricSettingEnabledForUser: Boolean = false,
@@ -58,7 +58,7 @@
userId.toString(),
listening.toString(),
// keep sorted
- allowedDisplayState.toString(),
+ allowedDisplayStateWhileAwake.toString(),
alternateBouncerShowing.toString(),
authInterruptActive.toString(),
biometricSettingEnabledForUser.toString(),
@@ -98,7 +98,7 @@
userId = model.userId
listening = model.listening
// keep sorted
- allowedDisplayState = model.allowedDisplayState
+ allowedDisplayStateWhileAwake = model.allowedDisplayStateWhileAwake
alternateBouncerShowing = model.alternateBouncerShowing
authInterruptActive = model.authInterruptActive
biometricSettingEnabledForUser = model.biometricSettingEnabledForUser
@@ -143,7 +143,7 @@
"userId",
"listening",
// keep sorted
- "allowedDisplayState",
+ "allowedDisplayStateWhileAwake",
"alternateBouncerShowing",
"authInterruptActive",
"biometricSettingEnabledForUser",
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index e2855c9..7d2043d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -75,6 +75,7 @@
import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_STRONG_AUTH_CHANGED;
import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_USER_SWITCHING;
import static com.android.systemui.DejankUtils.whitelistIpcs;
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN;
@@ -163,6 +164,7 @@
import com.android.systemui.dump.DumpsysTableLogger;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.keyguard.domain.interactor.FaceAuthenticationListener;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.keyguard.shared.constants.TrustAgentUiEvent;
@@ -345,15 +347,16 @@
return;
}
- if (mDisplayTracker.getDisplay(mDisplayTracker.getDefaultDisplayId()).getState()
+ if (mWakefulness.getWakefulness() == WAKEFULNESS_AWAKE
+ && mDisplayTracker.getDisplay(mDisplayTracker.getDefaultDisplayId()).getState()
== Display.STATE_OFF) {
- mAllowedDisplayStateForFaceAuth = false;
+ mAllowedDisplayStateWhileAwakeForFaceAuth = false;
updateFaceListeningState(
BIOMETRIC_ACTION_STOP,
FACE_AUTH_DISPLAY_OFF
);
} else {
- mAllowedDisplayStateForFaceAuth = true;
+ mAllowedDisplayStateWhileAwakeForFaceAuth = true;
}
}
};
@@ -377,7 +380,7 @@
private boolean mOccludingAppRequestingFp;
private boolean mOccludingAppRequestingFace;
private boolean mSecureCameraLaunched;
- private boolean mAllowedDisplayStateForFaceAuth = true;
+ private boolean mAllowedDisplayStateWhileAwakeForFaceAuth = true;
@VisibleForTesting
protected boolean mTelephonyCapable;
private boolean mAllowFingerprintOnCurrentOccludingActivity;
@@ -426,6 +429,7 @@
private KeyguardFaceAuthInteractor mFaceAuthInteractor;
private final TaskStackChangeListeners mTaskStackChangeListeners;
private final IActivityTaskManager mActivityTaskManager;
+ private final WakefulnessLifecycle mWakefulness;
private final DisplayTracker mDisplayTracker;
private final LockPatternUtils mLockPatternUtils;
@VisibleForTesting
@@ -2211,7 +2215,7 @@
Trace.beginSection("KeyguardUpdateMonitor#handleStartedWakingUp");
Assert.isMainThread();
- mAllowedDisplayStateForFaceAuth = true;
+ mAllowedDisplayStateWhileAwakeForFaceAuth = true;
updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
if (mFaceWakeUpTriggersConfig.shouldTriggerFaceAuthOnWakeUpFrom(pmWakeReason)) {
FACE_AUTH_UPDATED_STARTED_WAKING_UP.setExtraInfo(pmWakeReason);
@@ -2368,7 +2372,8 @@
FeatureFlags featureFlags,
TaskStackChangeListeners taskStackChangeListeners,
IActivityTaskManager activityTaskManagerService,
- DisplayTracker displayTracker) {
+ DisplayTracker displayTracker,
+ WakefulnessLifecycle wakefulness) {
mContext = context;
mSubscriptionManager = subscriptionManager;
mUserTracker = userTracker;
@@ -2416,6 +2421,7 @@
.collect(Collectors.toSet());
mTaskStackChangeListeners = taskStackChangeListeners;
mActivityTaskManager = activityTaskManagerService;
+ mWakefulness = wakefulness;
mDisplayTracker = displayTracker;
if (mFeatureFlags.isEnabled(Flags.STOP_FACE_AUTH_ON_DISPLAY_OFF)) {
mDisplayTracker.addDisplayChangeCallback(mDisplayCallback, mainExecutor);
@@ -3230,7 +3236,7 @@
&& faceAndFpNotAuthenticated
&& !mGoingToSleep
&& isPostureAllowedForFaceAuth
- && mAllowedDisplayStateForFaceAuth;
+ && mAllowedDisplayStateWhileAwakeForFaceAuth;
// Aggregate relevant fields for debug logging.
logListenerModelData(
@@ -3238,7 +3244,7 @@
System.currentTimeMillis(),
user,
shouldListen,
- mAllowedDisplayStateForFaceAuth,
+ mAllowedDisplayStateWhileAwakeForFaceAuth,
mAlternateBouncerShowing,
mAuthInterruptActive,
biometricEnabledForUser,
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index f26404ca..4416b19 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -309,8 +309,14 @@
}
int invocationType = args.getInt(INVOCATION_TYPE_KEY);
- return mAssistOverrideInvocationTypes != null && Arrays.stream(
- mAssistOverrideInvocationTypes).anyMatch(override -> override == invocationType);
+ return shouldOverrideAssist(invocationType);
+ }
+
+ /** @return true if the invocation type should be handled by OverviewProxy instead of SysUI. */
+ public boolean shouldOverrideAssist(int invocationType) {
+ return mAssistOverrideInvocationTypes != null
+ && Arrays.stream(mAssistOverrideInvocationTypes).anyMatch(
+ override -> override == invocationType);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index c29f884..3472a85 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -562,7 +562,9 @@
+ mOverlay.getRequestId());
return false;
}
- if (mLockscreenShadeTransitionController.getQSDragProgress() != 0f
+
+ if ((mLockscreenShadeTransitionController.getQSDragProgress() != 0f
+ && !mAlternateBouncerInteractor.isVisibleState())
|| mPrimaryBouncerInteractor.isInTransit()) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index b6c50f7..f2d9144 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -389,7 +389,8 @@
@JvmField val NEW_BLUETOOTH_REPOSITORY = releasedFlag("new_bluetooth_repository")
// TODO(b/292533677): Tracking Bug
- val WIFI_TRACKER_LIB_FOR_WIFI_ICON = releasedFlag("wifi_tracker_lib_for_wifi_icon")
+ val WIFI_TRACKER_LIB_FOR_WIFI_ICON =
+ unreleasedFlag("wifi_tracker_lib_for_wifi_icon", teamfood = true)
// TODO(b/293863612): Tracking Bug
@JvmField val INCOMPATIBLE_CHARGING_BATTERY_ICON =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 2414ef98..2fe3201 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -763,6 +763,13 @@
}
}
}
+
+ @Override
+ public void onStrongAuthStateChanged(int userId) {
+ if (mLockPatternUtils.isUserInLockdown(KeyguardUpdateMonitor.getCurrentUser())) {
+ doKeyguardLocked(null);
+ }
+ }
};
ViewMediatorCallback mViewMediatorCallback = new ViewMediatorCallback() {
@@ -1957,6 +1964,10 @@
mExternallyEnabled = enabled;
if (!enabled && mShowing) {
+ if (mLockPatternUtils.isUserInLockdown(KeyguardUpdateMonitor.getCurrentUser())) {
+ Log.d(TAG, "keyguardEnabled(false) overridden by user lockdown");
+ return;
+ }
// hiding keyguard that is showing, remember to reshow later
if (DEBUG) Log.d(TAG, "remembering to reshow, hiding keyguard, "
+ "disabling status bar expansion");
@@ -2178,7 +2189,8 @@
*/
private void doKeyguardLocked(Bundle options) {
// if another app is disabling us, don't show
- if (!mExternallyEnabled) {
+ if (!mExternallyEnabled
+ && !mLockPatternUtils.isUserInLockdown(KeyguardUpdateMonitor.getCurrentUser())) {
if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");
mNeedToReshowWhenReenabled = true;
@@ -3774,6 +3786,13 @@
}
/**
+ * Notify whether keyguard has created a remote animation runner for next app launch.
+ */
+ public void launchingActivityOverLockscreen(boolean isLaunchingActivityOverLockscreen) {
+ mKeyguardTransitions.setLaunchingActivityOverLockscreen(isLaunchingActivityOverLockscreen);
+ }
+
+ /**
* Implementation of RemoteAnimationRunner that creates a new
* {@link ActivityLaunchAnimator.Runner} whenever onAnimationStart is called, delegating the
* remote animation methods to that runner.
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index c6f73ef..d7e062f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -233,6 +233,9 @@
Settings.Secure.getUriFor(Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED),
false, mAssistContentObserver, UserHandle.USER_ALL);
mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Secure.SEARCH_LONG_PRESS_HOME_ENABLED),
+ false, mAssistContentObserver, UserHandle.USER_ALL);
+ mContentResolver.registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED),
false, mAssistContentObserver, UserHandle.USER_ALL);
@@ -422,11 +425,17 @@
private void updateAssistantAvailability() {
boolean assistantAvailableForUser = mAssistManagerLazy.get()
.getAssistInfoForUser(mUserTracker.getUserId()) != null;
- boolean longPressDefault = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_assistLongPressHomeEnabledDefault);
+
+ boolean overrideLongPressHome = mAssistManagerLazy.get()
+ .shouldOverrideAssist(AssistManager.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
+ boolean longPressDefault = mContext.getResources().getBoolean(overrideLongPressHome
+ ? com.android.internal.R.bool.config_searchLongPressHomeEnabledDefault
+ : com.android.internal.R.bool.config_assistLongPressHomeEnabledDefault);
mLongPressHomeEnabled = Settings.Secure.getIntForUser(mContentResolver,
- Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, longPressDefault ? 1 : 0,
+ overrideLongPressHome ? Secure.SEARCH_LONG_PRESS_HOME_ENABLED
+ : Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, longPressDefault ? 1 : 0,
mUserTracker.getUserId()) != 0;
+
boolean gestureDefault = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_assistTouchGestureEnabledDefault);
mAssistantTouchGestureEnabled = Settings.Secure.getIntForUser(mContentResolver,
@@ -455,6 +464,7 @@
@Override
public void setAssistantOverridesRequested(int[] invocationTypes) {
mAssistManagerLazy.get().setAssistantOverridesRequested(invocationTypes);
+ updateAssistantAvailability();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index e76bae5..6f65f70 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -3716,6 +3716,7 @@
@Override
public void setIsLaunchingActivityOverLockscreen(boolean isLaunchingActivityOverLockscreen) {
mIsLaunchingActivityOverLockscreen = isLaunchingActivityOverLockscreen;
+ mKeyguardViewMediator.launchingActivityOverLockscreen(mIsLaunchingActivityOverLockscreen);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
index 6483b7c..7ec8e12 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
@@ -25,6 +25,7 @@
import android.widget.Space
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.android.settingslib.graph.SignalDrawable
import com.android.systemui.R
@@ -62,7 +63,7 @@
val roamingSpace = view.requireViewById<Space>(R.id.mobile_roaming_space)
val dotView = view.requireViewById<StatusBarIconView>(R.id.status_bar_dot)
- view.isVisible = true
+ view.isVisible = viewModel.isVisible.value
iconView.isVisible = true
// TODO(b/238425913): We should log this visibility state.
@@ -75,99 +76,113 @@
var isCollecting = false
view.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- logger.logCollectionStarted(view, viewModel)
- isCollecting = true
-
- launch {
- visibilityState.collect { state ->
- ModernStatusBarViewVisibilityHelper.setVisibilityState(
- state,
- mobileGroupView,
- dotView,
- )
+ lifecycleScope.launch {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ // isVisible controls the visibility state of the outer group, and thus it needs
+ // to run in the CREATED lifecycle so it can continue to watch while invisible
+ // See (b/291031862) for details
+ launch {
+ viewModel.isVisible.collect { isVisible ->
+ viewModel.verboseLogger?.logBinderReceivedVisibility(
+ view,
+ viewModel.subscriptionId,
+ isVisible
+ )
+ view.isVisible = isVisible
+ // [StatusIconContainer] can get out of sync sometimes. Make sure to
+ // request another layout when this changes.
+ view.requestLayout()
+ }
}
}
+ }
- launch {
- viewModel.isVisible.collect { isVisible ->
- viewModel.verboseLogger?.logBinderReceivedVisibility(
- view,
- viewModel.subscriptionId,
- isVisible
- )
- view.isVisible = isVisible
+ lifecycleScope.launch {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ logger.logCollectionStarted(view, viewModel)
+ isCollecting = true
+
+ launch {
+ visibilityState.collect { state ->
+ ModernStatusBarViewVisibilityHelper.setVisibilityState(
+ state,
+ mobileGroupView,
+ dotView,
+ )
+ }
}
- }
- // Set the icon for the triangle
- launch {
- viewModel.icon.distinctUntilChanged().collect { icon ->
- viewModel.verboseLogger?.logBinderReceivedSignalIcon(
- view,
- viewModel.subscriptionId,
- icon,
- )
- mobileDrawable.level = icon.toSignalDrawableState()
+ // Set the icon for the triangle
+ launch {
+ viewModel.icon.distinctUntilChanged().collect { icon ->
+ viewModel.verboseLogger?.logBinderReceivedSignalIcon(
+ view,
+ viewModel.subscriptionId,
+ icon,
+ )
+ mobileDrawable.level = icon.toSignalDrawableState()
+ }
}
- }
- launch {
- viewModel.contentDescription.distinctUntilChanged().collect {
- ContentDescriptionViewBinder.bind(it, view)
+ launch {
+ viewModel.contentDescription.distinctUntilChanged().collect {
+ ContentDescriptionViewBinder.bind(it, view)
+ }
}
- }
- // Set the network type icon
- launch {
- viewModel.networkTypeIcon.distinctUntilChanged().collect { dataTypeId ->
- viewModel.verboseLogger?.logBinderReceivedNetworkTypeIcon(
- view,
- viewModel.subscriptionId,
- dataTypeId,
- )
- dataTypeId?.let { IconViewBinder.bind(dataTypeId, networkTypeView) }
- networkTypeView.visibility = if (dataTypeId != null) VISIBLE else GONE
+ // Set the network type icon
+ launch {
+ viewModel.networkTypeIcon.distinctUntilChanged().collect { dataTypeId ->
+ viewModel.verboseLogger?.logBinderReceivedNetworkTypeIcon(
+ view,
+ viewModel.subscriptionId,
+ dataTypeId,
+ )
+ dataTypeId?.let { IconViewBinder.bind(dataTypeId, networkTypeView) }
+ networkTypeView.visibility = if (dataTypeId != null) VISIBLE else GONE
+ }
}
- }
- // Set the roaming indicator
- launch {
- viewModel.roaming.distinctUntilChanged().collect { isRoaming ->
- roamingView.isVisible = isRoaming
- roamingSpace.isVisible = isRoaming
+ // Set the roaming indicator
+ launch {
+ viewModel.roaming.distinctUntilChanged().collect { isRoaming ->
+ roamingView.isVisible = isRoaming
+ roamingSpace.isVisible = isRoaming
+ }
}
- }
- // Set the activity indicators
- launch { viewModel.activityInVisible.collect { activityIn.isVisible = it } }
+ // Set the activity indicators
+ launch { viewModel.activityInVisible.collect { activityIn.isVisible = it } }
- launch { viewModel.activityOutVisible.collect { activityOut.isVisible = it } }
+ launch { viewModel.activityOutVisible.collect { activityOut.isVisible = it } }
- launch {
- viewModel.activityContainerVisible.collect { activityContainer.isVisible = it }
- }
-
- // Set the tint
- launch {
- iconTint.collect { tint ->
- val tintList = ColorStateList.valueOf(tint)
- iconView.imageTintList = tintList
- networkTypeView.imageTintList = tintList
- roamingView.imageTintList = tintList
- activityIn.imageTintList = tintList
- activityOut.imageTintList = tintList
- dotView.setDecorColor(tint)
+ launch {
+ viewModel.activityContainerVisible.collect {
+ activityContainer.isVisible = it
+ }
}
- }
- launch { decorTint.collect { tint -> dotView.setDecorColor(tint) } }
+ // Set the tint
+ launch {
+ iconTint.collect { tint ->
+ val tintList = ColorStateList.valueOf(tint)
+ iconView.imageTintList = tintList
+ networkTypeView.imageTintList = tintList
+ roamingView.imageTintList = tintList
+ activityIn.imageTintList = tintList
+ activityOut.imageTintList = tintList
+ dotView.setDecorColor(tint)
+ }
+ }
- try {
- awaitCancellation()
- } finally {
- isCollecting = false
- logger.logCollectionStopped(view, viewModel)
+ launch { decorTint.collect { tint -> dotView.setDecorColor(tint) } }
+
+ try {
+ awaitCancellation()
+ } finally {
+ isCollecting = false
+ logger.logCollectionStopped(view, viewModel)
+ }
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 6f3322a..a052f77 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -41,6 +41,9 @@
import static com.android.keyguard.KeyguardUpdateMonitor.getCurrentUser;
import static com.android.systemui.flags.Flags.FP_LISTEN_OCCLUDING_APPS;
import static com.android.systemui.flags.Flags.STOP_FACE_AUTH_ON_DISPLAY_OFF;
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_CLOSED;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN;
@@ -146,6 +149,7 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.settings.FakeDisplayTracker;
@@ -283,6 +287,8 @@
private TaskStackChangeListeners mTaskStackChangeListeners;
@Mock
private IActivityTaskManager mActivityTaskManager;
+ @Mock
+ private WakefulnessLifecycle mWakefulness;
private List<FaceSensorPropertiesInternal> mFaceSensorProperties;
private List<FingerprintSensorPropertiesInternal> mFingerprintSensorProperties;
@@ -3084,8 +3090,57 @@
// THEN face listening is stopped.
verify(faceCancel).cancel();
verify(callback).onBiometricRunningStateChanged(
- eq(false), eq(BiometricSourceType.FACE)); // beverlyt
+ eq(false), eq(BiometricSourceType.FACE));
+ }
+ @Test
+ public void onDisplayOff_whileAsleep_doesNotStopFaceAuth() throws RemoteException {
+ enableStopFaceAuthOnDisplayOff();
+ when(mWakefulness.getWakefulness()).thenReturn(WAKEFULNESS_ASLEEP);
+
+ // GIVEN device is listening for face
+ mKeyguardUpdateMonitor.setKeyguardShowing(true, false);
+ mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
+ mTestableLooper.processAllMessages();
+ verifyFaceAuthenticateCall();
+
+ final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal);
+ mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel;
+ KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
+ mKeyguardUpdateMonitor.registerCallback(callback);
+
+ // WHEN the default display state changes to OFF
+ triggerDefaultDisplayStateChangeToOff();
+
+ // THEN face listening is NOT stopped.
+ verify(faceCancel, never()).cancel();
+ verify(callback, never()).onBiometricRunningStateChanged(
+ eq(false), eq(BiometricSourceType.FACE));
+ }
+
+ @Test
+ public void onDisplayOff_whileWaking_doesNotStopFaceAuth() throws RemoteException {
+ enableStopFaceAuthOnDisplayOff();
+ when(mWakefulness.getWakefulness()).thenReturn(WAKEFULNESS_WAKING);
+
+ // GIVEN device is listening for face
+ mKeyguardUpdateMonitor.setKeyguardShowing(true, false);
+ mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
+ mTestableLooper.processAllMessages();
+ verifyFaceAuthenticateCall();
+
+ final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal);
+ mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel;
+ KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
+ mKeyguardUpdateMonitor.registerCallback(callback);
+
+ // WHEN the default display state changes to OFF
+ triggerDefaultDisplayStateChangeToOff();
+
+ // THEN face listening is NOT stopped.
+ verify(faceCancel, never()).cancel();
+ verify(callback, never()).onBiometricRunningStateChanged(
+ eq(false), eq(BiometricSourceType.FACE));
}
private void triggerDefaultDisplayStateChangeToOn() {
@@ -3393,6 +3448,7 @@
mFeatureFlags.set(STOP_FACE_AUTH_ON_DISPLAY_OFF, true);
mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mContext);
setupBiometrics(mKeyguardUpdateMonitor);
+ when(mWakefulness.getWakefulness()).thenReturn(WAKEFULNESS_AWAKE);
assertThat(mDisplayTracker.getDisplayCallbacks().size()).isEqualTo(1);
}
@@ -3473,7 +3529,8 @@
mPackageManager, mFaceManager, mFingerprintManager, mBiometricManager,
mFaceWakeUpTriggersConfig, mDevicePostureController,
Optional.of(mInteractiveToAuthProvider), mFeatureFlags,
- mTaskStackChangeListeners, mActivityTaskManager, mDisplayTracker);
+ mTaskStackChangeListeners, mActivityTaskManager, mDisplayTracker,
+ mWakefulness);
setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
index cc00436..59c7e76 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
@@ -57,7 +57,8 @@
@Before
fun setup() {
- activityLaunchAnimator = ActivityLaunchAnimator(testLaunchAnimator, testLaunchAnimator)
+ activityLaunchAnimator =
+ ActivityLaunchAnimator(testLaunchAnimator, testLaunchAnimator, disableWmTimeout = true)
activityLaunchAnimator.callback = callback
activityLaunchAnimator.addListener(listener)
}
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 7dd88b4..e7fc870 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -1597,6 +1597,53 @@
anyBoolean());
}
+
+ @Test
+ public void onTouch_withNewTouchDetection_qsDrag_processesTouchWhenAlternateBouncerVisible()
+ throws RemoteException {
+ final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
+ 0L);
+ final TouchProcessorResult processorResultMove =
+ new TouchProcessorResult.ProcessedTouch(InteractionEvent.DOWN,
+ 1 /* pointerId */, touchData);
+
+ // Enable new touch detection.
+ when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true);
+
+ // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
+ initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */);
+
+ // Configure UdfpsView to accept the ACTION_MOVE event
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
+ when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
+
+ // GIVEN that the alternate bouncer is showing and a11y touch exploration NOT enabled
+ when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
+ when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+ mFgExecutor.runAllReady();
+
+ verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+
+ // GIVEN swipe down for QS
+ when(mPrimaryBouncerInteractor.isInTransit()).thenReturn(false);
+ when(mLockscreenShadeTransitionController.getQSDragProgress()).thenReturn(1f);
+
+ // WHEN ACTION_MOVE is received and touch is within sensor
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ processorResultMove);
+ MotionEvent moveEvent = MotionEvent.obtain(0, 0, ACTION_MOVE, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
+ mBiometricExecutor.runAllReady();
+ moveEvent.recycle();
+
+ // THEN the touch is still processed
+ verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(),
+ anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(),
+ anyBoolean());
+ }
+
@Test
public void onAodInterrupt_onAcquiredGood_fingerNoLongerDown() throws RemoteException {
// GIVEN UDFPS overlay is showing
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index e568c24..fdeed9f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -177,6 +177,8 @@
private @Mock ShadeWindowLogger mShadeWindowLogger;
private @Captor ArgumentCaptor<KeyguardStateController.Callback>
mKeyguardStateControllerCallback;
+ private @Captor ArgumentCaptor<KeyguardUpdateMonitorCallback>
+ mKeyguardUpdateMonitorCallbackCaptor;
private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake();
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
@@ -237,6 +239,45 @@
}
@Test
+ @TestableLooper.RunWithLooper(setAsMainLooper = true)
+ public void onLockdown_showKeyguard_evenIfKeyguardIsNotEnabledExternally() {
+ // GIVEN keyguard is not enabled and isn't showing
+ mViewMediator.onSystemReady();
+ mViewMediator.setKeyguardEnabled(false);
+ TestableLooper.get(this).processAllMessages();
+ captureKeyguardUpdateMonitorCallback();
+ assertFalse(mViewMediator.isShowingAndNotOccluded());
+
+ // WHEN lockdown occurs
+ when(mLockPatternUtils.isUserInLockdown(anyInt())).thenReturn(true);
+ mKeyguardUpdateMonitorCallbackCaptor.getValue().onStrongAuthStateChanged(0);
+
+ // THEN keyguard is shown
+ TestableLooper.get(this).processAllMessages();
+ assertTrue(mViewMediator.isShowingAndNotOccluded());
+ }
+
+ @Test
+ @TestableLooper.RunWithLooper(setAsMainLooper = true)
+ public void doNotHideKeyguard_whenLockdown_onKeyguardNotEnabledExternally() {
+ // GIVEN keyguard is enabled and lockdown occurred so the keyguard is showing
+ mViewMediator.onSystemReady();
+ mViewMediator.setKeyguardEnabled(true);
+ TestableLooper.get(this).processAllMessages();
+ captureKeyguardUpdateMonitorCallback();
+ when(mLockPatternUtils.isUserInLockdown(anyInt())).thenReturn(true);
+ mKeyguardUpdateMonitorCallbackCaptor.getValue().onStrongAuthStateChanged(0);
+ assertTrue(mViewMediator.isShowingAndNotOccluded());
+
+ // WHEN keyguard is externally not enabled anymore
+ mViewMediator.setKeyguardEnabled(false);
+
+ // THEN keyguard is NOT dismissed; it continues to show
+ TestableLooper.get(this).processAllMessages();
+ assertTrue(mViewMediator.isShowingAndNotOccluded());
+ }
+
+ @Test
public void testOnGoingToSleep_UpdatesKeyguardGoingAway() {
mViewMediator.onStartedGoingToSleep(OFF_BECAUSE_OF_USER);
verify(mUpdateMonitor).dispatchKeyguardGoingAway(false);
@@ -982,4 +1023,8 @@
private void captureKeyguardStateControllerCallback() {
verify(mKeyguardStateController).addCallback(mKeyguardStateControllerCallback.capture());
}
+
+ private void captureKeyguardUpdateMonitorCallback() {
+ verify(mUpdateMonitor).registerCallback(mKeyguardUpdateMonitorCallbackCaptor.capture());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java
index 5fa6b3a..e7056c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java
@@ -58,6 +58,7 @@
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel;
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.ShadeCarrierGroupMobileIconViewModel;
import com.android.systemui.util.CarrierConfigTracker;
+import com.android.systemui.util.kotlin.FlowProviderKt;
import com.android.systemui.utils.leaks.LeakCheckedTest;
import com.android.systemui.utils.os.FakeHandler;
@@ -72,6 +73,8 @@
import java.util.Arrays;
import java.util.List;
+import kotlinx.coroutines.flow.MutableStateFlow;
+
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
@@ -105,7 +108,8 @@
private ShadeCarrier mShadeCarrier3;
private TestableLooper mTestableLooper;
@Mock
- private ShadeCarrierGroupController.OnSingleCarrierChangedListener mOnSingleCarrierChangedListener;
+ private ShadeCarrierGroupController.OnSingleCarrierChangedListener
+ mOnSingleCarrierChangedListener;
@Mock
private MobileUiAdapter mMobileUiAdapter;
@Mock
@@ -119,6 +123,9 @@
@Mock
private StatusBarPipelineFlags mStatusBarPipelineFlags;
+ private final MutableStateFlow<Boolean> mIsVisibleFlow =
+ FlowProviderKt.getMutableStateFlow(true);
+
private FakeSlotIndexResolver mSlotIndexResolver;
private ClickListenerTextView mNoCarrierTextView;
@@ -170,7 +177,7 @@
mMobileUiAdapter,
mMobileContextProvider,
mStatusBarPipelineFlags
- )
+ )
.setShadeCarrierGroup(mShadeCarrierGroup)
.build();
@@ -181,6 +188,7 @@
when(mStatusBarPipelineFlags.useNewShadeCarrierGroupMobileIcons()).thenReturn(true);
when(mMobileContextProvider.getMobileContextForSub(anyInt(), any())).thenReturn(mContext);
when(mMobileIconsViewModel.getLogger()).thenReturn(mMobileViewLogger);
+ when(mShadeCarrierGroupMobileIconViewModel.isVisible()).thenReturn(mIsVisibleFlow);
when(mMobileIconsViewModel.viewModelForSub(anyInt(), any()))
.thenReturn(mShadeCarrierGroupMobileIconViewModel);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowProvider.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowProvider.kt
new file mode 100644
index 0000000..274acc9
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowProvider.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2023 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.util.kotlin
+
+import kotlinx.coroutines.flow.MutableStateFlow
+
+/** Wrapper for flow constructors that can be retrieved from java tests */
+fun <T> getMutableStateFlow(value: T): MutableStateFlow<T> = MutableStateFlow(value)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index 2f563dd..33deb65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -41,6 +41,7 @@
import android.app.KeyguardManager;
import android.content.res.Configuration;
import android.media.AudioManager;
+import android.os.Handler;
import android.os.SystemClock;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -146,6 +147,10 @@
mTestableLooper = TestableLooper.get(this);
allowTestableLooperAsMainThread();
+ // Ensure previous tests have not left messages on main looper
+ Handler localHandler = new Handler(mTestableLooper.getLooper());
+ localHandler.removeCallbacksAndMessages(null);
+
when(mPostureController.getDevicePosture())
.thenReturn(DevicePostureController.DEVICE_POSTURE_CLOSED);
diff --git a/services/autofill/java/com/android/server/autofill/FieldClassificationEventLogger.java b/services/autofill/java/com/android/server/autofill/FieldClassificationEventLogger.java
index ffb4632..db2e18a0 100644
--- a/services/autofill/java/com/android/server/autofill/FieldClassificationEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/FieldClassificationEventLogger.java
@@ -17,12 +17,19 @@
package com.android.server.autofill;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED__STATUS__STATUS_CANCELLED;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED__STATUS__STATUS_FAIL;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED__STATUS__STATUS_SUCCESS;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED__STATUS__STATUS_UNKNOWN;
import static com.android.server.autofill.Helper.sVerbose;
+import android.annotation.IntDef;
import android.util.Slog;
import com.android.internal.util.FrameworkStatsLog;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Optional;
/**
@@ -36,6 +43,29 @@
mEventInternal = Optional.empty();
}
+ public static final int STATUS_SUCCESS =
+ AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED__STATUS__STATUS_SUCCESS;
+ public static final int STATUS_UNKNOWN =
+ AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED__STATUS__STATUS_UNKNOWN;
+ public static final int STATUS_FAIL =
+ AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED__STATUS__STATUS_FAIL;
+ public static final int STATUS_CANCELLED =
+ AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED__STATUS__STATUS_CANCELLED;
+
+ /**
+ * Status of the FieldClassification IPC request. These are wrappers around
+ * {@link com.android.os.AtomsProto.AutofillFieldClassificationEventReported.FieldClassificationRequestStatus}.
+ */
+ @IntDef(prefix = {"STATUS"}, value = {
+ STATUS_UNKNOWN,
+ STATUS_SUCCESS,
+ STATUS_FAIL,
+ STATUS_CANCELLED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FieldClassificationStatus {
+ }
+
/**
* A factory constructor to create FieldClassificationEventLogger.
*/
@@ -56,7 +86,7 @@
}
/**
- * Set latency as long as mEventInternal presents.
+ * Set latency_millis as long as mEventInternal presents.
*/
public void maybeSetLatencyMillis(long timestamp) {
mEventInternal.ifPresent(event -> {
@@ -65,6 +95,69 @@
}
/**
+ * Set count_classifications as long as mEventInternal presents.
+ */
+ public void maybeSetCountClassifications(int countClassifications) {
+ mEventInternal.ifPresent(event -> {
+ event.mCountClassifications = countClassifications;
+ });
+ }
+
+ /**
+ * Set session_id as long as mEventInternal presents.
+ */
+ public void maybeSetSessionId(int sessionId) {
+ mEventInternal.ifPresent(event -> {
+ event.mSessionId = sessionId;
+ });
+ }
+
+ /**
+ * Set request_id as long as mEventInternal presents.
+ */
+ public void maybeSetRequestId(int requestId) {
+ mEventInternal.ifPresent(event -> {
+ event.mRequestId = requestId;
+ });
+ }
+
+ /**
+ * Set next_fill_request_id as long as mEventInternal presents.
+ */
+ public void maybeSetNextFillRequestId(int nextFillRequestId) {
+ mEventInternal.ifPresent(event -> {
+ event.mNextFillRequestId = nextFillRequestId;
+ });
+ }
+
+ /**
+ * Set app_package_uid as long as mEventInternal presents.
+ */
+ public void maybeSetAppPackageUid(int uid) {
+ mEventInternal.ifPresent(event -> {
+ event.mAppPackageUid = uid;
+ });
+ }
+
+ /**
+ * Set status as long as mEventInternal presents.
+ */
+ public void maybeSetRequestStatus(@FieldClassificationStatus int status) {
+ mEventInternal.ifPresent(event -> {
+ event.mStatus = status;
+ });
+ }
+
+ /**
+ * Set is_session_gc as long as mEventInternal presents.
+ */
+ public void maybeSetSessionGc(boolean isSessionGc) {
+ mEventInternal.ifPresent(event -> {
+ event.mIsSessionGc = isSessionGc;
+ });
+ }
+
+ /**
* Log an AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED event.
*/
public void logAndEndEvent() {
@@ -77,16 +170,37 @@
if (sVerbose) {
Slog.v(TAG, "Log AutofillFieldClassificationEventReported:"
+ " mLatencyClassificationRequestMillis="
- + event.mLatencyClassificationRequestMillis);
+ + event.mLatencyClassificationRequestMillis
+ + " mCountClassifications=" + event.mCountClassifications
+ + " mSessionId=" + event.mSessionId
+ + " mRequestId=" + event.mRequestId
+ + " mNextFillRequestId=" + event.mNextFillRequestId
+ + " mAppPackageUid=" + event.mAppPackageUid
+ + " mStatus=" + event.mStatus
+ + " mIsSessionGc=" + event.mIsSessionGc);
}
FrameworkStatsLog.write(
AUTOFILL_FIELD_CLASSIFICATION_EVENT_REPORTED,
- event.mLatencyClassificationRequestMillis);
+ event.mLatencyClassificationRequestMillis,
+ event.mCountClassifications,
+ event.mSessionId,
+ event.mRequestId,
+ event.mNextFillRequestId,
+ event.mAppPackageUid,
+ event.mStatus,
+ event.mIsSessionGc);
mEventInternal = Optional.empty();
}
private static final class FieldClassificationEventInternal {
long mLatencyClassificationRequestMillis = -1;
+ int mCountClassifications = -1;
+ int mSessionId = -1;
+ int mRequestId = -1;
+ int mNextFillRequestId = -1;
+ int mAppPackageUid = -1;
+ int mStatus;
+ boolean mIsSessionGc;
FieldClassificationEventInternal() {
}
diff --git a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
index 11b45db..6b0fdb5 100644
--- a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
@@ -211,15 +211,25 @@
public static final int DETECTION_PREFER_PCC =
AUTOFILL_FILL_RESPONSE_REPORTED__DETECTION_PREFERENCE__DETECTION_PREFER_PCC;
private final int mSessionId;
+
+ /**
+ * For app_package_uid.
+ */
+ private final int mCallingAppUid;
private Optional<PresentationStatsEventInternal> mEventInternal;
- private PresentationStatsEventLogger(int sessionId) {
+ private PresentationStatsEventLogger(int sessionId, int callingAppUid) {
mSessionId = sessionId;
+ mCallingAppUid = callingAppUid;
mEventInternal = Optional.empty();
}
- public static PresentationStatsEventLogger forSessionId(int sessionId) {
- return new PresentationStatsEventLogger(sessionId);
+ /**
+ * Create PresentationStatsEventLogger, populated with sessionId and the callingAppUid
+ */
+ public static PresentationStatsEventLogger createPresentationLog(
+ int sessionId, int callingAppUid) {
+ return new PresentationStatsEventLogger(sessionId, callingAppUid);
}
public void startNewEvent() {
@@ -508,6 +518,14 @@
return PICK_REASON_UNKNOWN;
}
+ /**
+ * Set field_classification_request_id as long as mEventInternal presents.
+ */
+ public void maybeSetFieldClassificationRequestId(int requestId) {
+ mEventInternal.ifPresent(event -> {
+ event.mFieldClassificationRequestId = requestId;
+ });
+ }
public void logAndEndEvent() {
if (!mEventInternal.isPresent()) {
@@ -547,7 +565,9 @@
+ " mAvailablePccCount=" + event.mAvailablePccCount
+ " mAvailablePccOnlyCount=" + event.mAvailablePccOnlyCount
+ " mSelectedDatasetPickedReason=" + event.mSelectedDatasetPickedReason
- + " mDetectionPreference=" + event.mDetectionPreference);
+ + " mDetectionPreference=" + event.mDetectionPreference
+ + " mFieldClassificationRequestId=" + event.mFieldClassificationRequestId
+ + " mAppPackageUid=" + mCallingAppUid);
}
// TODO(b/234185326): Distinguish empty responses from other no presentation reasons.
@@ -584,7 +604,9 @@
event.mAvailablePccCount,
event.mAvailablePccOnlyCount,
event.mSelectedDatasetPickedReason,
- event.mDetectionPreference);
+ event.mDetectionPreference,
+ event.mFieldClassificationRequestId,
+ mCallingAppUid);
mEventInternal = Optional.empty();
}
@@ -617,6 +639,7 @@
int mAvailablePccOnlyCount = -1;
@DatasetPickedReason int mSelectedDatasetPickedReason = PICK_REASON_UNKNOWN;
@DetectionPreference int mDetectionPreference = DETECTION_PREFER_UNKNOWN;
+ int mFieldClassificationRequestId = -1;
PresentationStatsEventInternal() {}
}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFieldClassificationService.java b/services/autofill/java/com/android/server/autofill/RemoteFieldClassificationService.java
index bcca006..50fabfd 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFieldClassificationService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFieldClassificationService.java
@@ -69,6 +69,9 @@
void onClassificationRequestFailure(int requestId, @Nullable CharSequence message);
void onClassificationRequestTimeout(int requestId);
void onServiceDied(@NonNull RemoteFieldClassificationService service);
+ void logFieldClassificationEvent(
+ long startTime, @NonNull FieldClassificationResponse response,
+ @FieldClassificationEventLogger.FieldClassificationStatus int status);
}
RemoteFieldClassificationService(Context context, ComponentName serviceName,
@@ -149,15 +152,24 @@
new IFieldClassificationCallback.Stub() {
@Override
public void onCancellable(ICancellationSignal cancellation) {
- logLatency(startTime);
if (sDebug) {
Log.d(TAG, "onCancellable");
}
+ FieldClassificationServiceCallbacks
+ fieldClassificationServiceCallbacks =
+ Helper.weakDeref(
+ fieldClassificationServiceCallbacksWeakRef,
+ TAG, "onCancellable "
+ );
+ logFieldClassificationEvent(
+ startTime,
+ fieldClassificationServiceCallbacks,
+ FieldClassificationEventLogger.STATUS_CANCELLED,
+ null);
}
@Override
public void onSuccess(FieldClassificationResponse response) {
- logLatency(startTime);
if (sDebug) {
if (Build.IS_DEBUGGABLE) {
Slog.d(TAG, "onSuccess Response: " + response);
@@ -179,6 +191,11 @@
fieldClassificationServiceCallbacksWeakRef,
TAG, "onSuccess "
);
+ logFieldClassificationEvent(
+ startTime,
+ fieldClassificationServiceCallbacks,
+ FieldClassificationEventLogger.STATUS_SUCCESS,
+ response);
if (fieldClassificationServiceCallbacks == null) {
return;
}
@@ -188,7 +205,6 @@
@Override
public void onFailure() {
- logLatency(startTime);
if (sDebug) {
Slog.d(TAG, "onFailure");
}
@@ -198,6 +214,11 @@
fieldClassificationServiceCallbacksWeakRef,
TAG, "onFailure "
);
+ logFieldClassificationEvent(
+ startTime,
+ fieldClassificationServiceCallbacks,
+ FieldClassificationEventLogger.STATUS_FAIL,
+ null);
if (fieldClassificationServiceCallbacks == null) {
return;
}
@@ -215,11 +236,24 @@
}));
}
- private void logLatency(long startTime) {
- final FieldClassificationEventLogger logger = FieldClassificationEventLogger.createLogger();
- logger.startNewLogForRequest();
- logger.maybeSetLatencyMillis(
- SystemClock.elapsedRealtime() - startTime);
- logger.logAndEndEvent();
+ private void logFieldClassificationEvent(
+ long startTime,
+ @Nullable FieldClassificationServiceCallbacks fieldClassificationServiceCallbacks,
+ @FieldClassificationEventLogger.FieldClassificationStatus int status,
+ FieldClassificationResponse response) {
+ if (fieldClassificationServiceCallbacks == null) {
+ final FieldClassificationEventLogger logger =
+ FieldClassificationEventLogger.createLogger();
+ logger.startNewLogForRequest();
+ logger.maybeSetLatencyMillis(
+ SystemClock.elapsedRealtime() - startTime);
+ logger.maybeSetSessionGc(true);
+ logger.maybeSetRequestStatus(status);
+ logger.logAndEndEvent();
+ } else {
+ fieldClassificationServiceCallbacks.logFieldClassificationEvent(
+ startTime, response, status);
+ }
+
}
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index d6702b7..da9235f 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -53,8 +53,8 @@
import static com.android.server.autofill.FillRequestEventLogger.TRIGGER_REASON_SERVED_FROM_CACHED_RESPONSE;
import static com.android.server.autofill.FillResponseEventLogger.AVAILABLE_COUNT_WHEN_FILL_REQUEST_FAILED_OR_TIMEOUT;
import static com.android.server.autofill.FillResponseEventLogger.DETECTION_PREFER_AUTOFILL_PROVIDER;
-import static com.android.server.autofill.FillResponseEventLogger.DETECTION_PREFER_UNKNOWN;
import static com.android.server.autofill.FillResponseEventLogger.DETECTION_PREFER_PCC;
+import static com.android.server.autofill.FillResponseEventLogger.DETECTION_PREFER_UNKNOWN;
import static com.android.server.autofill.FillResponseEventLogger.HAVE_SAVE_TRIGGER_ID;
import static com.android.server.autofill.FillResponseEventLogger.RESPONSE_STATUS_FAILURE;
import static com.android.server.autofill.FillResponseEventLogger.RESPONSE_STATUS_SESSION_DESTROYED;
@@ -228,6 +228,8 @@
private static final String PCC_HINTS_DELIMITER = ",";
public static final String EXTRA_KEY_DETECTIONS = "detections";
+ private static final int DEFAULT__FILL_REQUEST_ID_SNAPSHOT = -2;
+ private static final int DEFAULT__FIELD_CLASSIFICATION_REQUEST_ID_SNAPSHOT = -2;
final Object mLock;
@@ -411,6 +413,20 @@
@GuardedBy("mLock")
private long mUiShownTime;
+ /**
+ * Tracks the value of the fill request id at the time of issuing request for field
+ * classification.
+ */
+ @GuardedBy("mLock")
+ private int mFillRequestIdSnapshot = DEFAULT__FILL_REQUEST_ID_SNAPSHOT;
+
+ /**
+ * Tracks the value of the field classification id at the time of issuing request for fill
+ * request.
+ */
+ @GuardedBy("mLock")
+ private int mFieldClassificationIdSnapshot = DEFAULT__FIELD_CLASSIFICATION_REQUEST_ID_SNAPSHOT;
+
@GuardedBy("mLock")
private final LocalLog mUiLatencyHistory;
@@ -660,6 +676,7 @@
if (mPendingFillRequest == null) {
return;
}
+ mFieldClassificationIdSnapshot = sIdCounterForPcc.get();
if (mWaitForInlineRequest) {
if (mPendingInlineSuggestionsRequest == null) {
@@ -1215,6 +1232,8 @@
+ ", flags=" + flags);
}
mPresentationStatsEventLogger.maybeSetRequestId(requestId);
+ mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId(
+ mFieldClassificationIdSnapshot);
mFillRequestEventLogger.maybeSetRequestId(requestId);
mFillRequestEventLogger.maybeSetAutofillServiceUid(getAutofillServiceUid());
if (mSessionFlags.mInlineSupportedByService) {
@@ -1284,6 +1303,7 @@
@GuardedBy("mLock")
private void requestAssistStructureForPccLocked(int flags) {
if (!mClassificationState.shouldTriggerRequest()) return;
+ mFillRequestIdSnapshot = sIdCounter.get();
mClassificationState.updatePendingRequest();
// Get request id
int requestId;
@@ -1368,7 +1388,8 @@
mStartTime = SystemClock.elapsedRealtime();
mLatencyBaseTime = mStartTime;
mRequestCount = 0;
- mPresentationStatsEventLogger = PresentationStatsEventLogger.forSessionId(sessionId);
+ mPresentationStatsEventLogger = PresentationStatsEventLogger.createPresentationLog(
+ sessionId, uid);
mFillRequestEventLogger = FillRequestEventLogger.forSessionId(sessionId);
mFillResponseEventLogger = FillResponseEventLogger.forSessionId(sessionId);
mSessionCommittedEventLogger = SessionCommittedEventLogger.forSessionId(sessionId);
@@ -4206,6 +4227,8 @@
if (viewState.getResponse() != null) {
FillResponse response = viewState.getResponse();
mPresentationStatsEventLogger.maybeSetRequestId(response.getRequestId());
+ mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId(
+ mFieldClassificationIdSnapshot);
mPresentationStatsEventLogger.maybeSetAvailableCount(
response.getDatasets(), mCurrentViewId);
}
@@ -4375,6 +4398,8 @@
public void onFillReady(@NonNull FillResponse response, @NonNull AutofillId filledId,
@Nullable AutofillValue value, int flags) {
synchronized (mLock) {
+ mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId(
+ mFieldClassificationIdSnapshot);
if (mDestroyed) {
Slog.w(TAG, "Call to Session#onFillReady() rejected - session: "
+ id + " destroyed");
@@ -5266,6 +5291,7 @@
List<Dataset> datasetList = newResponse.getDatasets();
+ mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId(sIdCounterForPcc.get());
mPresentationStatsEventLogger.maybeSetAvailableCount(datasetList, mCurrentViewId);
mFillResponseEventLogger.maybeSetDatasetsCountAfterPotentialPccFiltering(datasetList);
@@ -6308,7 +6334,7 @@
return serviceInfo == null ? Process.INVALID_UID : serviceInfo.applicationInfo.uid;
}
- // FieldClassificationServiceCallbacks
+ // FieldClassificationServiceCallbacks start
public void onClassificationRequestSuccess(@Nullable FieldClassificationResponse response) {
mClassificationState.updateResponseReceived(response);
}
@@ -6329,6 +6355,28 @@
// forceRemoveFromServiceLocked();
}
}
- // DetectionServiceCallbacks end
+
+ @Override
+ public void logFieldClassificationEvent(
+ long startTime, FieldClassificationResponse response,
+ @FieldClassificationEventLogger.FieldClassificationStatus int status) {
+ final FieldClassificationEventLogger logger = FieldClassificationEventLogger.createLogger();
+ logger.startNewLogForRequest();
+ logger.maybeSetLatencyMillis(
+ SystemClock.elapsedRealtime() - startTime);
+ logger.maybeSetAppPackageUid(uid);
+ logger.maybeSetNextFillRequestId(mFillRequestIdSnapshot + 1);
+ logger.maybeSetRequestId(sIdCounterForPcc.get());
+ logger.maybeSetSessionId(id);
+ int count = -1;
+ if (response != null) {
+ count = response.getClassifications().size();
+ }
+ logger.maybeSetRequestStatus(status);
+ logger.maybeSetCountClassifications(count);
+ logger.logAndEndEvent();
+ mFillRequestIdSnapshot = DEFAULT__FILL_REQUEST_ID_SNAPSHOT;
+ }
+ // FieldClassificationServiceCallbacks end
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/BottomSheetLayout.java b/services/autofill/java/com/android/server/autofill/ui/BottomSheetLayout.java
new file mode 100644
index 0000000..06bbc2b
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/ui/BottomSheetLayout.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2023 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.autofill.ui;
+
+import static com.android.server.autofill.Helper.sDebug;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.Slog;
+import android.widget.LinearLayout;
+
+import com.android.internal.R;
+
+/**
+ {@link LinearLayout} that displays content of save dialog.
+ */
+public class BottomSheetLayout extends LinearLayout {
+
+ private static final String TAG = "BottomSheetLayout";
+
+ public BottomSheetLayout(Context context) {
+ super(context);
+ }
+
+ public BottomSheetLayout(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public BottomSheetLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ public void onMeasure(int widthSpec, int heightSpec) {
+ if (getContext() == null || getContext().getResources() == null) {
+ super.onMeasure(widthSpec, heightSpec);
+ Slog.w(TAG, "onMeasure failed due to missing context or missing resources.");
+ return;
+ }
+
+ if (getChildCount() == 0) {
+ // Should not happen
+ super.onMeasure(widthSpec, heightSpec);
+ Slog.wtf(TAG, "onMeasure failed due to missing children views.");
+ return;
+ }
+
+ DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
+
+ final int pxOffset = getContext().getResources().getDimensionPixelSize(
+ R.dimen.autofill_dialog_offset);
+ final int outerMargin = getContext().getResources().getDimensionPixelSize(
+ R.dimen.autofill_save_outer_margin);
+
+ final boolean includeHorizontalSpace =
+ getContext().getResources().getBoolean(
+ R.bool.autofill_dialog_horizontal_space_included);
+
+
+ final int screenHeight = displayMetrics.heightPixels;
+ final int screenWidth = displayMetrics.widthPixels;
+
+ final int maxHeight = screenHeight - pxOffset - outerMargin;
+
+ int maxWidth = screenWidth;
+
+ if (includeHorizontalSpace) {
+ maxWidth -= 2 * pxOffset;
+ }
+
+ maxWidth =
+ Math.min(maxWidth, getContext().getResources().getDimensionPixelSize(
+ R.dimen.autofill_dialog_max_width));
+
+ super.onMeasure(MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST));
+
+
+ if (sDebug) {
+ Slog.d(TAG, "onMeasure() values in dp:"
+ + " screenHeight: " + screenHeight / displayMetrics.density + ", screenWidth: "
+ + screenWidth / displayMetrics.density
+ + ", maxHeight: " + maxHeight / displayMetrics.density
+ + ", maxWidth: " + maxWidth / displayMetrics.density + ", getMeasuredWidth(): "
+ + getMeasuredWidth() / displayMetrics.density + ", getMeasuredHeight(): "
+ + getMeasuredHeight() / displayMetrics.density);
+ }
+ setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight());
+ }
+
+
+}
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index 70382f1..3581db2 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -48,7 +48,6 @@
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.util.ArraySet;
-import android.util.DisplayMetrics;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -363,13 +362,6 @@
window.setCloseOnTouchOutside(true);
final WindowManager.LayoutParams params = window.getAttributes();
- DisplayMetrics displayMetrics = new DisplayMetrics();
- window.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
- final int screenWidth = displayMetrics.widthPixels;
- final int maxWidth =
- context.getResources().getDimensionPixelSize(R.dimen.autofill_dialog_max_width);
- params.width = Math.min(screenWidth, maxWidth);
-
params.accessibilityTitle = context.getString(R.string.autofill_save_accessibility_title);
params.windowAnimations = R.style.AutofillSaveAnimation;
params.setTrustedOverlay();
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 5493da6..5bb0f86 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -486,6 +486,13 @@
File baseStateDir,
File dataDir,
TransportManager transportManager) {
+ // check if we are past the retention period for BMM Events,
+ // if so delete expired events and do not print them to dumpsys
+ BackupManagerMonitorDumpsysUtils backupManagerMonitorDumpsysUtils =
+ new BackupManagerMonitorDumpsysUtils();
+ if (backupManagerMonitorDumpsysUtils.deleteExpiredBMMEvents() && DEBUG){
+ Slog.d(TAG, "BMM Events recorded for dumpsys have expired");
+ }
return new UserBackupManagerService(
userId,
context,
@@ -654,6 +661,13 @@
// the pending backup set
mBackupHandler.postDelayed(this::parseLeftoverJournals, INITIALIZATION_DELAY_MILLIS);
+ // check if we are past the retention period for BMM Events,
+ // if so delete expired events and do not print them to dumpsys
+ BackupManagerMonitorDumpsysUtils backupManagerMonitorDumpsysUtils =
+ new BackupManagerMonitorDumpsysUtils();
+ mBackupHandler.postDelayed(backupManagerMonitorDumpsysUtils::deleteExpiredBMMEvents,
+ INITIALIZATION_DELAY_MILLIS);
+
mBackupPreferences = new UserBackupPreferences(mContext, mBaseStateDir);
// Power management
@@ -4177,7 +4191,19 @@
private void dumpBMMEvents(PrintWriter pw) {
BackupManagerMonitorDumpsysUtils bm =
new BackupManagerMonitorDumpsysUtils();
+ if (bm.deleteExpiredBMMEvents()) {
+ pw.println("BACKUP MANAGER MONITOR EVENTS HAVE EXPIRED");
+ return;
+ }
File events = bm.getBMMEventsFile();
+ if (events.length() == 0){
+ // We have not recorded BMMEvents yet.
+ pw.println("NO BACKUP MANAGER MONITOR EVENTS");
+ return;
+ } else if (bm.isFileLargerThanSizeLimit(events)){
+ pw.println("BACKUP MANAGER MONITOR EVENTS FILE OVER SIZE LIMIT - "
+ + "future events will not be recorded");
+ }
pw.println("START OF BACKUP MANAGER MONITOR EVENTS");
try (BufferedReader reader = new BufferedReader(new FileReader(events))) {
String line;
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index e04bf11..bbec79d 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -394,6 +394,20 @@
// If we're starting a full-system restore, set up to begin widget ID remapping
if (mIsSystemRestore) {
AppWidgetBackupBridge.systemRestoreStarting(mUserId);
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_START_SYSTEM_RESTORE,
+ null,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ monitoringExtras);
+ } else {
+ //We are either performing RestoreAtInstall or Bmgr.
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_START_RESTORE_AT_INSTALL,
+ null,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ monitoringExtras);
}
try {
@@ -421,6 +435,12 @@
mStatus = transport.startRestore(mToken, packages);
if (mStatus != BackupTransport.TRANSPORT_OK) {
Slog.e(TAG, "Transport error " + mStatus + "; no restore possible");
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_DURING_START_RESTORE,
+ mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT,
+ monitoringExtras);
mStatus = BackupTransport.TRANSPORT_ERROR;
executeNextState(UnifiedRestoreState.FINAL);
return;
@@ -528,6 +548,12 @@
final String pkgName = (mRestoreDescription != null)
? mRestoreDescription.getPackageName() : null;
if (pkgName == null) {
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_CANNOT_GET_NEXT_PKG_NAME,
+ null,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT,
+ monitoringExtras);
Slog.e(TAG, "Failure getting next package name");
EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
nextState = UnifiedRestoreState.FINAL;
@@ -550,6 +576,14 @@
Metadata metaInfo = mPmAgent.getRestoredMetadata(pkgName);
if (metaInfo == null) {
+ PackageInfo pkgInfo = new PackageInfo();
+ pkgInfo.packageName = pkgName;
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_PM_AGENT_HAS_NO_METADATA,
+ pkgInfo,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ monitoringExtras);
Slog.e(TAG, "No metadata for " + pkgName);
EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName,
"Package metadata missing");
@@ -560,6 +594,13 @@
try {
mCurrentPackage = backupManagerService.getPackageManager().getPackageInfoAsUser(
pkgName, PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_START_PACKAGE_RESTORE,
+ mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ monitoringExtras);
+
} catch (NameNotFoundException e) {
// Whoops, we thought we could restore this package but it
// turns out not to be present. Skip it.
@@ -641,12 +682,24 @@
} else {
// Unknown restore type; ignore this package and move on
Slog.e(TAG, "Unrecognized restore type " + type);
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);;
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_UNKNOWN_RESTORE_TYPE,
+ mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ monitoringExtras);
nextState = UnifiedRestoreState.RUNNING_QUEUE;
return;
}
} catch (Exception e) {
Slog.e(TAG, "Can't get next restore target from transport; halting: "
+ e.getMessage());
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);;
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_NO_NEXT_RESTORE_TARGET,
+ mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ monitoringExtras);
EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
nextState = UnifiedRestoreState.FINAL;
return;
@@ -663,6 +716,10 @@
final String packageName = mCurrentPackage.packageName;
// Validate some semantic requirements that apply in this way
// only to the key/value restore API flow
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_KV_RESTORE, mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ /*monitoringExtras*/ addRestoreOperationTypeToEvent(/*extras*/null));
if (mCurrentPackage.applicationInfo.backupAgentName == null
|| "".equals(mCurrentPackage.applicationInfo.backupAgentName)) {
if (MORE_DEBUG) {
@@ -721,6 +778,11 @@
++mCount;
} catch (Exception e) {
Slog.e(TAG, "Error when attempting restore: " + e.toString());
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_KV_AGENT_ERROR, mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT,
+ monitoringExtras);
keyValueAgentErrorCleanup(false);
executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
}
@@ -759,6 +821,12 @@
// Transport-level failure. This failure could be specific to package currently in
// restore.
Slog.e(TAG, "Error getting restore data for " + packageName);
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_KV_RESTORE,
+ mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT,
+ monitoringExtras);
EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
stage.close();
downloadFile.delete();
@@ -815,6 +883,11 @@
new ArrayList<>(getExcludedKeysForPackage(packageName)));
} catch (Exception e) {
Slog.e(TAG, "Unable to call app for restore: " + packageName, e);
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_KV_AGENT_ERROR, mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT,
+ monitoringExtras);
EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
packageName, e.toString());
// Clears any pending timeout messages as well.
@@ -888,6 +961,10 @@
//
// When finished, StreamFeederThread executes next state as appropriate on the
// backup looper, and the overall unified restore task resumes
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE, mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ /*monitoringExtras*/ addRestoreOperationTypeToEvent(/*extras*/null));
try {
StreamFeederThread feeder = new StreamFeederThread();
if (MORE_DEBUG) {
@@ -903,6 +980,11 @@
// current target. We haven't asked the transport for data yet, though,
// so we can do that simply by going back to running the restore queue.
Slog.e(TAG, "Unable to construct pipes for stream restore!");
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_NO_FEEDER_THREAD, mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ monitoringExtras);
executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
}
}
@@ -927,6 +1009,10 @@
} catch (Exception e) {
final String packageName = mCurrentPackage.packageName;
Slog.e(TAG, "Unable to finalize restore of " + packageName);
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_AGENT_FAILURE, mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT, monitoringExtras);
EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
packageName, e.toString());
keyValueAgentErrorCleanup(true);
@@ -1020,6 +1106,12 @@
// handling will deal properly with that.
Slog.e(TAG, "Error " + result + " streaming restore for "
+ mCurrentPackage.packageName);
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_FULL_RESTORE,
+ mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT,
+ monitoringExtras);
EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
status = result;
}
@@ -1032,6 +1124,12 @@
// but potentially recoverable; abandon this package's restore but
// carry on with the next restore target.
Slog.e(TAG, "Unable to route data for restore");
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_FULL_AGENT_ERROR,
+ mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT,
+ monitoringExtras);
EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
mCurrentPackage.packageName, "I/O error on pipes");
status = BackupTransport.AGENT_ERROR;
@@ -1040,6 +1138,12 @@
// the sockets will wake up the engine and it will then tidy up the
// remote end.
Slog.e(TAG, "Transport failed during restore: " + e.getMessage());
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_FULL_RESTORE,
+ mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT,
+ monitoringExtras);
EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
status = BackupTransport.TRANSPORT_ERROR;
} finally {
@@ -1213,6 +1317,13 @@
}
Slog.i(TAG, "Restore complete.");
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_RESTORE_COMPLETE,
+ null,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ monitoringExtras);
+
mListener.onFinished(callerLogString);
}
@@ -1313,6 +1424,7 @@
@Override
public void operationComplete(long unusedResult) {
mOperationStorage.removeOperation(mEphemeralOpToken);
+
if (MORE_DEBUG) {
Slog.i(TAG, "operationComplete() during restore: target="
+ mCurrentPackage.packageName
@@ -1341,6 +1453,11 @@
// Okay, we're done with this package. Tidy up and go on to the next
// app in the queue.
int size = (int) mBackupDataName.length();
+ Bundle monitoringExtras = addRestoreOperationTypeToEvent(/*extras*/null);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_RESTORE_FINISHED, mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ monitoringExtras);
EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE,
mCurrentPackage.packageName, size);
diff --git a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtils.java b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtils.java
index 0b55ca2..797aed9 100644
--- a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtils.java
@@ -23,15 +23,22 @@
import android.os.Environment;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FastPrintWriter;
+import java.io.BufferedReader;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Map;
+import java.util.concurrent.TimeUnit;
/*
@@ -46,12 +53,27 @@
// Name of the subdirectory where the text file containing the BMM events will be stored.
// Same as {@link UserBackupManagerFiles}
private static final String BACKUP_PERSISTENT_DIR = "backup";
+ private static final String INITIAL_SETUP_TIMESTAMP_KEY = "initialSetupTimestamp";
+ // Retention period of 60 days (in millisec) for the BMM Events.
+ // After tha time has passed the text file containing the BMM events will be emptied
+ private static final long LOGS_RETENTION_PERIOD_MILLISEC = 60 * TimeUnit.DAYS.toMillis(1);
+ // Size limit for the text file containing the BMM events
+ private static final long BMM_FILE_SIZE_LIMIT_BYTES = 25 * 1024 * 1000; // 2.5 MB
+
+ // We cache the value of IsAfterRetentionPeriod() to avoid unnecessary disk I/O
+ // mIsAfterRetentionPeriodCached tracks if we have cached the value of IsAfterRetentionPeriod()
+ private boolean mIsAfterRetentionPeriodCached = false;
+ // The cached value of IsAfterRetentionPeriod()
+ private boolean mIsAfterRetentionPeriod;
+ // If isFileLargerThanSizeLimit(bmmEvents) returns true we cache the value to avoid
+ // unnecessary disk I/O
+ private boolean mIsFileLargerThanSizeLimit = false;
/**
* Parses the BackupManagerMonitor bundle for a RESTORE event in a series of strings that
* will be persisted in a text file and printed in the dumpsys.
*
- * If the evenntBundle passed is not a RESTORE event, return early
+ * If the eventBundle passed is not a RESTORE event, return early
*
* Key information related to the event:
* - Timestamp (HAS TO ALWAYS BE THE FIRST LINE OF EACH EVENT)
@@ -62,17 +84,22 @@
* - Agent logs (if available)
*
* Example of formatting:
- * RESTORE Event: [2023-08-18 17:16:00.735] Agent - Agent logging results
- * Package name: com.android.wallpaperbackup
- * Agent Logs:
- * Data Type: wlp_img_system
- * Item restored: 0/1
- * Agent Error - Category: no_wallpaper, Count: 1
- * Data Type: wlp_img_lock
- * Item restored: 0/1
- * Agent Error - Category: no_wallpaper, Count: 1
+ * [2023-09-21 14:43:33.824] - Agent logging results
+ * Package: com.android.wallpaperbackup
+ * Agent Logs:
+ * Data Type: wlp_img_system
+ * Item restored: 0/1
+ * Agent Error - Category: no_wallpaper, Count: 1
+ * Data Type: wlp_img_lock
+ * Item restored: 0/1
+ * Agent Error - Category: no_wallpaper, Count: 1
*/
public void parseBackupManagerMonitorRestoreEventForDumpsys(Bundle eventBundle) {
+ if (isAfterRetentionPeriod()) {
+ // We only log data for the first 60 days since setup
+ return;
+ }
+
if (eventBundle == null) {
return;
}
@@ -89,8 +116,19 @@
}
File bmmEvents = getBMMEventsFile();
+ if (bmmEvents.length() == 0) {
+ // We are parsing the first restore event.
+ // Time to also record the setup timestamp of the device
+ recordSetUpTimestamp();
+ }
+
+ if(isFileLargerThanSizeLimit(bmmEvents)){
+ // Do not write more events if the file is over size limit
+ return;
+ }
+
try (FileOutputStream out = new FileOutputStream(bmmEvents, /*append*/ true);
- PrintWriter pw = new FastPrintWriter(out);) {
+ PrintWriter pw = new FastPrintWriter(out);) {
int eventCategory = eventBundle.getInt(BackupManagerMonitor.EXTRA_LOG_EVENT_CATEGORY);
int eventId = eventBundle.getInt(BackupManagerMonitor.EXTRA_LOG_EVENT_ID);
@@ -101,17 +139,16 @@
return;
}
- pw.println("RESTORE Event: [" + timestamp() + "] " +
- getCategory(eventCategory) + " - " +
- getId(eventId));
+ pw.println("[" + timestamp() + "] - " + getId(eventId));
if (eventBundle.containsKey(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_NAME)) {
- pw.println("\tPackage name: "
+ pw.println("\tPackage: "
+ eventBundle.getString(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_NAME));
}
// TODO(b/296818666): add extras to the events
addAgentLogsIfAvailable(eventBundle, pw);
+ addExtrasIfAvailable(eventBundle, pw);
} catch (java.io.IOException e) {
Slog.e(TAG, "IO Exception when writing BMM events to file: " + e);
}
@@ -156,6 +193,37 @@
}
}
+ /**
+ * Extracts some extras (defined in BackupManagerMonitor as EXTRA_LOG_<description>)
+ * from the BackupManagerMonitor event. Not all extras have the same importance. For now only
+ * focus on extras relating to version mismatches between packages on the source and target.
+ *
+ * When an event with ID LOG_EVENT_ID_RESTORE_VERSION_HIGHER (trying to restore from higher to
+ * lower version of a package) parse:
+ * EXTRA_LOG_RESTORE_VERSION [int]: the version of the package on the source
+ * EXTRA_LOG_RESTORE_ANYWAY [bool]: if the package allows restore any version
+ * EXTRA_LOG_RESTORE_VERSION_TARGET [int]: an extra to record the package version on the target
+ */
+ private void addExtrasIfAvailable(Bundle eventBundle, PrintWriter pw) {
+ if (eventBundle.getInt(BackupManagerMonitor.EXTRA_LOG_EVENT_ID) ==
+ BackupManagerMonitor.LOG_EVENT_ID_RESTORE_VERSION_HIGHER) {
+ if (eventBundle.containsKey(BackupManagerMonitor.EXTRA_LOG_RESTORE_ANYWAY)) {
+ pw.println("\t\tPackage supports RestoreAnyVersion: "
+ + eventBundle.getBoolean(BackupManagerMonitor.EXTRA_LOG_RESTORE_ANYWAY));
+ }
+ if (eventBundle.containsKey(BackupManagerMonitor.EXTRA_LOG_RESTORE_VERSION)) {
+ pw.println("\t\tPackage version on source: "
+ + eventBundle.getLong(BackupManagerMonitor.EXTRA_LOG_RESTORE_VERSION));
+ }
+ if (eventBundle.containsKey(
+ BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION)) {
+ pw.println("\t\tPackage version on target: "
+ + eventBundle.getLong(
+ BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION));
+ }
+ }
+ }
+
/*
* Get the path of the text files which stores the BMM events
*/
@@ -165,6 +233,13 @@
return fname;
}
+ public boolean isFileLargerThanSizeLimit(File events){
+ if (!mIsFileLargerThanSizeLimit) {
+ mIsFileLargerThanSizeLimit = events.length() > getBMMEventsFileSizeLimit();
+ }
+ return mIsFileLargerThanSizeLimit;
+ }
+
private String timestamp() {
long currentTime = System.currentTimeMillis();
Date date = new Date(currentTime);
@@ -245,6 +320,32 @@
case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED ->
"Transport non-incremental backup required";
case BackupManagerMonitor.LOG_EVENT_ID_AGENT_LOGGING_RESULTS -> "Agent logging results";
+ case BackupManagerMonitor.LOG_EVENT_ID_START_SYSTEM_RESTORE -> "Start system restore";
+ case BackupManagerMonitor.LOG_EVENT_ID_START_RESTORE_AT_INSTALL ->
+ "Start restore at install";
+ case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_DURING_START_RESTORE ->
+ "Transport error during start restore";
+ case BackupManagerMonitor.LOG_EVENT_ID_CANNOT_GET_NEXT_PKG_NAME ->
+ "Cannot get next package name";
+ case BackupManagerMonitor.LOG_EVENT_ID_UNKNOWN_RESTORE_TYPE -> "Unknown restore type";
+ case BackupManagerMonitor.LOG_EVENT_ID_KV_RESTORE -> "KV restore";
+ case BackupManagerMonitor.LOG_EVENT_ID_FULL_RESTORE -> "Full restore";
+ case BackupManagerMonitor.LOG_EVENT_ID_NO_NEXT_RESTORE_TARGET ->
+ "No next restore target";
+ case BackupManagerMonitor.LOG_EVENT_ID_KV_AGENT_ERROR -> "KV agent error";
+ case BackupManagerMonitor.LOG_EVENT_ID_PACKAGE_RESTORE_FINISHED ->
+ "Package restore finished";
+ case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_KV_RESTORE ->
+ "Transport error KV restore";
+ case BackupManagerMonitor.LOG_EVENT_ID_NO_FEEDER_THREAD -> "No feeder thread";
+ case BackupManagerMonitor.LOG_EVENT_ID_FULL_AGENT_ERROR -> "Full agent error";
+ case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_ERROR_FULL_RESTORE ->
+ "Transport error full restore";
+ case BackupManagerMonitor.LOG_EVENT_ID_RESTORE_COMPLETE -> "Restore complete";
+ case BackupManagerMonitor.LOG_EVENT_ID_START_PACKAGE_RESTORE ->
+ "Start package restore";
+ case BackupManagerMonitor.LOG_EVENT_ID_AGENT_FAILURE ->
+ "Agent failure";
default -> "Unknown log event ID: " + code;
};
return id;
@@ -257,4 +358,128 @@
default -> false;
};
}
+
+ /**
+ * Store the timestamp when the device was set up (date when the first BMM event is parsed)
+ * in a text file.
+ */
+ @VisibleForTesting
+ void recordSetUpTimestamp() {
+ File setupDateFile = getSetUpDateFile();
+ // record setup timestamp only once
+ if (setupDateFile.length() == 0) {
+ try (FileOutputStream out = new FileOutputStream(setupDateFile, /*append*/ true);
+ PrintWriter pw = new FastPrintWriter(out);) {
+ long currentDate = System.currentTimeMillis();
+ pw.println(currentDate);
+ } catch (IOException e) {
+ Slog.w(TAG, "An error occurred while recording the setup date: "
+ + e.getMessage());
+ }
+ }
+
+ }
+
+ @VisibleForTesting
+ String getSetUpDate() {
+ File fname = getSetUpDateFile();
+ try (FileInputStream inputStream = new FileInputStream(fname);
+ InputStreamReader reader = new InputStreamReader(inputStream);
+ BufferedReader bufferedReader = new BufferedReader(reader);) {
+ return bufferedReader.readLine();
+ } catch (Exception e) {
+ Slog.w(TAG, "An error occurred while reading the date: " + e.getMessage());
+ return "Could not retrieve setup date";
+ }
+ }
+
+ @VisibleForTesting
+ static boolean isDateAfterNMillisec(long startTimeStamp, long endTimeStamp, long millisec) {
+ if (startTimeStamp > endTimeStamp) {
+ // Something has gone wrong, timeStamp1 should always precede timeStamp2.
+ // Out of caution return true: we would delete the logs rather than
+ // risking them being kept for longer than the retention period
+ return true;
+ }
+ long timeDifferenceMillis = endTimeStamp - startTimeStamp;
+ return (timeDifferenceMillis >= millisec);
+ }
+
+ /**
+ * Check if current date is after retention period
+ */
+ @VisibleForTesting
+ boolean isAfterRetentionPeriod() {
+ if (mIsAfterRetentionPeriodCached) {
+ return mIsAfterRetentionPeriod;
+ } else {
+ File setUpDateFile = getSetUpDateFile();
+ if (setUpDateFile.length() == 0) {
+ // We are yet to record a setup date. This means we haven't parsed the first event.
+ mIsAfterRetentionPeriod = false;
+ mIsAfterRetentionPeriodCached = true;
+ return false;
+ }
+ try {
+ long setupTimestamp = Long.parseLong(getSetUpDate());
+ long currentTimestamp = System.currentTimeMillis();
+ mIsAfterRetentionPeriod = isDateAfterNMillisec(setupTimestamp, currentTimestamp,
+ getRetentionPeriodInMillisec());
+ mIsAfterRetentionPeriodCached = true;
+ return mIsAfterRetentionPeriod;
+ } catch (NumberFormatException e) {
+ // An error occurred when parsing the setup timestamp.
+ // Out of caution return true: we would delete the logs rather than
+ // risking them being kept for longer than the retention period
+ mIsAfterRetentionPeriod = true;
+ mIsAfterRetentionPeriodCached = true;
+ return true;
+ }
+ }
+ }
+
+ @VisibleForTesting
+ File getSetUpDateFile() {
+ File dataDir = new File(Environment.getDataDirectory(), BACKUP_PERSISTENT_DIR);
+ File setupDateFile = new File(dataDir, INITIAL_SETUP_TIMESTAMP_KEY + ".txt");
+ return setupDateFile;
+ }
+
+ @VisibleForTesting
+ long getRetentionPeriodInMillisec() {
+ return LOGS_RETENTION_PERIOD_MILLISEC;
+ }
+
+ @VisibleForTesting
+ long getBMMEventsFileSizeLimit(){
+ return BMM_FILE_SIZE_LIMIT_BYTES;
+ }
+
+ /**
+ * Delete the BMM Events file after the retention period has passed.
+ *
+ * @return true if the retention period has passed false otherwise.
+ * we want to return true even if we were unable to delete the file, as this will prevent
+ * expired BMM events from being printed to the dumpsys
+ */
+ public boolean deleteExpiredBMMEvents() {
+ try {
+ if (isAfterRetentionPeriod()) {
+ File bmmEvents = getBMMEventsFile();
+ if (bmmEvents.exists()) {
+ if (bmmEvents.delete()) {
+ Slog.i(TAG, "Deleted expired BMM Events");
+ } else {
+ Slog.e(TAG, "Unable to delete expired BMM Events");
+ }
+ }
+ return true;
+ }
+ return false;
+ } catch (Exception e) {
+ // Handle any unexpected exceptions
+ // To be safe we return true as we want to avoid exposing expired BMMEvents
+ return true;
+ }
+ }
}
diff --git a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorEventSender.java b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorEventSender.java
index 92e3107..549d08c0 100644
--- a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorEventSender.java
+++ b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorEventSender.java
@@ -222,4 +222,21 @@
extras.putBoolean(key, value);
return extras;
}
+
+ /**
+ * Adds given key-value pair in the bundle and returns the bundle. If bundle was null it will
+ * be created.
+ *
+ * @param extras - bundle where to add key-value to, if null a new bundle will be created.
+ * @param key - key.
+ * @param value - value.
+ * @return extras if it was not null and new bundle otherwise.
+ */
+ public static Bundle putMonitoringExtra(Bundle extras, String key, int value) {
+ if (extras == null) {
+ extras = new Bundle();
+ }
+ extras.putInt(key, value);
+ return extras;
+ }
}
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index d94f4f2..556eba6 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -167,6 +167,8 @@
private int mBatteryNearlyFullLevel;
private int mShutdownBatteryTemperature;
+ private static String sSystemUiPackage;
+
private int mPlugType;
private int mLastPlugType = -1; // Extra state so we can detect first run
@@ -228,6 +230,8 @@
com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
mShutdownBatteryTemperature = mContext.getResources().getInteger(
com.android.internal.R.integer.config_shutdownBatteryTemperature);
+ sSystemUiPackage = mContext.getResources().getString(
+ com.android.internal.R.string.config_systemUi);
mBatteryLevelsEventQueue = new ArrayDeque<>();
mMetricsLogger = new MetricsLogger();
@@ -750,8 +754,21 @@
+ ", info:" + mHealthInfo.toString());
}
- mHandler.post(() -> ActivityManager.broadcastStickyIntent(intent, AppOpsManager.OP_NONE,
- mBatteryChangedOptions, UserHandle.USER_ALL));
+ mHandler.post(() -> broadcastBatteryChangedIntent(intent, mBatteryChangedOptions));
+ }
+
+ private static void broadcastBatteryChangedIntent(Intent intent, Bundle options) {
+ // TODO (293959093): It is important that SystemUI receives this broadcast as soon as
+ // possible. Ideally, it should be using binder callbacks but until then, dispatch this
+ // as a foreground broadcast to SystemUI.
+ final Intent fgIntent = new Intent(intent);
+ fgIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ fgIntent.setPackage(sSystemUiPackage);
+ ActivityManager.broadcastStickyIntent(fgIntent, AppOpsManager.OP_NONE,
+ options, UserHandle.USER_ALL);
+
+ ActivityManager.broadcastStickyIntent(intent, new String[] {sSystemUiPackage},
+ AppOpsManager.OP_NONE, options, UserHandle.USER_ALL);
}
private void sendBatteryLevelChangedIntentLocked() {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 47abc10..43dc307 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -314,7 +314,6 @@
import android.net.Proxy;
import android.net.Uri;
import android.os.AppZygote;
-import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.BinderProxy;
@@ -15030,16 +15029,6 @@
}
}
- // STOPSHIP(b/298884211): Remove this logging
- if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
- final int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
- if (level < 0) {
- Slog.wtf(BroadcastQueue.TAG, "Unexpected broadcast: " + intent
- + "; callingUid: " + callingUid + ", callingPid: " + callingPid,
- new Throwable());
- }
- }
-
int[] users;
if (userId == UserHandle.USER_ALL) {
// Caller wants broadcast to go to all started users.
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index d5343a9..5d0fefc 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -59,7 +59,6 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.os.BatteryManager;
import android.os.Bundle;
import android.os.BundleMerger;
import android.os.Handler;
@@ -1075,16 +1074,6 @@
queue.lastProcessState = app.mState.getCurProcState();
if (receiver instanceof BroadcastFilter) {
notifyScheduleRegisteredReceiver(app, r, (BroadcastFilter) receiver);
- // STOPSHIP(b/298884211): Remove this logging
- if (Intent.ACTION_BATTERY_CHANGED.equals(receiverIntent.getAction())) {
- int level = receiverIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
- if (level < 0) {
- Slog.wtf(TAG, "Dispatching unexpected broadcast: " + receiverIntent
- + " to " + receiver
- + "; callingUid: " + r.callingUid
- + ", callingPid: " + r.callingPid);
- }
- }
thread.scheduleRegisteredReceiver(
((BroadcastFilter) receiver).receiverList.receiver,
receiverIntent, r.resultCode, r.resultData, r.resultExtras,
diff --git a/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java b/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
index e0a71d4..caafb42 100644
--- a/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
+++ b/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java
@@ -220,6 +220,24 @@
final ArrayList<Long> timestampsFound = new ArrayList<>();
for (int i = 0, size = apiTypes.size(); i < size; i++) {
final int apiType = apiTypes.get(i);
+
+ // remove the FGS record from the stack
+ final ArrayMap<ComponentName, ServiceRecord> runningFgsOfType =
+ uidState.mRunningFgs.get(apiType);
+ if (runningFgsOfType == null) {
+ Slog.w(TAG, "Could not find appropriate running FGS for FGS stop for UID " + uid
+ + " in package " + record.packageName);
+ continue;
+ }
+
+ runningFgsOfType.remove(record.getComponentName());
+ if (runningFgsOfType.size() == 0) {
+ // there's no more FGS running for this type, just get rid of it
+ uidState.mRunningFgs.remove(apiType);
+ // but we need to keep track of the timestamp in case an API stops
+ uidState.mLastFgsTimeStamp.put(apiType, System.currentTimeMillis());
+ }
+
final int apiTypeIndex = uidState.mOpenWithFgsCount.indexOfKey(apiType);
if (apiTypeIndex < 0) {
Slog.w(TAG, "Logger should be tracking FGS types correctly for UID " + uid
@@ -238,22 +256,6 @@
// remove the last API close call
uidState.mApiClosedCalls.remove(apiType);
}
- // remove the FGS record from the stack
- final ArrayMap<ComponentName, ServiceRecord> runningFgsOfType =
- uidState.mRunningFgs.get(apiType);
- if (runningFgsOfType == null) {
- Slog.w(TAG, "Could not find appropriate running FGS for FGS stop for UID " + uid
- + " in package " + record.packageName);
- continue;
- }
-
- runningFgsOfType.remove(record.getComponentName());
- if (runningFgsOfType.size() == 0) {
- // there's no more FGS running for this type, just get rid of it
- uidState.mRunningFgs.remove(apiType);
- // but we need to keep track of the timestamp in case an API stops
- uidState.mLastFgsTimeStamp.put(apiType, System.currentTimeMillis());
- }
}
if (!apisFound.isEmpty()) {
// time to log the call
@@ -383,9 +385,14 @@
// initialize if we don't contain
uidState.mOpenedWithoutFgsCount.put(apiType, 0);
}
- if (uidState.mOpenedWithoutFgsCount.get(apiType) != 0) {
+ int apiOpenWithoutFgsCount = uidState.mOpenedWithoutFgsCount.get(apiType);
+ if (apiOpenWithoutFgsCount != 0) {
+ apiOpenWithoutFgsCount -= 1;
+ if (apiOpenWithoutFgsCount == 0) {
+ uidState.mApiOpenCalls.remove(apiType);
+ }
uidState.mOpenedWithoutFgsCount
- .put(apiType, uidState.mOpenedWithoutFgsCount.get(apiType) - 1);
+ .put(apiType, apiOpenWithoutFgsCount);
return System.currentTimeMillis();
}
// This is a part of a valid active FGS
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index 0c9cb3b..675bb87 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -418,59 +418,6 @@
}
}
- public enum FrameRate {
- FPS_DEFAULT(0),
- FPS_30(30),
- FPS_36(36),
- FPS_40(40),
- FPS_45(45),
- FPS_48(48),
- FPS_60(60),
- FPS_72(72),
- FPS_90(90),
- FPS_120(120),
- FPS_144(144),
- FPS_INVALID(-1);
-
- public final int fps;
-
- FrameRate(int fps) {
- this.fps = fps;
- }
- }
-
- // Turn the raw string to the corresponding fps int.
- // Return 0 when disabling, -1 for invalid fps.
- static int getFpsInt(String raw) {
- // TODO(b/243448953): make sure this translates to proper values based on current display
- switch (raw) {
- case "30":
- return FrameRate.FPS_30.fps;
- case "36":
- return FrameRate.FPS_36.fps;
- case "40":
- return FrameRate.FPS_40.fps;
- case "45":
- return FrameRate.FPS_45.fps;
- case "48":
- return FrameRate.FPS_48.fps;
- case "60":
- return FrameRate.FPS_60.fps;
- case "72":
- return FrameRate.FPS_72.fps;
- case "90":
- return FrameRate.FPS_90.fps;
- case "120":
- return FrameRate.FPS_120.fps;
- case "144":
- return FrameRate.FPS_144.fps;
- case "disable":
- case "":
- return FrameRate.FPS_DEFAULT.fps;
- }
- return FrameRate.FPS_INVALID.fps;
- }
-
/**
* Called by games to communicate the current state to the platform.
*
@@ -718,7 +665,12 @@
}
public synchronized int getFps() {
- return GameManagerService.getFpsInt(mFps);
+ try {
+ final int fpsInt = Integer.parseInt(mFps);
+ return fpsInt;
+ } catch (NumberFormatException e) {
+ return 0;
+ }
}
synchronized String getFpsStr() {
@@ -758,7 +710,12 @@
}
android.app.GameModeConfiguration toPublicGameModeConfig() {
- int fpsOverride = getFpsInt(mFps);
+ int fpsOverride;
+ try {
+ fpsOverride = Integer.parseInt(mFps);
+ } catch (NumberFormatException e) {
+ fpsOverride = 0;
+ }
// TODO(b/243448953): match to proper value in case of display change?
fpsOverride = fpsOverride > 0 ? fpsOverride
: android.app.GameModeConfiguration.FPS_OVERRIDE_NONE;
diff --git a/services/core/java/com/android/server/app/GameManagerShellCommand.java b/services/core/java/com/android/server/app/GameManagerShellCommand.java
index 00ff489..ab57c4f 100644
--- a/services/core/java/com/android/server/app/GameManagerShellCommand.java
+++ b/services/core/java/com/android/server/app/GameManagerShellCommand.java
@@ -241,8 +241,10 @@
case "--fps":
if (fpsStr == null) {
fpsStr = getNextArgRequired();
- if (fpsStr != null && GameManagerService.getFpsInt(fpsStr) == -1) {
- pw.println("Invalid frame rate '" + fpsStr + "'");
+ try {
+ Integer.parseInt(fpsStr);
+ } catch (NumberFormatException e) {
+ pw.println("Invalid frame rate: '" + fpsStr + "'");
return -1;
}
} else {
@@ -375,8 +377,8 @@
pw.println(" --downscale [0.3|0.35|0.4|0.45|0.5|0.55|0.6|0.65");
pw.println(" |0.7|0.75|0.8|0.85|0.9|disable]: Set app to run at the");
pw.println(" specified scaling ratio.");
- pw.println(" --fps [30|45|60|90|120|disable]: Set app to run at the specified fps,");
- pw.println(" if supported.");
+ pw.println(" --fps: Integer value to set app to run at the specified fps,");
+ pw.println(" if supported. 0 to disable.");
pw.println(" reset [--mode [2|3|performance|battery] --user <USER_ID>] <PACKAGE_NAME>");
pw.println(" Resets the game mode of the app to device configuration.");
pw.println(" This should only be used to reset any override to non custom game mode");
diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java
index 95b6c2c..81365bf 100644
--- a/services/core/java/com/android/server/audio/SoundDoseHelper.java
+++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java
@@ -719,7 +719,9 @@
/*package*/ void initSafeMediaVolumeIndex() {
for (int i = 0; i < mSafeMediaVolumeDevices.size(); ++i) {
int deviceType = mSafeMediaVolumeDevices.keyAt(i);
- mSafeMediaVolumeDevices.put(deviceType, getSafeDeviceMediaVolumeIndex(deviceType));
+ if (mSafeMediaVolumeDevices.valueAt(i) == SAFE_MEDIA_VOLUME_UNINITIALIZED) {
+ mSafeMediaVolumeDevices.put(deviceType, getSafeDeviceMediaVolumeIndex(deviceType));
+ }
}
}
@@ -743,7 +745,7 @@
}
/*package*/ boolean safeDevicesContains(int device) {
- return mSafeMediaVolumeDevices.indexOfKey(device) >= 0;
+ return mSafeMediaVolumeDevices.get(device, SAFE_MEDIA_VOLUME_UNINITIALIZED) >= 0;
}
/*package*/ void invalidatPendingVolumeCommand() {
@@ -1014,6 +1016,7 @@
initCsd();
synchronized (mSafeMediaVolumeStateLock) {
+ initSafeMediaVolumeIndex();
updateSafeMediaVolume_l(caller);
}
}
@@ -1065,11 +1068,18 @@
}
private int getSafeDeviceMediaVolumeIndex(int deviceType) {
- // legacy implementation uses mSafeMediaVolumeIndex for wired HS/HP
- // instead of computing it from the volume curves
- if ((deviceType == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE
- || deviceType == AudioSystem.DEVICE_OUT_WIRED_HEADSET) && !mEnableCsd.get()) {
- return mSafeMediaVolumeIndex;
+ if (!mEnableCsd.get()) {
+ // legacy hearing safety only for wired and USB HS/HP
+ if (deviceType == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE
+ || deviceType == AudioSystem.DEVICE_OUT_WIRED_HEADSET) {
+ // legacy hearing safety uses mSafeMediaVolumeIndex for wired HS/HP
+ // instead of computing it from the volume curves
+ return mSafeMediaVolumeIndex;
+ }
+
+ if (deviceType != AudioSystem.DEVICE_OUT_USB_HEADSET) {
+ return SAFE_MEDIA_VOLUME_UNINITIALIZED;
+ }
}
// determine UI volume index corresponding to the wanted safe gain in dBFS
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index add94b1..3aa087a 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -37,7 +37,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
-import android.app.TaskStackListener;
+import android.app.ActivityManagerInternal;
+import android.app.IProcessObserver;
import android.content.Context;
import android.hardware.devicestate.DeviceStateInfo;
import android.hardware.devicestate.DeviceStateManager;
@@ -184,7 +185,30 @@
private final SystemPropertySetter mSystemPropertySetter;
@VisibleForTesting
- TaskStackListener mOverrideRequestTaskStackListener = new OverrideRequestTaskStackListener();
+ final IProcessObserver mProcessObserver = new IProcessObserver.Stub() {
+ @Override
+ public void onForegroundActivitiesChanged(int pid, int uid, boolean fg) {
+ synchronized (mLock) {
+ if (!shouldCancelOverrideRequestWhenRequesterNotOnTop()) {
+ return;
+ }
+
+ OverrideRequest request = mActiveOverride.get();
+ if (pid != request.getPid() || uid != request.getUid()) {
+ return;
+ }
+ if (!fg) {
+ mOverrideRequestController.cancelRequest(request);
+ }
+ }
+ }
+
+ @Override
+ public void onProcessDied(int pid, int uid) {}
+
+ @Override
+ public void onForegroundServicesChanged(int pid, int uid, int serviceTypes) {}
+ };
@VisibleForTesting
ActivityTaskManagerInternal.ScreenObserver mOverrideRequestScreenObserver =
new OverrideRequestScreenObserver();
@@ -239,8 +263,9 @@
mFoldedDeviceStates = readFoldedStates();
}
- mActivityTaskManagerInternal.registerTaskStackListener(mOverrideRequestTaskStackListener);
mActivityTaskManagerInternal.registerScreenObserver(mOverrideRequestScreenObserver);
+ LocalServices.getService(ActivityManagerInternal.class).registerProcessObserver(
+ mProcessObserver);
}
@VisibleForTesting
@@ -1289,23 +1314,6 @@
return deviceState.hasFlag(DeviceState.FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP);
}
- private class OverrideRequestTaskStackListener extends TaskStackListener {
- @Override
- public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo)
- throws RemoteException {
- synchronized (mLock) {
- if (!shouldCancelOverrideRequestWhenRequesterNotOnTop()) {
- return;
- }
-
- OverrideRequest request = mActiveOverride.get();
- if (!isTopApp(request.getPid())) {
- mOverrideRequestController.cancelRequest(request);
- }
- }
- }
- }
-
private class OverrideRequestScreenObserver implements
ActivityTaskManagerInternal.ScreenObserver {
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateNotificationController.java b/services/core/java/com/android/server/devicestate/DeviceStateNotificationController.java
index f4c84e7..f9aefd0 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateNotificationController.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateNotificationController.java
@@ -109,6 +109,7 @@
.setPackage(mContext.getPackageName());
final PendingIntent pendingIntent = PendingIntent.getBroadcast(
mContext, 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE);
+
showNotification(
info.name, info.activeNotificationTitle,
String.format(info.activeNotificationContent, requesterApplicationLabel),
@@ -175,7 +176,7 @@
if (getNotificationInfos().get(state) == null) {
return;
}
- mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID);
+ mHandler.post(() -> mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID));
}
@Override
@@ -219,8 +220,10 @@
builder.addAction(action);
}
- mNotificationManager.createNotificationChannel(channel);
- mNotificationManager.notify(NOTIFICATION_TAG, NOTIFICATION_ID, builder.build());
+ mHandler.post(() -> {
+ mNotificationManager.createNotificationChannel(channel);
+ mNotificationManager.notify(NOTIFICATION_TAG, NOTIFICATION_ID, builder.build());
+ });
}
private SparseArray<NotificationInfo> getNotificationInfos() {
diff --git a/services/core/java/com/android/server/display/BrightnessRangeController.java b/services/core/java/com/android/server/display/BrightnessRangeController.java
index 4bfc090..7b8b461 100644
--- a/services/core/java/com/android/server/display/BrightnessRangeController.java
+++ b/services/core/java/com/android/server/display/BrightnessRangeController.java
@@ -16,10 +16,12 @@
package com.android.server.display;
+import android.content.res.Resources;
import android.hardware.display.BrightnessInfo;
import android.os.IBinder;
import android.provider.DeviceConfigInterface;
+import com.android.internal.R;
import com.android.server.display.feature.DeviceConfigParameterProvider;
import java.io.PrintWriter;
@@ -33,20 +35,24 @@
private final Runnable mModeChangeCallback;
private final boolean mUseNbmController;
+ private Resources mResources;
BrightnessRangeController(HighBrightnessModeController hbmController,
- Runnable modeChangeCallback, DisplayDeviceConfig displayDeviceConfig) {
+ Runnable modeChangeCallback, DisplayDeviceConfig displayDeviceConfig,
+ Resources resources) {
this(hbmController, modeChangeCallback, displayDeviceConfig,
- new DeviceConfigParameterProvider(DeviceConfigInterface.REAL));
+ new DeviceConfigParameterProvider(DeviceConfigInterface.REAL), resources);
}
BrightnessRangeController(HighBrightnessModeController hbmController,
Runnable modeChangeCallback, DisplayDeviceConfig displayDeviceConfig,
- DeviceConfigParameterProvider configParameterProvider) {
+ DeviceConfigParameterProvider configParameterProvider, Resources resources) {
mHbmController = hbmController;
mModeChangeCallback = modeChangeCallback;
- mUseNbmController = configParameterProvider.isNormalBrightnessControllerFeatureEnabled();
+ mResources = resources;
+ mUseNbmController = configParameterProvider.isNormalBrightnessControllerFeatureEnabled() ||
+ resources.getBoolean(R.bool.config_allowNormalBrightnessControllerFeature);
mNormalBrightnessModeController.resetNbmData(displayDeviceConfig.getLuxThrottlingData());
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 53921d4..6466b44 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -116,6 +116,7 @@
import android.os.UserManager;
import android.provider.DeviceConfigInterface;
import android.provider.Settings;
+import android.sysprop.DisplayProperties;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.EventLog;
@@ -485,6 +486,9 @@
private boolean mBootCompleted = false;
+ // If we would like to keep a particular eye on a package, we can set the package name.
+ private boolean mExtraDisplayEventLogging;
+
private final BroadcastReceiver mIdleModeReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -565,6 +569,8 @@
mOverlayProperties = SurfaceControl.getOverlaySupport();
mSystemReady = false;
mConfigParameterProvider = new DeviceConfigParameterProvider(DeviceConfigInterface.REAL);
+ final String name = DisplayProperties.debug_vri_package().orElse(null);
+ mExtraDisplayEventLogging = !TextUtils.isEmpty(name);
}
public void setupSchedulerPolicies() {
@@ -2874,9 +2880,10 @@
// Delivers display event notifications to callbacks.
private void deliverDisplayEvent(int displayId, ArraySet<Integer> uids,
@DisplayEvent int event) {
- if (DEBUG) {
+ if (DEBUG || mExtraDisplayEventLogging) {
Slog.d(TAG, "Delivering display event: displayId="
- + displayId + ", event=" + event);
+ + displayId + ", event=" + event
+ + (uids != null ? ", uids=" + uids : ""));
}
// Grab the lock and copy the callbacks.
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index e8a954a..94904ca 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -675,7 +675,7 @@
HighBrightnessModeController hbmController = createHbmControllerLocked(modeChangeCallback);
mBrightnessRangeController = new BrightnessRangeController(hbmController,
- modeChangeCallback, mDisplayDeviceConfig);
+ modeChangeCallback, mDisplayDeviceConfig, resources);
mBrightnessThrottler = createBrightnessThrottlerLocked();
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 7558c6a..e849f7c 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -548,7 +548,7 @@
mBrightnessThrottler = createBrightnessThrottlerLocked();
mBrightnessRangeController = new BrightnessRangeController(hbmController,
- modeChangeCallback, mDisplayDeviceConfig);
+ modeChangeCallback, mDisplayDeviceConfig, resources);
mDisplayBrightnessController =
new DisplayBrightnessController(context, null,
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index a1d28da..999e30a 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -3387,6 +3387,10 @@
}
// Changing to a different IME.
+ IInputMethodInvoker curMethod = getCurMethodLocked();
+ if (curMethod != null) {
+ curMethod.removeStylusHandwritingWindow();
+ }
final long ident = Binder.clearCallingIdentity();
try {
// Set a subtype to this input method.
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
index b015a72..d2e980b 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
@@ -39,6 +39,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.FrameworkStatsLog;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.Objects;
@@ -497,6 +498,7 @@
final boolean is_non_dismissible;
final int fsi_state;
final boolean is_locked;
+ final int age_in_minutes;
@DurationMillisLong long post_duration_millis; // Not final; calculated at the end.
NotificationReported(NotificationRecordPair p,
@@ -541,6 +543,9 @@
hasFullScreenIntent, hasFsiRequestedButDeniedFlag, eventType);
this.is_locked = p.r.isLocked();
+
+ this.age_in_minutes = NotificationRecordLogger.getAgeInMinutes(
+ p.r.getSbn().getPostTime(), p.r.getSbn().getNotification().when);
}
}
@@ -601,4 +606,13 @@
}
return FrameworkStatsLog.NOTIFICATION_REPORTED__FSI_STATE__NO_FSI;
}
+
+ /**
+ * @param postTimeMs time (in {@link System#currentTimeMillis} time) the notification was posted
+ * @param whenMs A timestamp related to this notification, in milliseconds since the epoch.
+ * @return difference in duration as an integer in minutes
+ */
+ static int getAgeInMinutes(long postTimeMs, long whenMs) {
+ return (int) Duration.ofMillis(postTimeMs - whenMs).toMinutes();
+ }
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
index 9da0e98..fc0a776 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
@@ -77,7 +77,8 @@
notificationReported.is_non_dismissible,
notificationReported.post_duration_millis,
notificationReported.fsi_state,
- notificationReported.is_locked);
+ notificationReported.is_locked,
+ notificationReported.age_in_minutes);
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index e1f010f..b6f4aee 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -4126,6 +4126,10 @@
PackageManager.MATCH_DISABLED_COMPONENTS
| PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS
| PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, 0);
+ if (ai == null) {
+ Slog.e(TAG, "Failed to get ApplicationInfo for package name(" + pii.packageName + ").");
+ return null;
+ }
AssetManager am = new AssetManager();
am.addAssetPath(ai.publicSourceDir);
res = new Resources(am, null, null);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index d1a4e60..3394282 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3740,15 +3740,15 @@
*/
private int handleTransitionForKeyguardLw(boolean startKeyguardExitAnimation,
boolean notifyOccluded) {
+ int redoLayout = 0;
if (notifyOccluded) {
- final int redoLayout = applyKeyguardOcclusionChange();
- if (redoLayout != 0) return redoLayout;
+ redoLayout = applyKeyguardOcclusionChange();
}
if (startKeyguardExitAnimation) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "Starting keyguard exit animation");
startKeyguardExitAnimation(SystemClock.uptimeMillis());
}
- return 0;
+ return redoLayout;
}
// There are several different flavors of "assistant" that can be launched from
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 6821c40..f4e0407 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -12164,7 +12164,8 @@
if (DEBUG_ENERGY) {
Slog.d(TAG, "Updating mobile radio stats with " + activityInfo);
}
- ModemActivityInfo deltaInfo = mLastModemActivityInfo == null ? activityInfo
+ ModemActivityInfo deltaInfo = mLastModemActivityInfo == null
+ ? (activityInfo == null ? null : activityInfo.getDelta(activityInfo))
: mLastModemActivityInfo.getDelta(activityInfo);
mLastModemActivityInfo = activityInfo;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index d231cf3..5fcfb0d 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2884,7 +2884,7 @@
final boolean animate;
if (mStartingData != null) {
if (mStartingData.mWaitForSyncTransactionCommit
- || mTransitionController.inCollectingTransition(startingWindow)) {
+ || mTransitionController.isCollecting(this)) {
mStartingData.mRemoveAfterTransaction = AFTER_TRANSACTION_REMOVE_DIRECTLY;
mStartingData.mPrepareRemoveAnimation = prepareAnimation;
return;
@@ -9854,7 +9854,6 @@
ProtoLog.i(WM_DEBUG_STATES, "Moving to %s Relaunching %s callers=%s" ,
(andResume ? "RESUMED" : "PAUSED"), this, Debug.getCallers(6));
forceNewConfig = false;
- startRelaunching();
final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(pendingResults,
pendingNewIntents, configChangeFlags,
new MergedConfiguration(getProcessGlobalConfiguration(),
@@ -9871,11 +9870,12 @@
transaction.addCallback(callbackItem);
transaction.setLifecycleStateRequest(lifecycleItem);
mAtmService.getLifecycleManager().scheduleTransaction(transaction);
+ startRelaunching();
// Note: don't need to call pauseIfSleepingLocked() here, because the caller will only
// request resume if this activity is currently resumed, which implies we aren't
// sleeping.
} catch (RemoteException e) {
- ProtoLog.i(WM_DEBUG_STATES, "Relaunch failed %s", e);
+ Slog.w(TAG, "Failed to relaunch " + this + ": " + e);
}
if (andResume) {
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index d2d6552..735cbc4 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -33,6 +33,7 @@
import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR;
import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT;
import static android.content.pm.ActivityInfo.OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
@@ -672,7 +673,8 @@
// orientation.
candidate = mActivityRecord.mWmService.mapOrientationRequest(candidate);
- if (shouldApplyUserMinAspectRatioOverride() && !isFixedOrientation(candidate)) {
+ if (shouldApplyUserMinAspectRatioOverride() && (!isFixedOrientation(candidate)
+ || candidate == SCREEN_ORIENTATION_LOCKED)) {
Slog.v(TAG, "Requested orientation " + screenOrientationToString(candidate) + " for "
+ mActivityRecord + " is overridden to "
+ screenOrientationToString(SCREEN_ORIENTATION_PORTRAIT)
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index be90588..ee05e35 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
@@ -117,6 +118,11 @@
return;
}
if (targetActivity.attachedToProcess()) {
+ if (targetActivity.app.getCurrentProcState() >= PROCESS_STATE_CACHED_ACTIVITY) {
+ Slog.v(TAG, "Skip preload recents for cached proc " + targetActivity.app);
+ // The process may be frozen that cannot receive binder call.
+ return;
+ }
// The activity may be relaunched if it cannot handle the current configuration
// changes. The activity will be paused state if it is relaunched, otherwise it
// keeps the original stopped state.
diff --git a/services/core/java/com/android/server/wm/StartingData.java b/services/core/java/com/android/server/wm/StartingData.java
index 2d281c4..07ffa69e 100644
--- a/services/core/java/com/android/server/wm/StartingData.java
+++ b/services/core/java/com/android/server/wm/StartingData.java
@@ -108,4 +108,13 @@
boolean hasImeSurface() {
return false;
}
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " waitForSyncTransactionCommit=" + mWaitForSyncTransactionCommit
+ + " removeAfterTransaction= " + mRemoveAfterTransaction
+ + "}";
+ }
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 60b6a88..c322563 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3461,6 +3461,7 @@
info.topActivityLetterboxHeight = TaskInfo.PROPERTY_VALUE_UNSET;
info.isUserFullscreenOverrideEnabled = top != null
&& top.mLetterboxUiController.shouldApplyUserFullscreenOverride();
+ info.isTopActivityTransparent = top != null && !top.fillsParent();
info.isFromLetterboxDoubleTap = top != null && top.mLetterboxUiController.isFromDoubleTap();
if (info.isLetterboxDoubleTapEnabled) {
info.topActivityLetterboxWidth = top.getBounds().width();
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtilsTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtilsTest.java
index 8e17b3a..dcd5317 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtilsTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorDumpsysUtilsTest.java
@@ -17,26 +17,33 @@
package com.android.server.backup.utils;
import static org.junit.Assert.assertTrue;
-
+import static org.testng.AssertJUnit.assertFalse;
+import android.app.backup.BackupAnnotations;
import android.app.backup.BackupManagerMonitor;
import android.os.Bundle;
-
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
-
import java.io.File;
+import java.io.FileWriter;
public class BackupManagerMonitorDumpsysUtilsTest {
- private File mTempFile;
+ private long mRetentionPeriod;
+ private File mTempBMMEventsFile;
+ private File mTempSetUpDateFile;
+
+ private long mSizeLimit;
private TestBackupManagerMonitorDumpsysUtils mBackupManagerMonitorDumpsysUtils;
@Rule
public TemporaryFolder tmp = new TemporaryFolder();
@Before
public void setUp() throws Exception {
- mTempFile = tmp.newFile("testbmmevents.txt");
+ mRetentionPeriod = 30 * 60 * 1000;
+ mSizeLimit = 25 * 1024 * 1000;
+ mTempBMMEventsFile = tmp.newFile("testbmmevents.txt");
+ mTempSetUpDateFile = tmp.newFile("testSetUpDate.txt");
mBackupManagerMonitorDumpsysUtils = new TestBackupManagerMonitorDumpsysUtils();
}
@@ -46,7 +53,7 @@
throws Exception {
mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(null);
- assertTrue(mTempFile.length() == 0);
+ assertTrue(mTempBMMEventsFile.length() == 0);
}
@@ -57,7 +64,7 @@
event.putInt(BackupManagerMonitor.EXTRA_LOG_EVENT_CATEGORY, 1);
mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event);
- assertTrue(mTempFile.length() == 0);
+ assertTrue(mTempBMMEventsFile.length() == 0);
}
@Test
@@ -67,18 +74,236 @@
event.putInt(BackupManagerMonitor.EXTRA_LOG_EVENT_ID, 1);
mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event);
- assertTrue(mTempFile.length() == 0);
+ assertTrue(mTempBMMEventsFile.length() == 0);
+ }
+
+ @Test
+ public void parseBackupManagerMonitorEventForDumpsys_eventWithCategoryAndId_eventIsWrittenToFile()
+ throws Exception {
+ Bundle event = createRestoreBMMEvent();
+ mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event);
+
+ assertTrue(mTempBMMEventsFile.length() != 0);
+ }
+
+ @Test
+ public void parseBackupManagerMonitorEventForDumpsys_firstEvent_recordSetUpTimestamp()
+ throws Exception {
+ assertTrue(mTempBMMEventsFile.length()==0);
+ assertTrue(mTempSetUpDateFile.length()==0);
+
+ Bundle event = createRestoreBMMEvent();
+ mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event);
+
+ assertTrue(mTempBMMEventsFile.length() != 0);
+ assertTrue(mTempSetUpDateFile.length()!=0);
+ }
+
+ @Test
+ public void parseBackupManagerMonitorEventForDumpsys_notFirstEvent_doNotChangeSetUpTimestamp()
+ throws Exception {
+ Bundle event1 = createRestoreBMMEvent();
+ mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event1);
+ String setUpTimestampBefore = mBackupManagerMonitorDumpsysUtils.getSetUpDate();
+
+ Bundle event2 = createRestoreBMMEvent();
+ mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event2);
+ String setUpTimestampAfter = mBackupManagerMonitorDumpsysUtils.getSetUpDate();
+
+ assertTrue(setUpTimestampBefore.equals(setUpTimestampAfter));
+ }
+
+
+ @Test
+ public void parseBackupManagerMonitorEventForDumpsys_fileOverSizeLimit_doNotRecordEvents()
+ throws Exception {
+ assertTrue(mTempBMMEventsFile.length() == 0);
+ Bundle event = createRestoreBMMEvent();
+ mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event);
+ long fileSizeBefore = mTempBMMEventsFile.length();
+
+ mBackupManagerMonitorDumpsysUtils.setTestSizeLimit(0);
+ mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event);
+ long fileSizeAfter = mTempBMMEventsFile.length();
+ assertTrue(mBackupManagerMonitorDumpsysUtils.isFileLargerThanSizeLimit(mTempBMMEventsFile));
+ assertTrue(fileSizeBefore == fileSizeAfter);
+ }
+
+ @Test
+ public void parseBackupManagerMonitorEventForDumpsys_fileUnderSizeLimit_recordEvents()
+ throws Exception {
+ assertTrue(mTempBMMEventsFile.length() == 0);
+ Bundle event = createRestoreBMMEvent();
+
+ mBackupManagerMonitorDumpsysUtils.setTestSizeLimit(25 * 1024 * 1000);
+ mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event);
+ assertFalse(mBackupManagerMonitorDumpsysUtils.isFileLargerThanSizeLimit(mTempBMMEventsFile));
+ assertTrue(mTempBMMEventsFile.length() != 0);
+ }
+
+ @Test
+ public void deleteExpiredBackupManagerMonitorEvent_eventsAreExpired_deleteEventsAndReturnTrue()
+ throws Exception {
+ Bundle event = createRestoreBMMEvent();
+ mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event);
+ assertTrue(mTempBMMEventsFile.length() != 0);
+ // Re-initialise the test BackupManagerMonitorDumpsysUtils to
+ // clear the cached value of isAfterRetentionPeriod
+ mBackupManagerMonitorDumpsysUtils = new TestBackupManagerMonitorDumpsysUtils();
+
+ // set a retention period of 0 second
+ mBackupManagerMonitorDumpsysUtils.setTestRetentionPeriod(0);
+
+ assertTrue(mBackupManagerMonitorDumpsysUtils.deleteExpiredBMMEvents());
+ assertFalse(mTempBMMEventsFile.exists());
+ }
+
+ @Test
+ public void deleteExpiredBackupManagerMonitorEvent_eventsAreNotExpired_returnFalse() throws
+ Exception {
+ Bundle event = createRestoreBMMEvent();
+ mBackupManagerMonitorDumpsysUtils.parseBackupManagerMonitorRestoreEventForDumpsys(event);
+ assertTrue(mTempBMMEventsFile.length() != 0);
+
+ // set a retention period of 30 minutes
+ mBackupManagerMonitorDumpsysUtils.setTestRetentionPeriod(30 * 60 * 1000);
+
+ assertFalse(mBackupManagerMonitorDumpsysUtils.deleteExpiredBMMEvents());
+ assertTrue(mTempBMMEventsFile.length() != 0);
+ }
+
+ @Test
+ public void isAfterRetentionPeriod_afterRetentionPeriod_returnTrue() throws
+ Exception {
+ mBackupManagerMonitorDumpsysUtils.recordSetUpTimestamp();
+
+ // set a retention period of 0 second
+ mBackupManagerMonitorDumpsysUtils.setTestRetentionPeriod(0);
+
+ assertTrue(mBackupManagerMonitorDumpsysUtils.isAfterRetentionPeriod());
+ }
+
+ @Test
+ public void isAfterRetentionPeriod_beforeRetentionPeriod_returnFalse() throws
+ Exception {
+ mBackupManagerMonitorDumpsysUtils.recordSetUpTimestamp();
+
+ // set a retention period of 30 minutes
+ mBackupManagerMonitorDumpsysUtils.setTestRetentionPeriod(30 * 60 * 1000);
+
+ assertFalse(mBackupManagerMonitorDumpsysUtils.isAfterRetentionPeriod());
+ }
+
+ @Test
+ public void isAfterRetentionPeriod_noSetupDate_returnFalse() throws
+ Exception {
+ assertTrue(mTempSetUpDateFile.length() == 0);
+
+ assertFalse(mBackupManagerMonitorDumpsysUtils.isAfterRetentionPeriod());
+ }
+
+ @Test
+ public void isDateAfterNMillisec_date1IsAfterThanDate2_returnTrue() throws
+ Exception {
+ long timestamp1 = System.currentTimeMillis();
+ long timestamp2 = timestamp1 - 1;
+
+ assertTrue(mBackupManagerMonitorDumpsysUtils.isDateAfterNMillisec(timestamp1, timestamp2,
+ 0));
+ }
+
+ @Test
+ public void isDateAfterNMillisec_date1IsAfterNMillisecFromDate2_returnTrue() throws
+ Exception {
+ long timestamp1 = System.currentTimeMillis();
+ long timestamp2 = timestamp1 + 10;
+
+ assertTrue(mBackupManagerMonitorDumpsysUtils.isDateAfterNMillisec(timestamp1, timestamp2,
+ 10));
+ }
+
+ @Test
+ public void isDateAfterNMillisec_date1IsLessThanNMillisecFromDate2_returnFalse() throws
+ Exception {
+ long timestamp1 = System.currentTimeMillis();
+ long timestamp2 = timestamp1 + 10;
+
+ assertFalse(mBackupManagerMonitorDumpsysUtils.isDateAfterNMillisec(timestamp1, timestamp2,
+ 11));
+ }
+
+ @Test
+ public void recordSetUpTimestamp_timestampNotSetBefore_setTimestamp() throws
+ Exception {
+ assertTrue(mTempSetUpDateFile.length() == 0);
+
+ mBackupManagerMonitorDumpsysUtils.recordSetUpTimestamp();
+
+ assertTrue(mTempSetUpDateFile.length() != 0);
+ }
+
+ @Test
+ public void recordSetUpTimestamp_timestampSetBefore_doNothing() throws
+ Exception {
+ mBackupManagerMonitorDumpsysUtils.recordSetUpTimestamp();
+ assertTrue(mTempSetUpDateFile.length() != 0);
+ String timestampBefore = mBackupManagerMonitorDumpsysUtils.getSetUpDate();
+
+ mBackupManagerMonitorDumpsysUtils.recordSetUpTimestamp();
+
+ assertTrue(mTempSetUpDateFile.length() != 0);
+ String timestampAfter = mBackupManagerMonitorDumpsysUtils.getSetUpDate();
+ assertTrue(timestampAfter.equals(timestampBefore));
+ }
+
+ private Bundle createRestoreBMMEvent() {
+ Bundle event = new Bundle();
+ event.putInt(BackupManagerMonitor.EXTRA_LOG_EVENT_ID, 1);
+ event.putInt(BackupManagerMonitor.EXTRA_LOG_EVENT_CATEGORY, 1);
+ event.putInt(BackupManagerMonitor.EXTRA_LOG_OPERATION_TYPE,
+ BackupAnnotations.OperationType.RESTORE);
+ return event;
}
private class TestBackupManagerMonitorDumpsysUtils
extends BackupManagerMonitorDumpsysUtils {
+
+ private long testRetentionPeriod;
+ private long testSizeLimit;
+
TestBackupManagerMonitorDumpsysUtils() {
super();
+ this.testRetentionPeriod = mRetentionPeriod;
+ this.testSizeLimit = mSizeLimit;
+ }
+
+ public void setTestRetentionPeriod(long testRetentionPeriod) {
+ this.testRetentionPeriod = testRetentionPeriod;
+ }
+ public void setTestSizeLimit(long testSizeLimit) {
+ this.testSizeLimit = testSizeLimit;
}
@Override
public File getBMMEventsFile() {
- return mTempFile;
+ return mTempBMMEventsFile;
}
+
+ @Override
+ File getSetUpDateFile() {
+ return mTempSetUpDateFile;
+ }
+
+ @Override
+ long getRetentionPeriodInMillisec() {
+ return testRetentionPeriod;
+ }
+
+ @Override
+ long getBMMEventsFileSizeLimit(){
+ return testSizeLimit;
+ }
+
+
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorEventSenderTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorEventSenderTest.java
index 3af2932..604a68d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorEventSenderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupManagerMonitorEventSenderTest.java
@@ -340,8 +340,9 @@
@Test
public void putMonitoringExtraLong_bundleExists_fillsBundleCorrectly() throws Exception {
Bundle bundle = new Bundle();
+ long value = 123;
- Bundle result = mBackupManagerMonitorEventSender.putMonitoringExtra(bundle, "key", 123);
+ Bundle result = mBackupManagerMonitorEventSender.putMonitoringExtra(bundle, "key", value);
assertThat(result).isEqualTo(bundle);
assertThat(result.size()).isEqualTo(1);
@@ -350,7 +351,8 @@
@Test
public void putMonitoringExtraLong_bundleDoesNotExist_fillsBundleCorrectly() throws Exception {
- Bundle result = mBackupManagerMonitorEventSender.putMonitoringExtra(null, "key", 123);
+ long value = 123;
+ Bundle result = mBackupManagerMonitorEventSender.putMonitoringExtra(null, "key", value);
assertThat(result).isNotNull();
assertThat(result.size()).isEqualTo(1);
@@ -377,4 +379,25 @@
assertThat(result.size()).isEqualTo(1);
assertThat(result.getBoolean("key")).isTrue();
}
+
+ @Test
+ public void putMonitoringExtraInt_bundleExists_fillsBundleCorrectly() throws Exception {
+ Bundle bundle = new Bundle();
+
+ Bundle result = mBackupManagerMonitorEventSender.putMonitoringExtra(bundle, "key", 1);
+
+ assertThat(result).isEqualTo(bundle);
+ assertThat(result.size()).isEqualTo(1);
+ assertThat(result.getInt("key")).isEqualTo(1);
+ }
+
+ @Test
+ public void putMonitoringExtraInt_bundleDoesNotExist_fillsBundleCorrectly()
+ throws Exception {
+ Bundle result = mBackupManagerMonitorEventSender.putMonitoringExtra(null, "key", 1);
+
+ assertThat(result).isNotNull();
+ assertThat(result.size()).isEqualTo(1);
+ assertThat(result.getInt("key")).isEqualTo(1);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/am/FgsLoggerTest.java b/services/tests/servicestests/src/com/android/server/am/FgsLoggerTest.java
index f5005fd..38bb3de 100644
--- a/services/tests/servicestests/src/com/android/server/am/FgsLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/FgsLoggerTest.java
@@ -152,6 +152,50 @@
}
@Test
+ public void testApiStartStopFgs() throws InterruptedException {
+ ServiceRecord record = ServiceRecord.newEmptyInstanceForTest(null);
+ record.foregroundServiceType = ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA;
+
+ mFgsLogger.logForegroundServiceApiEventBegin(FOREGROUND_SERVICE_API_TYPE_CAMERA,
+ 1, 1, "aPackageHasNoName");
+ Thread.sleep(2000);
+
+ resetAndVerifyZeroInteractions();
+
+ mFgsLogger.logForegroundServiceApiEventEnd(FOREGROUND_SERVICE_API_TYPE_CAMERA, 1, 1);
+
+ resetAndVerifyZeroInteractions();
+
+ mFgsLogger.logForegroundServiceStart(1, 1, record);
+
+ resetAndVerifyZeroInteractions();
+ }
+
+ @Test
+ public void testFgsStartStopApiStartStop() throws InterruptedException {
+ ServiceRecord record = ServiceRecord.newEmptyInstanceForTest(null);
+ record.foregroundServiceType = ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA;
+
+ mFgsLogger.logForegroundServiceStart(1, 1, record);
+
+ resetAndVerifyZeroInteractions();
+
+ mFgsLogger.logForegroundServiceStop(1, record);
+
+ resetAndVerifyZeroInteractions();
+
+ mFgsLogger.logForegroundServiceApiEventBegin(FOREGROUND_SERVICE_API_TYPE_CAMERA,
+ 1, 1, "aPackageHasNoName");
+ Thread.sleep(2000);
+
+ resetAndVerifyZeroInteractions();
+
+ mFgsLogger.logForegroundServiceApiEventEnd(FOREGROUND_SERVICE_API_TYPE_CAMERA, 1, 1);
+
+ resetAndVerifyZeroInteractions();
+ }
+
+ @Test
public void testMultipleStartStopApis() throws InterruptedException {
ServiceRecord record = ServiceRecord.newEmptyInstanceForTest(null);
record.foregroundServiceType = ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA;
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index 6684150..a22a20e 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -28,7 +28,6 @@
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertThrows;
-import android.app.ActivityManager;
import android.hardware.devicestate.DeviceStateInfo;
import android.hardware.devicestate.DeviceStateRequest;
import android.hardware.devicestate.IDeviceStateManagerCallback;
@@ -582,10 +581,10 @@
// When the app is foreground, the state should not change
() -> {
int pid = Binder.getCallingPid();
- when(mWindowProcessController.getPid()).thenReturn(pid);
+ int uid = Binder.getCallingUid();
try {
- mService.mOverrideRequestTaskStackListener.onTaskMovedToFront(
- new ActivityManager.RunningTaskInfo());
+ mService.mProcessObserver.onForegroundActivitiesChanged(pid, uid,
+ true /* foregroundActivities */);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
@@ -594,8 +593,11 @@
() -> {
when(mWindowProcessController.getPid()).thenReturn(FAKE_PROCESS_ID);
try {
- mService.mOverrideRequestTaskStackListener.onTaskMovedToFront(
- new ActivityManager.RunningTaskInfo());
+ int pid = Binder.getCallingPid();
+ int uid = Binder.getCallingUid();
+ mService.mProcessObserver.onForegroundActivitiesChanged(pid, uid,
+ false /* foregroundActivities */);
+
} catch (RemoteException e) {
throw new RuntimeException(e);
}
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateNotificationControllerTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateNotificationControllerTest.java
index 728606f..bb0de03 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateNotificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateNotificationControllerTest.java
@@ -33,6 +33,8 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
import android.platform.test.annotations.Presubmit;
import android.util.SparseArray;
@@ -89,11 +91,12 @@
@Before
public void setup() throws Exception {
Context context = InstrumentationRegistry.getInstrumentation().getContext();
- Handler handler = mock(Handler.class);
PackageManager packageManager = mock(PackageManager.class);
Runnable cancelStateRunnable = mock(Runnable.class);
ApplicationInfo applicationInfo = mock(ApplicationInfo.class);
+ Handler handler = new DeviceStateNotificationControllerTestHandler(Looper.getMainLooper());
+
final SparseArray<DeviceStateNotificationController.NotificationInfo> notificationInfos =
new SparseArray<>();
notificationInfos.put(STATE_WITH_ACTIVE_NOTIFICATION,
@@ -259,4 +262,16 @@
assertEquals(Locale.ITALY, mNotificationInfoProvider.getCachedLocale());
clearInvocations(mNotificationInfoProvider);
}
+
+ private static class DeviceStateNotificationControllerTestHandler extends Handler {
+ DeviceStateNotificationControllerTestHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+ msg.getCallback().run();
+ return true;
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsNoteTest.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
index 6b21eb0..bf69216 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
@@ -1458,6 +1458,148 @@
}
@SmallTest
+ public void testGetPerStateActiveRadioDurationMs_initialModemActivity() {
+ final MockClock clock = new MockClock(); // holds realtime and uptime in ms
+ final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clock);
+ bi.setPowerProfile(mock(PowerProfile.class));
+
+ final int ratCount = RADIO_ACCESS_TECHNOLOGY_COUNT;
+ final int frequencyCount = ServiceState.FREQUENCY_RANGE_MMWAVE + 1;
+ final int txLevelCount = CellSignalStrength.getNumSignalStrengthLevels();
+
+ List<ActivityStatsTechSpecificInfo> specificInfoList = new ArrayList();
+
+ final long[][][] expectedDurationsMs = new long[ratCount][frequencyCount][txLevelCount];
+ final long[][] expectedRxDurationsMs = new long[ratCount][frequencyCount];
+ final long[][][] expectedTxDurationsMs = new long[ratCount][frequencyCount][txLevelCount];
+ for (int rat = 0; rat < ratCount; rat++) {
+ for (int freq = 0; freq < frequencyCount; freq++) {
+ if (rat == RADIO_ACCESS_TECHNOLOGY_NR
+ || freq == ServiceState.FREQUENCY_RANGE_UNKNOWN) {
+ // Only the NR RAT should have per frequency data.
+ expectedRxDurationsMs[rat][freq] = 0;
+ } else {
+ expectedRxDurationsMs[rat][freq] = POWER_DATA_UNAVAILABLE;
+ }
+ for (int txLvl = 0; txLvl < txLevelCount; txLvl++) {
+ if (rat == RADIO_ACCESS_TECHNOLOGY_NR
+ || freq == ServiceState.FREQUENCY_RANGE_UNKNOWN) {
+ // Only the NR RAT should have per frequency data.
+ expectedTxDurationsMs[rat][freq][txLvl] = 0;
+ } else {
+ expectedTxDurationsMs[rat][freq][txLvl] = POWER_DATA_UNAVAILABLE;
+ }
+ }
+ }
+ }
+
+ // The first modem activity pulled from modem with activity stats for each RATs.
+ specificInfoList.add(new ActivityStatsTechSpecificInfo(
+ AccessNetworkConstants.AccessNetworkType.UNKNOWN,
+ ServiceState.FREQUENCY_RANGE_UNKNOWN, new int[txLevelCount], 101));
+ specificInfoList.add(new ActivityStatsTechSpecificInfo(
+ AccessNetworkConstants.AccessNetworkType.GERAN,
+ ServiceState.FREQUENCY_RANGE_UNKNOWN, new int[txLevelCount], 202));
+ specificInfoList.add(new ActivityStatsTechSpecificInfo(
+ AccessNetworkConstants.AccessNetworkType.UTRAN,
+ ServiceState.FREQUENCY_RANGE_UNKNOWN, new int[txLevelCount], 303));
+ specificInfoList.add(new ActivityStatsTechSpecificInfo(
+ AccessNetworkConstants.AccessNetworkType.EUTRAN,
+ ServiceState.FREQUENCY_RANGE_UNKNOWN, new int[]{3, 9, 133, 48, 218}, 404));
+ specificInfoList.add(new ActivityStatsTechSpecificInfo(
+ AccessNetworkConstants.AccessNetworkType.CDMA2000,
+ ServiceState.FREQUENCY_RANGE_UNKNOWN, new int[txLevelCount], 505));
+ specificInfoList.add(new ActivityStatsTechSpecificInfo(
+ AccessNetworkConstants.AccessNetworkType.IWLAN,
+ ServiceState.FREQUENCY_RANGE_UNKNOWN, new int[txLevelCount], 606));
+ specificInfoList.add(new ActivityStatsTechSpecificInfo(
+ AccessNetworkConstants.AccessNetworkType.NGRAN,
+ ServiceState.FREQUENCY_RANGE_UNKNOWN, new int[txLevelCount], 707));
+ specificInfoList.add(new ActivityStatsTechSpecificInfo(
+ AccessNetworkConstants.AccessNetworkType.NGRAN,
+ ServiceState.FREQUENCY_RANGE_LOW, new int[txLevelCount], 808));
+ specificInfoList.add(new ActivityStatsTechSpecificInfo(
+ AccessNetworkConstants.AccessNetworkType.NGRAN,
+ ServiceState.FREQUENCY_RANGE_MID, new int[txLevelCount], 909));
+ specificInfoList.add(new ActivityStatsTechSpecificInfo(
+ AccessNetworkConstants.AccessNetworkType.NGRAN,
+ ServiceState.FREQUENCY_RANGE_HIGH, new int[txLevelCount], 1010));
+ specificInfoList.add(new ActivityStatsTechSpecificInfo(
+ AccessNetworkConstants.AccessNetworkType.NGRAN,
+ ServiceState.FREQUENCY_RANGE_MMWAVE, new int[txLevelCount], 1111));
+
+
+ final ActivityStatsTechSpecificInfo[] specificInfos = specificInfoList.toArray(
+ new ActivityStatsTechSpecificInfo[specificInfoList.size()]);
+ final ModemActivityInfo mai = new ModemActivityInfo(0L, 2002L, 3003L, specificInfos);
+ final ModemAndBatteryState state = new ModemAndBatteryState(bi, mai, specificInfos);
+
+
+ IntConsumer incrementTime = inc -> {
+ state.currentTimeMs += inc;
+ clock.realtime = clock.uptime = state.currentTimeMs;
+
+ final int currRat = state.currentRat;
+ final int currRant = state.currentRadioAccessNetworkType;
+ final int currFreqRange =
+ currRat == RADIO_ACCESS_TECHNOLOGY_NR ? state.currentFrequencyRange : 0;
+ int currSignalStrength = state.currentSignalStrengths.get(currRat);
+
+ if (state.modemActive) {
+ // Don't count the duration if the modem is not active
+ expectedDurationsMs[currRat][currFreqRange][currSignalStrength] += inc;
+ }
+
+ // Evaluate the HAL provided time in states.
+ final ActivityStatsTechSpecificInfo info = state.getSpecificInfo(currRant,
+ currFreqRange);
+ switch (state.modemState) {
+ case SLEEP:
+ long sleepMs = state.modemActivityInfo.getSleepTimeMillis();
+ state.modemActivityInfo.setSleepTimeMillis(sleepMs + inc);
+ break;
+ case IDLE:
+ long idleMs = state.modemActivityInfo.getIdleTimeMillis();
+ state.modemActivityInfo.setIdleTimeMillis(idleMs + inc);
+ break;
+ case RECEIVING:
+ long rxMs = info.getReceiveTimeMillis();
+ info.setReceiveTimeMillis(rxMs + inc);
+ expectedRxDurationsMs[currRat][currFreqRange] += inc;
+ break;
+ case TRANSMITTING:
+ int[] txMs = info.getTransmitTimeMillis().clone();
+ txMs[currSignalStrength] += inc;
+ info.setTransmitTimeMillis(txMs);
+ expectedTxDurationsMs[currRat][currFreqRange][currSignalStrength] += inc;
+ break;
+ }
+ };
+
+ // On battery, but the modem is not active
+ bi.updateTimeBasesLocked(true, Display.STATE_OFF, state.currentTimeMs * 1000,
+ state.currentTimeMs * 1000);
+ bi.setOnBatteryInternal(true);
+ state.noteModemControllerActivity();
+ // Ensure the first modem activity should not be counted up.
+ checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs,
+ expectedTxDurationsMs, bi, state.currentTimeMs);
+ // Start counting.
+ state.setRatType(TelephonyManager.NETWORK_TYPE_NR, BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR,
+ AccessNetworkConstants.AccessNetworkType.NGRAN);
+ // Frequency changed to low.
+ state.setFrequencyRange(ServiceState.FREQUENCY_RANGE_LOW);
+ incrementTime.accept(300);
+ state.setModemState(ModemState.RECEIVING);
+ incrementTime.accept(500);
+ state.setModemState(ModemState.TRANSMITTING);
+ incrementTime.accept(600);
+ state.noteModemControllerActivity();
+ checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs,
+ expectedTxDurationsMs, bi, state.currentTimeMs);
+ }
+
+ @SmallTest
public void testGetPerStateActiveRadioDurationMs_withModemActivity() {
final MockClock clock = new MockClock(); // holds realtime and uptime in ms
final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clock);
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java b/services/tests/servicestests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java
index 2e647c4..24e7725 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java
@@ -76,6 +76,9 @@
.initMeasuredEnergyStatsLocked();
BatteryStatsImpl stats = mStatsRule.getBatteryStats();
+ // The first ModemActivityInfo doesn't count up.
+ setInitialEmptyModemActivityInfo(stats);
+
// Scan for a cell
stats.notePhoneStateLocked(ServiceState.STATE_OUT_OF_SERVICE,
TelephonyManager.SIM_STATE_READY,
@@ -193,6 +196,9 @@
.initMeasuredEnergyStatsLocked();
BatteryStatsImpl stats = mStatsRule.getBatteryStats();
+ // The first ModemActivityInfo doesn't count up.
+ setInitialEmptyModemActivityInfo(stats);
+
// Scan for a cell
stats.notePhoneStateLocked(ServiceState.STATE_OUT_OF_SERVICE,
TelephonyManager.SIM_STATE_READY,
@@ -345,6 +351,9 @@
.initMeasuredEnergyStatsLocked();
BatteryStatsImpl stats = mStatsRule.getBatteryStats();
+ // The first ModemActivityInfo doesn't count up.
+ setInitialEmptyModemActivityInfo(stats);
+
// Scan for a cell
stats.notePhoneStateLocked(ServiceState.STATE_OUT_OF_SERVICE,
TelephonyManager.SIM_STATE_READY,
@@ -581,6 +590,9 @@
.initMeasuredEnergyStatsLocked();
BatteryStatsImpl stats = mStatsRule.getBatteryStats();
+ // The first ModemActivityInfo doesn't count up.
+ setInitialEmptyModemActivityInfo(stats);
+
// Scan for a cell
stats.notePhoneStateLocked(ServiceState.STATE_OUT_OF_SERVICE,
TelephonyManager.SIM_STATE_READY,
@@ -658,6 +670,9 @@
.initMeasuredEnergyStatsLocked();
BatteryStatsImpl stats = mStatsRule.getBatteryStats();
+ // The first ModemActivityInfo doesn't count up.
+ setInitialEmptyModemActivityInfo(stats);
+
stats.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH, 0, -1,
0, 0);
@@ -837,6 +852,9 @@
.initMeasuredEnergyStatsLocked();
BatteryStatsImpl stats = mStatsRule.getBatteryStats();
+ // The first ModemActivityInfo doesn't count up.
+ setInitialEmptyModemActivityInfo(stats);
+
stats.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH, 0, -1,
0, 0);
@@ -1019,4 +1037,10 @@
assertThat(uidConsumer.getConsumedPower(background)).isWithin(PRECISION).of(2.08130);
assertThat(uidConsumer.getConsumedPower(fgs)).isWithin(PRECISION).of(0);
}
+
+ public void setInitialEmptyModemActivityInfo(BatteryStatsImpl stats) {
+ // Initial empty ModemActivityInfo.
+ final ModemActivityInfo emptyMai = new ModemActivityInfo(0L, 0L, 0L, new int[5], 0L);
+ stats.noteModemControllerActivity(emptyMai, 0, 0, 0, mNetworkStatsManager);
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
index b522cab..5147a08 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
@@ -28,6 +28,7 @@
import static com.android.server.notification.NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_USER_OTHER;
import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED;
import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_UPDATED;
+import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -48,6 +49,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.time.Duration;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -230,4 +233,12 @@
NotificationRecordLogger.NotificationCancelledEvent.fromCancelReason(
REASON_CANCEL, DISMISSAL_OTHER));
}
+
+ @Test
+ public void testGetAgeInMinutes() {
+ long postTimeMs = Duration.ofMinutes(5).toMillis();
+ long whenMs = Duration.ofMinutes(2).toMillis();
+ int age = NotificationRecordLogger.getAgeInMinutes(postTimeMs, whenMs);
+ assertThat(age).isEqualTo(3);
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
index 381b27b..5f92fd5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -32,6 +32,7 @@
import static android.content.pm.ActivityInfo.OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT;
import static android.content.pm.ActivityInfo.OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
@@ -819,6 +820,9 @@
assertEquals(mController.overrideOrientationIfNeeded(
/* candidate */ SCREEN_ORIENTATION_UNSPECIFIED), SCREEN_ORIENTATION_PORTRAIT);
+ assertEquals(mController.overrideOrientationIfNeeded(
+ /* candidate */ SCREEN_ORIENTATION_LOCKED), SCREEN_ORIENTATION_PORTRAIT);
+
// unchanged if orientation is specified
assertEquals(mController.overrideOrientationIfNeeded(
/* candidate */ SCREEN_ORIENTATION_LANDSCAPE), SCREEN_ORIENTATION_LANDSCAPE);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index de3a526..491d5b5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.ActivityManager.PROCESS_STATE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -148,8 +149,9 @@
anyInt() /* startFlags */, any() /* profilerInfo */);
// Assume its process is alive because the caller should be the recents service.
- mSystemServicesTestRule.addProcess(aInfo.packageName, aInfo.processName, 12345 /* pid */,
- aInfo.applicationInfo.uid);
+ final WindowProcessController proc = mSystemServicesTestRule.addProcess(aInfo.packageName,
+ aInfo.processName, 12345 /* pid */, aInfo.applicationInfo.uid);
+ proc.setCurrentProcState(PROCESS_STATE_HOME);
Intent recentsIntent = new Intent().setComponent(mRecentsComponent);
// Null animation indicates to preload.
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
index bc41829..71ac94b 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
@@ -334,6 +334,7 @@
if (mServiceConnection != null) {
mContext.unbindService(mServiceConnection);
mServiceConnection = null;
+ mService = null;
}
}