Merge "Add provider facing CREATE APIs for Credential Manager."
diff --git a/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java b/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
index be0e025..6af24be 100644
--- a/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
+++ b/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
@@ -227,6 +227,9 @@
public static final String KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP =
"js_min_satiated_balance_other_app";
/** @hide */
+ public static final String KEY_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER =
+ "js_min_satiated_balance_increment_updater";
+ /** @hide */
public static final String KEY_JS_MAX_SATIATED_BALANCE =
"js_max_satiated_balance";
/** @hide */
@@ -509,6 +512,15 @@
public static final long DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_ONGOING_CAKES = arcToCake(0);
/** @hide */
public static final long DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_MAX_CAKES = arcToCake(5000);
+ /**
+ * How many credits to increase the updating app's min satiated balance by for each app that it
+ * is responsible for updating.
+ * @hide
+ */
+ public static final long DEFAULT_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER_CAKES =
+ // Research indicates that the median time between popular app updates is 13-14 days,
+ // so adjust by 14 to amortize over that time.
+ DEFAULT_JS_REWARD_APP_INSTALL_INSTANT_CAKES / 14;
/** @hide */
public static final long DEFAULT_JS_ACTION_JOB_MAX_START_CTP_CAKES = arcToCake(3);
/** @hide */
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
index c13e1dd9..4a26d21 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -81,6 +81,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.CopyOnWriteArraySet;
/**
@@ -164,6 +165,10 @@
@GuardedBy("mLock")
private final SparseArrayMap<String, Boolean> mVipOverrides = new SparseArrayMap<>();
+ /** Set of apps each installer is responsible for installing. */
+ @GuardedBy("mLock")
+ private final SparseArrayMap<String, ArraySet<String>> mInstallers = new SparseArrayMap<>();
+
private volatile boolean mHasBattery = true;
private volatile boolean mIsEnabled;
private volatile int mBootPhase;
@@ -353,6 +358,14 @@
return mCompleteEconomicPolicy;
}
+ /** Returns the number of apps that this app is expected to update at some point. */
+ int getAppUpdateResponsibilityCount(final int userId, @NonNull final String pkgName) {
+ synchronized (mLock) {
+ // TODO(248274798): return 0 if the app has lost the install permission
+ return ArrayUtils.size(mInstallers.get(userId, pkgName));
+ }
+ }
+
@NonNull
SparseArrayMap<String, InstalledPackageInfo> getInstalledPackages() {
synchronized (mLock) {
@@ -525,7 +538,8 @@
}
synchronized (mLock) {
final InstalledPackageInfo ipo = new InstalledPackageInfo(packageInfo);
- mPkgCache.add(userId, pkgName, ipo);
+ final InstalledPackageInfo oldIpo = mPkgCache.add(userId, pkgName, ipo);
+ maybeUpdateInstallerStatusLocked(oldIpo, ipo);
mUidToPackageCache.add(uid, pkgName);
// TODO: only do this when the user first launches the app (app leaves stopped state)
mAgent.grantBirthrightLocked(userId, pkgName);
@@ -552,7 +566,14 @@
synchronized (mLock) {
mUidToPackageCache.remove(uid, pkgName);
mVipOverrides.delete(userId, pkgName);
- mPkgCache.delete(userId, pkgName);
+ final InstalledPackageInfo ipo = mPkgCache.delete(userId, pkgName);
+ mInstallers.delete(userId, pkgName);
+ if (ipo != null && ipo.installerPackageName != null) {
+ final ArraySet<String> list = mInstallers.get(userId, ipo.installerPackageName);
+ if (list != null) {
+ list.remove(pkgName);
+ }
+ }
mAgent.onPackageRemovedLocked(userId, pkgName);
}
}
@@ -574,7 +595,8 @@
mPackageManager.getInstalledPackagesAsUser(PACKAGE_QUERY_FLAGS, userId);
for (int i = pkgs.size() - 1; i >= 0; --i) {
final InstalledPackageInfo ipo = new InstalledPackageInfo(pkgs.get(i));
- mPkgCache.add(userId, ipo.packageName, ipo);
+ final InstalledPackageInfo oldIpo = mPkgCache.add(userId, ipo.packageName, ipo);
+ maybeUpdateInstallerStatusLocked(oldIpo, ipo);
}
mAgent.grantBirthrightsLocked(userId);
}
@@ -590,6 +612,7 @@
mUidToPackageCache.remove(pkgInfo.uid);
}
}
+ mInstallers.delete(userId);
mPkgCache.delete(userId);
mAgent.onUserRemovedLocked(userId);
}
@@ -746,11 +769,49 @@
mPackageManager.getInstalledPackagesAsUser(PACKAGE_QUERY_FLAGS, userId);
for (int i = pkgs.size() - 1; i >= 0; --i) {
final InstalledPackageInfo ipo = new InstalledPackageInfo(pkgs.get(i));
- mPkgCache.add(userId, ipo.packageName, ipo);
+ final InstalledPackageInfo oldIpo = mPkgCache.add(userId, ipo.packageName, ipo);
+ maybeUpdateInstallerStatusLocked(oldIpo, ipo);
}
}
}
+ /**
+ * Used to update the set of installed apps for each installer. This only has an effect if the
+ * installer package name is different between {@code oldIpo} and {@code newIpo}.
+ */
+ @GuardedBy("mLock")
+ private void maybeUpdateInstallerStatusLocked(@Nullable InstalledPackageInfo oldIpo,
+ @NonNull InstalledPackageInfo newIpo) {
+ final boolean changed;
+ if (oldIpo == null) {
+ changed = newIpo.installerPackageName != null;
+ } else {
+ changed = !Objects.equals(oldIpo.installerPackageName, newIpo.installerPackageName);
+ }
+ if (!changed) {
+ return;
+ }
+ // InstallSourceInfo doesn't track userId, so for now, assume the installer on the package's
+ // user profile did the installation.
+ // TODO(246640162): use the actual installer's user ID
+ final int userId = UserHandle.getUserId(newIpo.uid);
+ final String pkgName = newIpo.packageName;
+ if (oldIpo != null) {
+ final ArraySet<String> oldList = mInstallers.get(userId, oldIpo.installerPackageName);
+ if (oldList != null) {
+ oldList.remove(pkgName);
+ }
+ }
+ if (newIpo.installerPackageName != null) {
+ ArraySet<String> newList = mInstallers.get(userId, newIpo.installerPackageName);
+ if (newList == null) {
+ newList = new ArraySet<>();
+ mInstallers.add(userId, newIpo.installerPackageName, newList);
+ }
+ newList.add(pkgName);
+ }
+ }
+
private void registerListeners() {
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
@@ -1360,6 +1421,23 @@
pw.println();
pw.println();
+ pw.println("Installers:");
+ pw.increaseIndent();
+ for (int u = 0; u < mInstallers.numMaps(); ++u) {
+ final int userId = mInstallers.keyAt(u);
+
+ for (int p = 0; p < mInstallers.numElementsForKeyAt(u); ++p) {
+ final String pkgName = mInstallers.keyAt(u, p);
+
+ pw.print(appToString(userId, pkgName));
+ pw.print(": ");
+ pw.print(mInstallers.valueAt(u, p).size());
+ pw.println(" apps");
+ }
+ }
+ pw.decreaseIndent();
+
+ pw.println();
mCompleteEconomicPolicy.dump(pw);
pw.println();
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
index 55cc352..322e824 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
@@ -42,6 +42,7 @@
import static android.app.tare.EconomyManager.DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT_CAKES;
import static android.app.tare.EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES;
import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED_CAKES;
+import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER_CAKES;
import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES;
import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_APP_INSTALL_INSTANT_CAKES;
import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_APP_INSTALL_MAX_CAKES;
@@ -87,6 +88,7 @@
import static android.app.tare.EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT;
import static android.app.tare.EconomyManager.KEY_JS_MAX_SATIATED_BALANCE;
import static android.app.tare.EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED;
+import static android.app.tare.EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER;
import static android.app.tare.EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP;
import static android.app.tare.EconomyManager.KEY_JS_REWARD_APP_INSTALL_INSTANT;
import static android.app.tare.EconomyManager.KEY_JS_REWARD_APP_INSTALL_MAX;
@@ -154,6 +156,7 @@
private long mMinSatiatedBalanceExempted;
private long mMinSatiatedBalanceOther;
+ private long mMinSatiatedBalanceIncrementalAppUpdater;
private long mMaxSatiatedBalance;
private long mInitialSatiatedConsumptionLimit;
private long mHardSatiatedConsumptionLimit;
@@ -183,11 +186,20 @@
if (mIrs.isPackageRestricted(userId, pkgName)) {
return 0;
}
+
+ final long baseBalance;
if (mIrs.isPackageExempted(userId, pkgName)) {
- return mMinSatiatedBalanceExempted;
+ baseBalance = mMinSatiatedBalanceExempted;
+ } else {
+ baseBalance = mMinSatiatedBalanceOther;
}
- // TODO: take other exemptions into account
- return mMinSatiatedBalanceOther;
+
+ long minBalance = baseBalance;
+
+ final int updateResponsibilityCount = mIrs.getAppUpdateResponsibilityCount(userId, pkgName);
+ minBalance += updateResponsibilityCount * mMinSatiatedBalanceIncrementalAppUpdater;
+
+ return Math.min(minBalance, mMaxSatiatedBalance);
}
@Override
@@ -242,6 +254,9 @@
mMinSatiatedBalanceExempted = getConstantAsCake(mParser, properties,
KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED, DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED_CAKES,
mMinSatiatedBalanceOther);
+ mMinSatiatedBalanceIncrementalAppUpdater = getConstantAsCake(mParser, properties,
+ KEY_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER,
+ DEFAULT_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER_CAKES);
mMaxSatiatedBalance = getConstantAsCake(mParser, properties,
KEY_JS_MAX_SATIATED_BALANCE, DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES,
Math.max(arcToCake(1), mMinSatiatedBalanceExempted));
@@ -397,10 +412,11 @@
@Override
void dump(IndentingPrintWriter pw) {
- pw.println("Min satiated balances:");
+ pw.println("Min satiated balance:");
pw.increaseIndent();
pw.print("Exempted", cakeToString(mMinSatiatedBalanceExempted)).println();
pw.print("Other", cakeToString(mMinSatiatedBalanceOther)).println();
+ pw.print("+App Updater", cakeToString(mMinSatiatedBalanceIncrementalAppUpdater)).println();
pw.decreaseIndent();
pw.print("Max satiated balance", cakeToString(mMaxSatiatedBalance)).println();
pw.print("Consumption limits: [");
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
index 620d1a0d..a68170c 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
@@ -286,7 +286,7 @@
final int idx = (mRewardBucketIndex - b + NUM_REWARD_BUCKET_WINDOWS)
% NUM_REWARD_BUCKET_WINDOWS;
final RewardBucket rewardBucket = mRewardBuckets[idx];
- if (rewardBucket == null) {
+ if (rewardBucket == null || rewardBucket.startTimeMs == 0) {
continue;
}
diff --git a/core/api/current.txt b/core/api/current.txt
index 65c1f46..c7ca0cd 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -4426,6 +4426,7 @@
method public void readFromParcel(android.os.Parcel);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.ActivityManager.MemoryInfo> CREATOR;
+ field public long advertisedMem;
field public long availMem;
field public boolean lowMemory;
field public long threshold;
@@ -18645,8 +18646,10 @@
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.Light> CREATOR;
field public static final int LIGHT_CAPABILITY_BRIGHTNESS = 1; // 0x1
- field public static final int LIGHT_CAPABILITY_RGB = 0; // 0x0
+ field public static final int LIGHT_CAPABILITY_COLOR_RGB = 2; // 0x2
+ field @Deprecated public static final int LIGHT_CAPABILITY_RGB = 0; // 0x0
field public static final int LIGHT_TYPE_INPUT = 10001; // 0x2711
+ field public static final int LIGHT_TYPE_KEYBOARD_BACKLIGHT = 10003; // 0x2713
field public static final int LIGHT_TYPE_MICROPHONE = 8; // 0x8
field public static final int LIGHT_TYPE_PLAYER_ID = 10002; // 0x2712
}
@@ -51882,10 +51885,11 @@
method public void setSpeechStateChangeTypes(int);
method public void writeToParcel(android.os.Parcel, int);
field public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 4; // 0x4
+ field public static final int CONTENT_CHANGE_TYPE_CONTENT_INVALID = 1024; // 0x400
field public static final int CONTENT_CHANGE_TYPE_DRAG_CANCELLED = 512; // 0x200
field public static final int CONTENT_CHANGE_TYPE_DRAG_DROPPED = 256; // 0x100
field public static final int CONTENT_CHANGE_TYPE_DRAG_STARTED = 128; // 0x80
- field public static final int CONTENT_CHANGE_TYPE_INVALID = 1024; // 0x400
+ field public static final int CONTENT_CHANGE_TYPE_ERROR = 2048; // 0x800
field public static final int CONTENT_CHANGE_TYPE_PANE_APPEARED = 16; // 0x10
field public static final int CONTENT_CHANGE_TYPE_PANE_DISAPPEARED = 32; // 0x20
field public static final int CONTENT_CHANGE_TYPE_PANE_TITLE = 8; // 0x8
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index fb2c9e4..e08cef9 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -121,6 +121,7 @@
public class ActivityManager {
method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void addHomeVisibilityListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.HomeVisibilityListener);
method public void alwaysShowUnsupportedCompileSdkWarning(android.content.ComponentName);
+ method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public int[] getSecondaryDisplayIdsForStartingBackgroundUsers();
method public long getTotalRam();
method @RequiresPermission(allOf={android.Manifest.permission.PACKAGE_USAGE_STATS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}, conditional=true) public int getUidProcessCapabilities(int);
method @RequiresPermission(allOf={android.Manifest.permission.PACKAGE_USAGE_STATS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}, conditional=true) public int getUidProcessState(int);
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 8ae16df6..576b572 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -29,6 +29,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
@@ -2812,6 +2813,15 @@
*/
public static class MemoryInfo implements Parcelable {
/**
+ * The advertised memory of the system, as the end user would encounter in a retail display
+ * environment. This value might be different from {@code totalMem}. This could be due to
+ * many reasons. For example, the ODM could reserve part of the memory for the Trusted
+ * Execution Environment (TEE) which the kernel doesn't have access or knowledge about it.
+ */
+ @SuppressLint("MutableBareField")
+ public long advertisedMem;
+
+ /**
* The available memory on the system. This number should not
* be considered absolute: due to the nature of the kernel, a significant
* portion of this memory is actually in use and needed for the overall
@@ -2860,6 +2870,7 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(advertisedMem);
dest.writeLong(availMem);
dest.writeLong(totalMem);
dest.writeLong(threshold);
@@ -2871,6 +2882,7 @@
}
public void readFromParcel(Parcel source) {
+ advertisedMem = source.readLong();
availMem = source.readLong();
totalMem = source.readLong();
threshold = source.readLong();
@@ -4375,13 +4387,15 @@
* a profile, the {@link #getCurrentUser()}, the {@link UserHandle#SYSTEM system user}, or
* does not exist.
*
- * @param displayId id of the display, it must exist.
+ * @param displayId id of the display.
*
* @return whether the operation succeeded. Notice that if the user was already started in such
* display before, it will return {@code false}.
*
* @throws UnsupportedOperationException if the device does not support background users on
* secondary displays.
+ * @throws IllegalArgumentException if the display doesn't exist or is not a valid display to
+ * start secondary users on.
*
* @hide
*/
@@ -4402,6 +4416,24 @@
}
/**
+ * Gets the id of displays that can be used by
+ * {@link #startUserInBackgroundOnSecondaryDisplay(int, int)}.
+ *
+ * @hide
+ */
+ @TestApi
+ @Nullable
+ @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_USERS})
+ public int[] getSecondaryDisplayIdsForStartingBackgroundUsers() {
+ try {
+ return getService().getSecondaryDisplayIdsForStartingBackgroundUsers();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Gets the message that is shown when a user is switched from.
*
* @hide
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 980b79b..b4abd3c 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -770,4 +770,10 @@
"@android.annotation.RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional = true)")
boolean startUserInBackgroundOnSecondaryDisplay(int userid, int displayId);
+ /**
+ * Gets the ids of displays that can be used on {@link #startUserInBackgroundOnSecondaryDisplay(int userId, int displayId)}.
+ *
+ * <p>Typically used only by automotive builds when the vehicle has multiple displays.
+ */
+ @nullable int[] getSecondaryDisplayIdsForStartingBackgroundUsers();
}
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index d4c9a42..fadfa5c 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -445,8 +445,8 @@
@Nullable Executor executor,
@Nullable AudioConfigurationChangeCallback callback) {
if (mVirtualAudioDevice == null) {
- mVirtualAudioDevice = new VirtualAudioDevice(
- mContext, mVirtualDevice, display, executor, callback);
+ mVirtualAudioDevice = new VirtualAudioDevice(mContext, mVirtualDevice, display,
+ executor, callback, () -> mVirtualAudioDevice = null);
}
return mVirtualAudioDevice;
}
diff --git a/core/java/android/companion/virtual/audio/VirtualAudioDevice.java b/core/java/android/companion/virtual/audio/VirtualAudioDevice.java
index 0db7b5f..e200a11 100644
--- a/core/java/android/companion/virtual/audio/VirtualAudioDevice.java
+++ b/core/java/android/companion/virtual/audio/VirtualAudioDevice.java
@@ -64,11 +64,24 @@
void onRecordingConfigChanged(@NonNull List<AudioRecordingConfiguration> configs);
}
+ /**
+ * Interface to be notified when {@link #close()} is called.
+ *
+ * @hide
+ */
+ public interface CloseListener {
+ /**
+ * Notifies when {@link #close()} is called.
+ */
+ void onClosed();
+ }
+
private final Context mContext;
private final IVirtualDevice mVirtualDevice;
private final VirtualDisplay mVirtualDisplay;
private final AudioConfigurationChangeCallback mCallback;
private final Executor mExecutor;
+ private final CloseListener mListener;
@Nullable
private VirtualAudioSession mOngoingSession;
@@ -77,12 +90,13 @@
*/
public VirtualAudioDevice(Context context, IVirtualDevice virtualDevice,
@NonNull VirtualDisplay virtualDisplay, @Nullable Executor executor,
- @Nullable AudioConfigurationChangeCallback callback) {
+ @Nullable AudioConfigurationChangeCallback callback, @Nullable CloseListener listener) {
mContext = context;
mVirtualDevice = virtualDevice;
mVirtualDisplay = virtualDisplay;
mExecutor = executor;
mCallback = callback;
+ mListener = listener;
}
/**
@@ -169,6 +183,10 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
+
+ if (mListener != null) {
+ mListener.onClosed();
+ }
}
}
}
diff --git a/core/java/android/hardware/lights/Light.java b/core/java/android/hardware/lights/Light.java
index c311379..1df9b75 100644
--- a/core/java/android/hardware/lights/Light.java
+++ b/core/java/android/hardware/lights/Light.java
@@ -60,15 +60,29 @@
public static final int LIGHT_TYPE_PLAYER_ID = 10002;
/**
+ * Type for lights that illuminate keyboard keys.
+ */
+ public static final int LIGHT_TYPE_KEYBOARD_BACKLIGHT = 10003;
+
+ /**
* Capability for lights that could adjust its LED brightness. If the capability is not present
- * the led can only be turned either on or off.
+ * the LED can only be turned either on or off.
*/
public static final int LIGHT_CAPABILITY_BRIGHTNESS = 1 << 0;
/**
- * Capability for lights that has red, green and blue LEDs to control the light's color.
+ * Capability for lights that have red, green and blue LEDs to control the light's color.
*/
- public static final int LIGHT_CAPABILITY_RGB = 0 << 1;
+ public static final int LIGHT_CAPABILITY_COLOR_RGB = 1 << 1;
+
+ /**
+ * Capability for lights that have red, green and blue LEDs to control the light's color.
+ *
+ * @deprecated Wrong int based flag with value 0. Use capability flag {@code
+ * LIGHT_CAPABILITY_COLOR_RGB} instead.
+ */
+ @Deprecated
+ public static final int LIGHT_CAPABILITY_RGB = 0;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -77,6 +91,7 @@
LIGHT_TYPE_MICROPHONE,
LIGHT_TYPE_INPUT,
LIGHT_TYPE_PLAYER_ID,
+ LIGHT_TYPE_KEYBOARD_BACKLIGHT,
})
public @interface LightType {}
@@ -85,6 +100,7 @@
@IntDef(flag = true, prefix = {"LIGHT_CAPABILITY_"},
value = {
LIGHT_CAPABILITY_BRIGHTNESS,
+ LIGHT_CAPABILITY_COLOR_RGB,
LIGHT_CAPABILITY_RGB,
})
public @interface LightCapability {}
@@ -233,7 +249,7 @@
* @return True if the hardware can control the RGB led, otherwise false.
*/
public boolean hasRgbControl() {
- return (mCapabilities & LIGHT_CAPABILITY_RGB) == LIGHT_CAPABILITY_RGB;
+ return (mCapabilities & LIGHT_CAPABILITY_COLOR_RGB) == LIGHT_CAPABILITY_COLOR_RGB;
}
}
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index edfcb3d..d5c3de1 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -54,6 +54,7 @@
import android.system.Os;
import android.system.StructStat;
import android.text.TextUtils;
+import android.util.DataUnit;
import android.util.Log;
import android.util.Slog;
import android.webkit.MimeTypeMap;
@@ -83,6 +84,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
+import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
@@ -1309,6 +1311,85 @@
return val * pow;
}
+ private static long toBytes(long value, String unit) {
+ unit = unit.toUpperCase();
+
+ if (List.of("B").contains(unit)) {
+ return value;
+ }
+
+ if (List.of("K", "KB").contains(unit)) {
+ return DataUnit.KILOBYTES.toBytes(value);
+ }
+
+ if (List.of("M", "MB").contains(unit)) {
+ return DataUnit.MEGABYTES.toBytes(value);
+ }
+
+ if (List.of("G", "GB").contains(unit)) {
+ return DataUnit.GIGABYTES.toBytes(value);
+ }
+
+ if (List.of("KI", "KIB").contains(unit)) {
+ return DataUnit.KIBIBYTES.toBytes(value);
+ }
+
+ if (List.of("MI", "MIB").contains(unit)) {
+ return DataUnit.MEBIBYTES.toBytes(value);
+ }
+
+ if (List.of("GI", "GIB").contains(unit)) {
+ return DataUnit.GIBIBYTES.toBytes(value);
+ }
+
+ return Long.MIN_VALUE;
+ }
+
+ /**
+ * @param fmtSize The string that contains the size to be parsed. The
+ * expected format is:
+ *
+ * <p>"^((\\s*[-+]?[0-9]+)\\s*(B|K|KB|M|MB|G|GB|Ki|KiB|Mi|MiB|Gi|GiB)\\s*)$"
+ *
+ * <p>For example: 10Kb, 500GiB, 100mb. The unit is not case sensitive.
+ *
+ * @return the size in bytes. If {@code fmtSize} has invalid format, it
+ * returns {@link Long#MIN_VALUE}.
+ * @hide
+ */
+ public static long parseSize(@Nullable String fmtSize) {
+ if (fmtSize == null || fmtSize.isBlank()) {
+ return Long.MIN_VALUE;
+ }
+
+ int sign = 1;
+ fmtSize = fmtSize.trim();
+ char first = fmtSize.charAt(0);
+ if (first == '-' || first == '+') {
+ if (first == '-') {
+ sign = -1;
+ }
+
+ fmtSize = fmtSize.replace(first + "", "");
+ }
+
+ int index = 0;
+ // Find the last index of the value in fmtSize.
+ while (index < fmtSize.length() && Character.isDigit(fmtSize.charAt(index))) {
+ index++;
+ }
+
+ // Check if number and units are present.
+ if (index == 0 || index == fmtSize.length()) {
+ return Long.MIN_VALUE;
+ }
+
+ long value = sign * Long.valueOf(fmtSize.substring(0, index));
+ String unit = fmtSize.substring(index).trim();
+
+ return toBytes(value, unit);
+ }
+
/**
* Closes the given object quietly, ignoring any checked exceptions. Does
* nothing if the given object is {@code null}.
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 14082f3..c943a3d 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -26,6 +26,7 @@
import android.annotation.UptimeMillisLong;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build.VERSION_CODES;
+import android.sysprop.MemoryProperties;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
@@ -1330,6 +1331,24 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public static final native void sendSignalQuiet(int pid, int signal);
+ /**
+ * @return The advertised memory of the system, as the end user would encounter in a retail
+ * display environment. If the advertised memory is not defined, it returns
+ * {@code getTotalMemory()} rounded.
+ *
+ * @hide
+ */
+ public static final long getAdvertisedMem() {
+ String formatSize = MemoryProperties.memory_ddr_size().orElse("0KB");
+ long memSize = FileUtils.parseSize(formatSize);
+
+ if (memSize == Long.MIN_VALUE) {
+ return FileUtils.roundStorageSize(getTotalMemory());
+ }
+
+ return memSize;
+ }
+
/** @hide */
@UnsupportedAppUsage
public static final native long getFreeMemory();
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 1b46107..1285d1e 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -345,6 +345,12 @@
* Calling this a second time invalidates the previously created hotword detector
* which can no longer be used to manage recognition.
*
+ * <p>Note: If there are any active detectors that are created by using
+ * {@link #createAlwaysOnHotwordDetector(String, Locale, PersistableBundle, SharedMemory,
+ * AlwaysOnHotwordDetector.Callback)} or {@link #createHotwordDetector(PersistableBundle,
+ * SharedMemory, HotwordDetector.Callback)}, call this will throw an
+ * {@link IllegalArgumentException}.
+ *
* @param keyphrase The keyphrase that's being used, for example "Hello Android".
* @param locale The locale for which the enrollment needs to be performed.
* @param callback The callback to notify of detection events.
@@ -377,6 +383,10 @@
* <p>Note: The system will trigger hotword detection service after calling this function when
* all conditions meet the requirements.
*
+ * <p>Note: If there are any active detectors that are created by using
+ * {@link #createAlwaysOnHotwordDetector(String, Locale, AlwaysOnHotwordDetector.Callback)},
+ * call this will throw an {@link IllegalArgumentException}.
+ *
* @param keyphrase The keyphrase that's being used, for example "Hello Android".
* @param locale The locale for which the enrollment needs to be performed.
* @param options Application configuration data provided by the
@@ -420,6 +430,14 @@
safelyShutdownAllHotwordDetectors();
}
+ for (HotwordDetector detector : mActiveHotwordDetectors) {
+ if (detector.isUsingHotwordDetectionService() != supportHotwordDetectionService) {
+ throw new IllegalArgumentException(
+ "It disallows to create trusted and non-trusted detectors "
+ + "at the same time.");
+ }
+ }
+
AlwaysOnHotwordDetector dspDetector = new AlwaysOnHotwordDetector(keyphrase, locale,
callback, mKeyphraseEnrollmentInfo, mSystemService,
getApplicationContext().getApplicationInfo().targetSdkVersion,
@@ -460,6 +478,10 @@
* devices where hardware filtering is available (such as through a DSP), it's highly
* recommended to use {@link #createAlwaysOnHotwordDetector} instead.
*
+ * <p>Note: If there are any active detectors that are created by using
+ * {@link #createAlwaysOnHotwordDetector(String, Locale, AlwaysOnHotwordDetector.Callback)},
+ * call this will throw an {@link IllegalArgumentException}.
+ *
* @param options Application configuration data to be provided to the
* {@link HotwordDetectionService}. PersistableBundle does not allow any remotable objects or
* other contents that can be used to communicate with other processes.
@@ -490,7 +512,11 @@
safelyShutdownAllHotwordDetectors();
} else {
for (HotwordDetector detector : mActiveHotwordDetectors) {
- if (detector instanceof SoftwareHotwordDetector) {
+ if (!detector.isUsingHotwordDetectionService()) {
+ throw new IllegalArgumentException(
+ "It disallows to create trusted and non-trusted detectors "
+ + "at the same time.");
+ } else if (detector instanceof SoftwareHotwordDetector) {
throw new IllegalArgumentException(
"There is already an active SoftwareHotwordDetector. "
+ "It must be destroyed to create a new one.");
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index f2c8355..f86f51fc 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -24,7 +24,6 @@
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Log;
-import android.widget.TextView;
import com.android.internal.util.BitUtils;
@@ -688,15 +687,27 @@
/**
* Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
- * It means the content is invalid or associated with an error.
- * For example, text that sets an error message, such as when input isn't in a valid format,
- * should send this event and use {@link AccessibilityNodeInfo#setError} to
- * provide more context.
+ * The source node changed its content validity returned by
+ * {@link AccessibilityNodeInfo#isContentInvalid}.
+ * The view changing content validity should call
+ * {@link AccessibilityNodeInfo#setContentInvalid} and then send this event.
*
- * @see AccessibilityNodeInfo#setError
- * @see TextView#setError
+ * @see AccessibilityNodeInfo#isContentInvalid
+ * @see AccessibilityNodeInfo#setContentInvalid
*/
- public static final int CONTENT_CHANGE_TYPE_INVALID = 0x0000400;
+ public static final int CONTENT_CHANGE_TYPE_CONTENT_INVALID = 0x0000400;
+
+ /**
+ * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
+ * The source node changed its erroneous content's error message returned by
+ * {@link AccessibilityNodeInfo#getError}.
+ * The view changing erroneous content's error message should call
+ * {@link AccessibilityNodeInfo#setError} and then send this event.
+ *
+ * @see AccessibilityNodeInfo#getError
+ * @see AccessibilityNodeInfo#setError
+ */
+ public static final int CONTENT_CHANGE_TYPE_ERROR = 0x0000800;
/** Change type for {@link #TYPE_SPEECH_STATE_CHANGE} event: another service is speaking. */
public static final int SPEECH_STATE_SPEAKING_START = 0x00000001;
@@ -823,7 +834,8 @@
CONTENT_CHANGE_TYPE_DRAG_STARTED,
CONTENT_CHANGE_TYPE_DRAG_DROPPED,
CONTENT_CHANGE_TYPE_DRAG_CANCELLED,
- CONTENT_CHANGE_TYPE_INVALID,
+ CONTENT_CHANGE_TYPE_CONTENT_INVALID,
+ CONTENT_CHANGE_TYPE_ERROR,
})
public @interface ContentChangeTypes {}
@@ -1090,7 +1102,9 @@
case CONTENT_CHANGE_TYPE_DRAG_STARTED: return "CONTENT_CHANGE_TYPE_DRAG_STARTED";
case CONTENT_CHANGE_TYPE_DRAG_DROPPED: return "CONTENT_CHANGE_TYPE_DRAG_DROPPED";
case CONTENT_CHANGE_TYPE_DRAG_CANCELLED: return "CONTENT_CHANGE_TYPE_DRAG_CANCELLED";
- case CONTENT_CHANGE_TYPE_INVALID: return "CONTENT_CHANGE_TYPE_INVALID";
+ case CONTENT_CHANGE_TYPE_CONTENT_INVALID:
+ return "CONTENT_CHANGE_TYPE_CONTENT_INVALID";
+ case CONTENT_CHANGE_TYPE_ERROR: return "CONTENT_CHANGE_TYPE_ERROR";
default: return Integer.toHexString(type);
}
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 752d02c..d11fa5f 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -7650,7 +7650,8 @@
createEditorIfNeeded();
mEditor.setError(error, icon);
notifyViewAccessibilityStateChangedIfNeeded(
- AccessibilityEvent.CONTENT_CHANGE_TYPE_INVALID);
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_ERROR
+ | AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_INVALID);
}
@Override
@@ -12221,6 +12222,11 @@
}
}
+ @Override
+ public boolean isAutoHandwritingEnabled() {
+ return super.isAutoHandwritingEnabled() && !isAnyPasswordInputType();
+ }
+
/** @hide */
@Override
public boolean isStylusHandwritingAvailable() {
diff --git a/core/java/com/android/internal/jank/EventLogTags.logtags b/core/java/com/android/internal/jank/EventLogTags.logtags
new file mode 100644
index 0000000..6139bce
--- /dev/null
+++ b/core/java/com/android/internal/jank/EventLogTags.logtags
@@ -0,0 +1,10 @@
+# See system/core/logcat/event.logtags for a description of the format of this file.
+
+option java_package com.android.internal.jank;
+
+# Marks a request to start tracing a CUJ. Doesn't mean the request was executed.
+37001 jank_cuj_events_begin_request (CUJ Type|1|5)
+# Marks a request to end tracing a CUJ. Doesn't mean the request was executed.
+37002 jank_cuj_events_end_request (CUJ Type|1|5)
+# Marks a request to cancel tracing a CUJ. Doesn't mean the request was executed.
+37003 jank_cuj_events_cancel_request (CUJ Type|1|5)
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 40d192e..76f33a6 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -541,6 +541,7 @@
public boolean begin(@NonNull Configuration.Builder builder) {
try {
final Configuration config = builder.build();
+ EventLogTags.writeJankCujEventsBeginRequest(config.mCujType);
final TrackerResult result = new TrackerResult();
final boolean success = config.getHandler().runWithScissors(
() -> result.mResult = beginInternal(config), EXECUTOR_TASK_TIMEOUT);
@@ -614,6 +615,7 @@
* @return boolean true if the tracker is ended successfully, false otherwise.
*/
public boolean end(@CujType int cujType) {
+ EventLogTags.writeJankCujEventsEndRequest(cujType);
FrameTracker tracker = getTracker(cujType);
// Skip this call since we haven't started a trace yet.
if (tracker == null) return false;
@@ -651,6 +653,7 @@
* @return boolean true if the tracker is cancelled successfully, false otherwise.
*/
public boolean cancel(@CujType int cujType) {
+ EventLogTags.writeJankCujEventsCancelRequest(cujType);
return cancel(cujType, REASON_CANCEL_NORMAL);
}
diff --git a/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java b/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java
index 3ecc7ff..bf65af3 100644
--- a/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java
+++ b/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java
@@ -131,7 +131,7 @@
new Light(1 /* id */, "Light1", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
Light.LIGHT_CAPABILITY_BRIGHTNESS),
new Light(2 /* id */, "Light2", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
- Light.LIGHT_CAPABILITY_RGB),
+ Light.LIGHT_CAPABILITY_COLOR_RGB),
new Light(3 /* id */, "Light3", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
0 /* capabilities */)
};
@@ -150,13 +150,13 @@
Light[] mockedLights = {
new Light(1 /* id */, "Light1", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
- Light.LIGHT_CAPABILITY_RGB),
+ Light.LIGHT_CAPABILITY_COLOR_RGB),
new Light(2 /* id */, "Light2", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
- Light.LIGHT_CAPABILITY_RGB),
+ Light.LIGHT_CAPABILITY_COLOR_RGB),
new Light(3 /* id */, "Light3", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
- Light.LIGHT_CAPABILITY_RGB),
+ Light.LIGHT_CAPABILITY_COLOR_RGB),
new Light(4 /* id */, "Light4", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
- Light.LIGHT_CAPABILITY_RGB)
+ Light.LIGHT_CAPABILITY_COLOR_RGB)
};
mockLights(mockedLights);
@@ -204,7 +204,7 @@
new Light(1 /* id */, "Light1", 0 /* ordinal */, Light.LIGHT_TYPE_PLAYER_ID,
0 /* capabilities */),
new Light(2 /* id */, "Light2", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
- Light.LIGHT_CAPABILITY_RGB | Light.LIGHT_CAPABILITY_BRIGHTNESS),
+ Light.LIGHT_CAPABILITY_COLOR_RGB | Light.LIGHT_CAPABILITY_BRIGHTNESS),
new Light(3 /* id */, "Light3", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
Light.LIGHT_CAPABILITY_BRIGHTNESS)
};
@@ -239,9 +239,9 @@
@Test
public void testLightCapabilities() throws Exception {
Light light = new Light(1 /* id */, "Light1", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
- Light.LIGHT_CAPABILITY_RGB | Light.LIGHT_CAPABILITY_BRIGHTNESS);
+ Light.LIGHT_CAPABILITY_COLOR_RGB | Light.LIGHT_CAPABILITY_BRIGHTNESS);
assertThat(light.getType()).isEqualTo(Light.LIGHT_TYPE_INPUT);
- assertThat(light.getCapabilities()).isEqualTo(Light.LIGHT_CAPABILITY_RGB
+ assertThat(light.getCapabilities()).isEqualTo(Light.LIGHT_CAPABILITY_COLOR_RGB
| Light.LIGHT_CAPABILITY_BRIGHTNESS);
assertTrue(light.hasBrightnessControl());
assertTrue(light.hasRgbControl());
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index 32c3a26..91fbe00 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -532,6 +532,56 @@
}
@Test
+ public void testParseSize() {
+ assertEquals(0L, FileUtils.parseSize("0MB"));
+ assertEquals(1_024L, FileUtils.parseSize("1024b"));
+ assertEquals(-1L, FileUtils.parseSize(" -1 b "));
+ assertEquals(0L, FileUtils.parseSize(" -0 gib "));
+ assertEquals(1_000L, FileUtils.parseSize("1K"));
+ assertEquals(1_000L, FileUtils.parseSize("1KB"));
+ assertEquals(10_000L, FileUtils.parseSize("10KB"));
+ assertEquals(100_000L, FileUtils.parseSize("100KB"));
+ assertEquals(1_000_000L, FileUtils.parseSize("1000KB"));
+ assertEquals(1_024_000L, FileUtils.parseSize("1000KiB"));
+ assertEquals(70_000_000L, FileUtils.parseSize("070M"));
+ assertEquals(70_000_000L, FileUtils.parseSize("070MB"));
+ assertEquals(73_400_320L, FileUtils.parseSize("70MiB"));
+ assertEquals(700_000_000L, FileUtils.parseSize("700000KB"));
+ assertEquals(200_000_000L, FileUtils.parseSize("+200MB"));
+ assertEquals(1_000_000_000L, FileUtils.parseSize("1000MB"));
+ assertEquals(1_000_000_000L, FileUtils.parseSize("+1000 mb"));
+ assertEquals(644_245_094_400L, FileUtils.parseSize("600GiB"));
+ assertEquals(999_000_000_000L, FileUtils.parseSize("999GB"));
+ assertEquals(999_000_000_000L, FileUtils.parseSize("999 gB"));
+ assertEquals(9_999_000_000_000L, FileUtils.parseSize("9999GB"));
+ assertEquals(9_000_000_000_000L, FileUtils.parseSize(" 9000 GB "));
+ assertEquals(1_234_000_000_000L, FileUtils.parseSize(" 1234 GB "));
+ assertEquals(1_234_567_890_000L, FileUtils.parseSize(" 1234567890 KB "));
+ }
+
+ @Test
+ public void testParseSize_invalidArguments() {
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize(null));
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize("null"));
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize(""));
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize(" "));
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize("KB"));
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize("123 dd"));
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize("Invalid"));
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize(" ABC890 KB "));
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize("-=+90 KB "));
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize("123"));
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize("--123"));
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize("-KB"));
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize("++123"));
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize("+"));
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize("+ 1 +"));
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize("+--+ 1 +"));
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize("1GB+"));
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize(" + 1234567890 KB "));
+ }
+
+ @Test
public void testTranslateMode() throws Exception {
assertTranslate("r", O_RDONLY, MODE_READ_ONLY);
diff --git a/core/tests/coretests/src/android/os/ProcessTest.java b/core/tests/coretests/src/android/os/ProcessTest.java
index ae4edb9..52846df 100644
--- a/core/tests/coretests/src/android/os/ProcessTest.java
+++ b/core/tests/coretests/src/android/os/ProcessTest.java
@@ -72,4 +72,7 @@
assertEquals(-1, Process.getThreadGroupLeader(BAD_PID));
}
+ public void testGetAdvertisedMem() {
+ assertTrue(Process.getTotalMemory() <= Process.getAdvertisedMem());
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index f4dda4c..80cdd1f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -484,12 +484,14 @@
ShellInit shellInit,
ShellCommandHandler shellCommandHandler,
TaskStackListenerImpl taskStackListener,
+ ActivityTaskManager activityTaskManager,
Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
@ShellMainThread ShellExecutor mainExecutor
) {
return Optional.ofNullable(
RecentTasksController.create(context, shellInit, shellCommandHandler,
- taskStackListener, desktopModeTaskRepository, mainExecutor));
+ taskStackListener, activityTaskManager, desktopModeTaskRepository,
+ mainExecutor));
}
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
index a5748f6..2a62552 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
@@ -17,6 +17,11 @@
package com.android.wm.shell.recents;
import com.android.wm.shell.common.annotations.ExternalThread;
+import com.android.wm.shell.util.GroupedRecentTaskInfo;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
/**
* Interface for interacting with the recent tasks.
@@ -29,4 +34,11 @@
default IRecentTasks createExternalInterface() {
return null;
}
+
+ /**
+ * Gets the set of recent tasks.
+ */
+ default void getRecentTasks(int maxNum, int flags, int userId, Executor callbackExecutor,
+ Consumer<List<GroupedRecentTaskInfo>> callback) {
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 6409e70..02b5a35 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -58,6 +58,8 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
/**
* Manages the recent task list from the system, caching it as necessary.
@@ -72,6 +74,7 @@
private final ShellExecutor mMainExecutor;
private final TaskStackListenerImpl mTaskStackListener;
private final RecentTasks mImpl = new RecentTasksImpl();
+ private final ActivityTaskManager mActivityTaskManager;
private IRecentTasksListener mListener;
private final boolean mIsDesktopMode;
@@ -96,6 +99,7 @@
ShellInit shellInit,
ShellCommandHandler shellCommandHandler,
TaskStackListenerImpl taskStackListener,
+ ActivityTaskManager activityTaskManager,
Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
@ShellMainThread ShellExecutor mainExecutor
) {
@@ -103,17 +107,19 @@
return null;
}
return new RecentTasksController(context, shellInit, shellCommandHandler, taskStackListener,
- desktopModeTaskRepository, mainExecutor);
+ activityTaskManager, desktopModeTaskRepository, mainExecutor);
}
RecentTasksController(Context context,
ShellInit shellInit,
ShellCommandHandler shellCommandHandler,
TaskStackListenerImpl taskStackListener,
+ ActivityTaskManager activityTaskManager,
Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
ShellExecutor mainExecutor) {
mContext = context;
mShellCommandHandler = shellCommandHandler;
+ mActivityTaskManager = activityTaskManager;
mIsDesktopMode = mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
mTaskStackListener = taskStackListener;
mDesktopModeTaskRepository = desktopModeTaskRepository;
@@ -270,15 +276,10 @@
}
@VisibleForTesting
- List<ActivityManager.RecentTaskInfo> getRawRecentTasks(int maxNum, int flags, int userId) {
- return ActivityTaskManager.getInstance().getRecentTasks(maxNum, flags, userId);
- }
-
- @VisibleForTesting
ArrayList<GroupedRecentTaskInfo> getRecentTasks(int maxNum, int flags, int userId) {
// Note: the returned task list is from the most-recent to least-recent order
- final List<ActivityManager.RecentTaskInfo> rawList = getRawRecentTasks(maxNum, flags,
- userId);
+ final List<ActivityManager.RecentTaskInfo> rawList = mActivityTaskManager.getRecentTasks(
+ maxNum, flags, userId);
// Make a mapping of task id -> task info
final SparseArray<ActivityManager.RecentTaskInfo> rawMapping = new SparseArray<>();
@@ -335,8 +336,9 @@
if (componentName == null) {
return null;
}
- List<ActivityManager.RecentTaskInfo> tasks = getRawRecentTasks(Integer.MAX_VALUE,
- ActivityManager.RECENT_IGNORE_UNAVAILABLE, ActivityManager.getCurrentUser());
+ List<ActivityManager.RecentTaskInfo> tasks = mActivityTaskManager.getRecentTasks(
+ Integer.MAX_VALUE, ActivityManager.RECENT_IGNORE_UNAVAILABLE,
+ ActivityManager.getCurrentUser());
for (int i = 0; i < tasks.size(); i++) {
final ActivityManager.RecentTaskInfo task = tasks.get(i);
if (task.isVisible) {
@@ -374,6 +376,16 @@
mIRecentTasks = new IRecentTasksImpl(RecentTasksController.this);
return mIRecentTasks;
}
+
+ @Override
+ public void getRecentTasks(int maxNum, int flags, int userId, Executor executor,
+ Consumer<List<GroupedRecentTaskInfo>> callback) {
+ mMainExecutor.execute(() -> {
+ List<GroupedRecentTaskInfo> tasks =
+ RecentTasksController.this.getRecentTasks(maxNum, flags, userId);
+ executor.execute(() -> callback.accept(tasks));
+ });
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index 9c7131a..8b36db920 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.windowdecor;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -240,6 +241,7 @@
private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) return true;
return DesktopModeStatus.IS_SUPPORTED
+ && taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD
&& mDisplayController.getDisplayContext(taskInfo.displayId)
.getResources().getConfiguration().smallestScreenWidthDp >= 600;
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index 2ccf121..f9d1e88 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -46,6 +46,66 @@
}
}
+fun FlickerTestParameter.splitScreenEntered(
+ component1: IComponentMatcher,
+ component2: IComponentMatcher,
+ fromOtherApp: Boolean
+) {
+ if (fromOtherApp) {
+ appWindowIsInvisibleAtStart(component1)
+ } else {
+ appWindowIsVisibleAtStart(component1)
+ }
+ appWindowIsInvisibleAtStart(component2)
+ splitScreenDividerIsInvisibleAtStart()
+
+ appWindowIsVisibleAtEnd(component1)
+ appWindowIsVisibleAtEnd(component2)
+ splitScreenDividerIsVisibleAtEnd()
+}
+
+fun FlickerTestParameter.splitScreenDismissed(
+ component1: IComponentMatcher,
+ component2: IComponentMatcher,
+ toHome: Boolean
+) {
+ appWindowIsVisibleAtStart(component1)
+ appWindowIsVisibleAtStart(component2)
+ splitScreenDividerIsVisibleAtStart()
+
+ appWindowIsInvisibleAtEnd(component1)
+ if (toHome) {
+ appWindowIsInvisibleAtEnd(component2)
+ } else {
+ appWindowIsVisibleAtEnd(component2)
+ }
+ splitScreenDividerIsInvisibleAtEnd()
+}
+
+fun FlickerTestParameter.splitScreenDividerIsVisibleAtStart() {
+ assertLayersStart {
+ this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
+ }
+}
+
+fun FlickerTestParameter.splitScreenDividerIsVisibleAtEnd() {
+ assertLayersEnd {
+ this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
+ }
+}
+
+fun FlickerTestParameter.splitScreenDividerIsInvisibleAtStart() {
+ assertLayersStart {
+ this.isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
+ }
+}
+
+fun FlickerTestParameter.splitScreenDividerIsInvisibleAtEnd() {
+ assertLayersEnd {
+ this.isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
+ }
+}
+
fun FlickerTestParameter.splitScreenDividerBecomesVisible() {
layerBecomesVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
}
@@ -271,6 +331,14 @@
}
}
+fun FlickerTestParameter.appWindowIsVisibleAtStart(
+ component: IComponentMatcher
+) {
+ assertWmStart {
+ this.isAppWindowVisible(component)
+ }
+}
+
fun FlickerTestParameter.appWindowIsVisibleAtEnd(
component: IComponentMatcher
) {
@@ -279,6 +347,22 @@
}
}
+fun FlickerTestParameter.appWindowIsInvisibleAtStart(
+ component: IComponentMatcher
+) {
+ assertWmStart {
+ this.isAppWindowInvisible(component)
+ }
+}
+
+fun FlickerTestParameter.appWindowIsInvisibleAtEnd(
+ component: IComponentMatcher
+) {
+ assertWmEnd {
+ this.isAppWindowInvisible(component)
+ }
+}
+
fun FlickerTestParameter.appWindowKeepVisible(
component: IComponentMatcher
) {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
index 7dbd279..5b044e3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
@@ -27,9 +27,13 @@
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
+import com.android.wm.shell.flicker.appWindowIsVisibleAtStart
import com.android.wm.shell.flicker.appWindowKeepVisible
import com.android.wm.shell.flicker.layerKeepVisible
import com.android.wm.shell.flicker.splitAppLayerBoundsKeepVisible
+import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtStart
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -64,36 +68,44 @@
@IwTest(focusArea = "sysui")
@Presubmit
@Test
+ fun cujCompleted() {
+ testSpec.appWindowIsVisibleAtStart(primaryApp)
+ testSpec.appWindowIsVisibleAtStart(textEditApp)
+ testSpec.splitScreenDividerIsVisibleAtStart()
+
+ testSpec.appWindowIsVisibleAtEnd(primaryApp)
+ testSpec.appWindowIsVisibleAtEnd(textEditApp)
+ testSpec.splitScreenDividerIsVisibleAtEnd()
+
+ // The validation of copied text is already done in SplitScreenUtils.copyContentInSplit()
+ }
+
+ @Presubmit
+ @Test
fun splitScreenDividerKeepVisible() = testSpec.layerKeepVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppLayerKeepVisible() = testSpec.layerKeepVisible(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun textEditAppLayerKeepVisible() = testSpec.layerKeepVisible(textEditApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppBoundsKeepVisible() = testSpec.splitAppLayerBoundsKeepVisible(
primaryApp, landscapePosLeft = tapl.isTablet, portraitPosTop = false)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun textEditAppBoundsKeepVisible() = testSpec.splitAppLayerBoundsKeepVisible(
textEditApp, landscapePosLeft = !tapl.isTablet, portraitPosTop = true)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppWindowKeepVisible() = testSpec.appWindowKeepVisible(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun textEditAppWindowKeepVisible() = testSpec.appWindowKeepVisible(textEditApp)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
index 3646fd7..838026f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
@@ -33,6 +33,7 @@
import com.android.wm.shell.flicker.layerBecomesInvisible
import com.android.wm.shell.flicker.layerIsVisibleAtEnd
import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesInvisible
+import com.android.wm.shell.flicker.splitScreenDismissed
import com.android.wm.shell.flicker.splitScreenDividerBecomesInvisible
import org.junit.FixMethodOrder
import org.junit.Test
@@ -75,25 +76,25 @@
@IwTest(focusArea = "sysui")
@Presubmit
@Test
+ fun cujCompleted() = testSpec.splitScreenDismissed(primaryApp, secondaryApp, toHome = false)
+
+ @Presubmit
+ @Test
fun splitScreenDividerBecomesInvisible() = testSpec.splitScreenDividerBecomesInvisible()
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppLayerBecomesInvisible() = testSpec.layerBecomesInvisible(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(secondaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppBoundsBecomesInvisible() = testSpec.splitAppLayerBoundsBecomesInvisible(
primaryApp, landscapePosLeft = tapl.isTablet, portraitPosTop = false)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppBoundsIsFullscreenAtEnd() {
@@ -116,12 +117,10 @@
}
}
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppWindowBecomesInvisible() = testSpec.appWindowBecomesInvisible(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(secondaryApp)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
index 80abedd..a764206 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
@@ -30,6 +30,7 @@
import com.android.wm.shell.flicker.appWindowBecomesInvisible
import com.android.wm.shell.flicker.layerBecomesInvisible
import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesInvisible
+import com.android.wm.shell.flicker.splitScreenDismissed
import com.android.wm.shell.flicker.splitScreenDividerBecomesInvisible
import org.junit.FixMethodOrder
import org.junit.Test
@@ -64,13 +65,15 @@
.waitForAndVerify()
}
}
-
@IwTest(focusArea = "sysui")
@Presubmit
@Test
+ fun cujCompleted() = testSpec.splitScreenDismissed(primaryApp, secondaryApp, toHome = true)
+
+ @Presubmit
+ @Test
fun splitScreenDividerBecomesInvisible() = testSpec.splitScreenDividerBecomesInvisible()
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppLayerBecomesInvisible() = testSpec.layerBecomesInvisible(primaryApp)
@@ -86,18 +89,15 @@
fun primaryAppBoundsBecomesInvisible() = testSpec.splitAppLayerBoundsBecomesInvisible(
primaryApp, landscapePosLeft = tapl.isTablet, portraitPosTop = false)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppBoundsBecomesInvisible() = testSpec.splitAppLayerBoundsBecomesInvisible(
secondaryApp, landscapePosLeft = !tapl.isTablet, portraitPosTop = true)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppWindowBecomesInvisible() = testSpec.appWindowBecomesInvisible(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppWindowBecomesInvisible() = testSpec.appWindowBecomesInvisible(secondaryApp)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
index 2915787..ba02317 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
@@ -28,9 +28,13 @@
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
+import com.android.wm.shell.flicker.appWindowIsVisibleAtStart
import com.android.wm.shell.flicker.appWindowKeepVisible
import com.android.wm.shell.flicker.layerKeepVisible
import com.android.wm.shell.flicker.splitAppLayerBoundsChanges
+import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtStart
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -63,14 +67,27 @@
@IwTest(focusArea = "sysui")
@Presubmit
@Test
+ fun cujCompleted() {
+ testSpec.appWindowIsVisibleAtStart(primaryApp)
+ testSpec.appWindowIsVisibleAtStart(secondaryApp)
+ testSpec.splitScreenDividerIsVisibleAtStart()
+
+ testSpec.appWindowIsVisibleAtEnd(primaryApp)
+ testSpec.appWindowIsVisibleAtEnd(secondaryApp)
+ testSpec.splitScreenDividerIsVisibleAtEnd()
+
+ // TODO(b/246490534): Add validation for resized app after withAppTransitionIdle is
+ // robust enough to get the correct end state.
+ }
+
+ @Presubmit
+ @Test
fun splitScreenDividerKeepVisible() = testSpec.layerKeepVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppLayerKeepVisible() = testSpec.layerKeepVisible(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppLayerVisibilityChanges() {
@@ -83,23 +100,19 @@
}
}
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppWindowKeepVisible() = testSpec.appWindowKeepVisible(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppWindowKeepVisible() = testSpec.appWindowKeepVisible(secondaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppBoundsChanges() = testSpec.splitAppLayerBoundsChanges(
primaryApp, landscapePosLeft = true, portraitPosTop = false)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppBoundsChanges() = testSpec.splitAppLayerBoundsChanges(
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
index 8e041a7..bb44789 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
@@ -35,6 +35,7 @@
import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisibleByDrag
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
+import com.android.wm.shell.flicker.splitScreenEntered
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
@@ -82,13 +83,16 @@
@IwTest(focusArea = "sysui")
@Presubmit
@Test
+ fun cujCompleted() = testSpec.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = false)
+
+ @Presubmit
+ @Test
fun splitScreenDividerBecomesVisible() {
Assume.assumeFalse(isShellTransitionsEnabled)
testSpec.splitScreenDividerBecomesVisible()
}
// TODO(b/245472831): Back to splitScreenDividerBecomesVisible after shell transition ready.
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun splitScreenDividerIsVisibleAtEnd_ShellTransit() {
@@ -98,12 +102,10 @@
}
}
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppLayerBecomesVisible() {
@@ -127,24 +129,20 @@
testSpec.layerBecomesVisible(secondaryApp)
}
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
primaryApp, landscapePosLeft = false, portraitPosTop = false)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisibleByDrag(
secondaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
index 2ee12f1..e208196 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
@@ -34,6 +34,7 @@
import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisibleByDrag
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
+import com.android.wm.shell.flicker.splitScreenEntered
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
@@ -87,13 +88,16 @@
@IwTest(focusArea = "sysui")
@Presubmit
@Test
+ fun cujCompleted() = testSpec.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = false)
+
+ @Presubmit
+ @Test
fun splitScreenDividerBecomesVisible() {
Assume.assumeFalse(isShellTransitionsEnabled)
testSpec.splitScreenDividerBecomesVisible()
}
// TODO(b/245472831): Back to splitScreenDividerBecomesVisible after shell transition ready.
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun splitScreenDividerIsVisibleAtEnd_ShellTransit() {
@@ -103,12 +107,10 @@
}
}
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppLayerBecomesVisible() {
@@ -132,24 +134,20 @@
testSpec.layerBecomesVisible(sendNotificationApp)
}
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
primaryApp, landscapePosLeft = false, portraitPosTop = false)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisibleByDrag(
sendNotificationApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(sendNotificationApp)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
index a11874e..84d2e6a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
@@ -35,6 +35,7 @@
import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisibleByDrag
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
+import com.android.wm.shell.flicker.splitScreenEntered
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
@@ -85,13 +86,16 @@
@IwTest(focusArea = "sysui")
@Presubmit
@Test
+ fun cujCompleted() = testSpec.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = false)
+
+ @Presubmit
+ @Test
fun splitScreenDividerBecomesVisible() {
Assume.assumeFalse(isShellTransitionsEnabled)
testSpec.splitScreenDividerBecomesVisible()
}
// TODO(b/245472831): Back to splitScreenDividerBecomesVisible after shell transition ready.
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun splitScreenDividerIsVisibleAtEnd_ShellTransit() {
@@ -101,12 +105,10 @@
}
}
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppLayerBecomesVisible() {
@@ -130,24 +132,20 @@
testSpec.layerBecomesVisible(secondaryApp)
}
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
primaryApp, landscapePosLeft = false, portraitPosTop = false)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisibleByDrag(
secondaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
index 6064b52..23623aa 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
@@ -31,6 +31,7 @@
import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisible
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
+import com.android.wm.shell.flicker.splitScreenEntered
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -71,36 +72,34 @@
@IwTest(focusArea = "sysui")
@Presubmit
@Test
+ fun cujCompleted() = testSpec.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = true)
+
+ @Presubmit
+ @Test
fun splitScreenDividerBecomesVisible() = testSpec.splitScreenDividerBecomesVisible()
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(secondaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
primaryApp, landscapePosLeft = tapl.isTablet, portraitPosTop = false)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisible(
secondaryApp, landscapePosLeft = !tapl.isTablet, portraitPosTop = true)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
index f06dd66f6..025bb408 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
@@ -31,9 +31,12 @@
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
+import com.android.wm.shell.flicker.appWindowIsVisibleAtStart
import com.android.wm.shell.flicker.layerIsVisibleAtEnd
import com.android.wm.shell.flicker.layerKeepVisible
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtStart
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -111,19 +114,31 @@
@IwTest(focusArea = "sysui")
@Presubmit
@Test
+ fun cujCompleted() {
+ testSpec.appWindowIsVisibleAtStart(primaryApp)
+ testSpec.appWindowIsVisibleAtStart(secondaryApp)
+ testSpec.splitScreenDividerIsVisibleAtStart()
+
+ testSpec.appWindowIsVisibleAtEnd(primaryApp)
+ testSpec.appWindowIsVisibleAtEnd(secondaryApp)
+ testSpec.splitScreenDividerIsVisibleAtEnd()
+
+ // TODO(b/246490534): Add validation for switched app after withAppTransitionIdle is
+ // robust enough to get the correct end state.
+ }
+
+ @Presubmit
+ @Test
fun splitScreenDividerKeepVisible() = testSpec.layerKeepVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(secondaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
@@ -136,12 +151,10 @@
fun secondaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
secondaryApp, landscapePosLeft = tapl.isTablet, portraitPosTop = false)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(secondaryApp)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
index 5c30116..9947a53 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
@@ -30,6 +30,7 @@
import com.android.wm.shell.flicker.layerBecomesVisible
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
+import com.android.wm.shell.flicker.splitScreenEntered
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -69,36 +70,34 @@
@IwTest(focusArea = "sysui")
@Presubmit
@Test
+ fun cujCompleted() = testSpec.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = true)
+
+ @Presubmit
+ @Test
fun splitScreenDividerBecomesVisible() = testSpec.splitScreenDividerBecomesVisible()
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(secondaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
primaryApp, landscapePosLeft = tapl.isTablet, portraitPosTop = false)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
secondaryApp, landscapePosLeft = !tapl.isTablet, portraitPosTop = true)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
index 9c66a37..3716dc9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
@@ -30,6 +30,7 @@
import com.android.wm.shell.flicker.layerBecomesVisible
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
+import com.android.wm.shell.flicker.splitScreenEntered
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -68,36 +69,34 @@
@IwTest(focusArea = "sysui")
@Presubmit
@Test
+ fun cujCompleted() = testSpec.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = true)
+
+ @Presubmit
+ @Test
fun splitScreenDividerBecomesVisible() = testSpec.splitScreenDividerBecomesVisible()
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(secondaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
primaryApp, landscapePosLeft = tapl.isTablet, portraitPosTop = false)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
secondaryApp, landscapePosLeft = !tapl.isTablet, portraitPosTop = true)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
index e8862bd..db07f21 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
@@ -30,6 +30,7 @@
import com.android.wm.shell.flicker.layerBecomesVisible
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
+import com.android.wm.shell.flicker.splitScreenEntered
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -70,36 +71,34 @@
@IwTest(focusArea = "sysui")
@Presubmit
@Test
+ fun cujCompleted() = testSpec.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = true)
+
+ @Presubmit
+ @Test
fun splitScreenDividerBecomesVisible() = testSpec.splitScreenDividerBecomesVisible()
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(secondaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
primaryApp, landscapePosLeft = tapl.isTablet, portraitPosTop = false)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
secondaryApp, landscapePosLeft = !tapl.isTablet, portraitPosTop = true)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index 70fee2b..b8aaaa7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -40,6 +40,7 @@
import static java.lang.Integer.MAX_VALUE;
import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Rect;
@@ -52,7 +53,6 @@
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
-import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
@@ -68,7 +68,9 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.Optional;
+import java.util.function.Consumer;
/**
* Tests for {@link RecentTasksController}.
@@ -85,11 +87,13 @@
private ShellCommandHandler mShellCommandHandler;
@Mock
private DesktopModeTaskRepository mDesktopModeTaskRepository;
+ @Mock
+ private ActivityTaskManager mActivityTaskManager;
private ShellTaskOrganizer mShellTaskOrganizer;
private RecentTasksController mRecentTasksController;
private ShellInit mShellInit;
- private ShellExecutor mMainExecutor;
+ private TestShellExecutor mMainExecutor;
@Before
public void setUp() {
@@ -97,8 +101,8 @@
when(mContext.getPackageManager()).thenReturn(mock(PackageManager.class));
mShellInit = spy(new ShellInit(mMainExecutor));
mRecentTasksController = spy(new RecentTasksController(mContext, mShellInit,
- mShellCommandHandler, mTaskStackListener, Optional.of(mDesktopModeTaskRepository),
- mMainExecutor));
+ mShellCommandHandler, mTaskStackListener, mActivityTaskManager,
+ Optional.of(mDesktopModeTaskRepository), mMainExecutor));
mShellTaskOrganizer = new ShellTaskOrganizer(mShellInit, mShellCommandHandler,
null /* sizeCompatUI */, Optional.empty(), Optional.of(mRecentTasksController),
mMainExecutor);
@@ -188,6 +192,37 @@
}
@Test
+ public void testGetRecentTasks_ReturnsRecentTasksAsynchronously() {
+ @SuppressWarnings("unchecked")
+ final List<GroupedRecentTaskInfo>[] recentTasks = new List[1];
+ Consumer<List<GroupedRecentTaskInfo>> consumer = argument -> recentTasks[0] = argument;
+ ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
+ ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
+ ActivityManager.RecentTaskInfo t3 = makeTaskInfo(3);
+ ActivityManager.RecentTaskInfo t4 = makeTaskInfo(4);
+ ActivityManager.RecentTaskInfo t5 = makeTaskInfo(5);
+ ActivityManager.RecentTaskInfo t6 = makeTaskInfo(6);
+ setRawList(t1, t2, t3, t4, t5, t6);
+
+ // Mark a couple pairs [t2, t4], [t3, t5]
+ SplitBounds pair1Bounds = new SplitBounds(new Rect(), new Rect(), 2, 4);
+ SplitBounds pair2Bounds = new SplitBounds(new Rect(), new Rect(), 3, 5);
+
+ mRecentTasksController.addSplitPair(t2.taskId, t4.taskId, pair1Bounds);
+ mRecentTasksController.addSplitPair(t3.taskId, t5.taskId, pair2Bounds);
+
+ mRecentTasksController.asRecentTasks()
+ .getRecentTasks(MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0, Runnable::run, consumer);
+ mMainExecutor.flushAll();
+
+ assertGroupedTasksListEquals(recentTasks[0],
+ t1.taskId, -1,
+ t2.taskId, t4.taskId,
+ t3.taskId, t5.taskId,
+ t6.taskId, -1);
+ }
+
+ @Test
public void testGetRecentTasks_groupActiveFreeformTasks() {
StaticMockitoSession mockitoSession = mockitoSession().mockStatic(
DesktopModeStatus.class).startMocking();
@@ -296,7 +331,7 @@
for (ActivityManager.RecentTaskInfo task : tasks) {
rawList.add(task);
}
- doReturn(rawList).when(mRecentTasksController).getRawRecentTasks(anyInt(), anyInt(),
+ doReturn(rawList).when(mActivityTaskManager).getRecentTasks(anyInt(), anyInt(),
anyInt());
return rawList;
}
@@ -307,7 +342,7 @@
* @param expectedTaskIds list of task ids that map to the flattened task ids of the tasks in
* the grouped task list
*/
- private void assertGroupedTasksListEquals(ArrayList<GroupedRecentTaskInfo> recentTasks,
+ private void assertGroupedTasksListEquals(List<GroupedRecentTaskInfo> recentTasks,
int... expectedTaskIds) {
int[] flattenedTaskIds = new int[recentTasks.size() * 2];
for (int i = 0; i < recentTasks.size(); i++) {
diff --git a/media/native/midi/Android.bp b/media/native/midi/Android.bp
index 7acb8c7..a991a71f 100644
--- a/media/native/midi/Android.bp
+++ b/media/native/midi/Android.bp
@@ -74,4 +74,8 @@
symbol_file: "libamidi.map.txt",
first_version: "29",
+ export_header_libs: [
+ "amidi",
+ ],
+
}
diff --git a/native/android/Android.bp b/native/android/Android.bp
index 32b7a07..8594ba5 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -27,6 +27,9 @@
symbol_file: "libandroid.map.txt",
first_version: "9",
unversioned_until: "current",
+ export_header_libs: [
+ "libandroid_headers",
+ ],
}
cc_defaults {
diff --git a/packages/SettingsLib/Spa/TEST_MAPPING b/packages/SettingsLib/Spa/TEST_MAPPING
index ef3db4a..b4b65d4 100644
--- a/packages/SettingsLib/Spa/TEST_MAPPING
+++ b/packages/SettingsLib/Spa/TEST_MAPPING
@@ -2,6 +2,9 @@
"presubmit": [
{
"name": "SpaLibTests"
+ },
+ {
+ "name": "SpaPrivilegedLibTests"
}
]
}
diff --git a/packages/SettingsLib/Spa/build.gradle b/packages/SettingsLib/Spa/build.gradle
index f8667ed..6384cad 100644
--- a/packages/SettingsLib/Spa/build.gradle
+++ b/packages/SettingsLib/Spa/build.gradle
@@ -22,7 +22,7 @@
}
}
plugins {
- id 'com.android.application' version '7.3.0-rc01' apply false
- id 'com.android.library' version '7.3.0-rc01' apply false
+ id 'com.android.application' version '7.3.0' apply false
+ id 'com.android.library' version '7.3.0' apply false
id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt
index 1ebc5da..332d5a8 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt
@@ -18,8 +18,4 @@
import com.android.settingslib.spa.framework.DebugActivity
-class GalleryDebugActivity : DebugActivity(
- SpaEnvironment.EntryRepository,
- browseActivityClass = MainActivity::class.java,
- entryProviderAuthorities = "com.android.spa.gallery.provider",
-)
+class GalleryDebugActivity : DebugActivity(GallerySpaEnvironment)
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryEntryProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryEntryProvider.kt
index d3e0096..5e04861 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryEntryProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryEntryProvider.kt
@@ -18,7 +18,4 @@
import com.android.settingslib.spa.framework.EntryProvider
-class GalleryEntryProvider : EntryProvider(
- SpaEnvironment.EntryRepository,
- browseActivityClass = MainActivity::class.java,
-)
+class GalleryEntryProvider : EntryProvider(GallerySpaEnvironment)
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/SpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
similarity index 89%
rename from packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/SpaEnvironment.kt
rename to packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
index 6fe88e1..33c4d77 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/SpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
@@ -18,9 +18,9 @@
import android.os.Bundle
import androidx.navigation.NamedNavArgument
-import com.android.settingslib.spa.framework.common.SettingsEntryRepository
import com.android.settingslib.spa.framework.common.SettingsPage
import com.android.settingslib.spa.framework.common.SettingsPageProviderRepository
+import com.android.settingslib.spa.framework.common.SpaEnvironment
import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
import com.android.settingslib.spa.gallery.home.HomePageProvider
import com.android.settingslib.spa.gallery.page.ArgumentPageProvider
@@ -62,9 +62,8 @@
)
}
-object SpaEnvironment {
- val PageProviderRepository: SettingsPageProviderRepository by
- lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
+object GallerySpaEnvironment : SpaEnvironment() {
+ override val pageProviderRepository = lazy {
SettingsPageProviderRepository(
allPageProviders = listOf(
HomePageProvider,
@@ -88,9 +87,7 @@
)
}
- val EntryRepository: SettingsEntryRepository by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
- SettingsEntryRepository(PageProviderRepository)
- }
+ override val browseActivityClass = MainActivity::class.java
- // TODO: add other environment setup here.
+ override val entryProviderAuthorities = "com.android.spa.gallery.provider"
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/MainActivity.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/MainActivity.kt
index a063847..5e859ce 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/MainActivity.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/MainActivity.kt
@@ -18,4 +18,4 @@
import com.android.settingslib.spa.framework.BrowseActivity
-class MainActivity : BrowseActivity(SpaEnvironment.PageProviderRepository)
+class MainActivity : BrowseActivity(GallerySpaEnvironment)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
index 0bb631a..138ea02 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
@@ -31,7 +31,7 @@
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.android.settingslib.spa.R
-import com.android.settingslib.spa.framework.common.SettingsPageProviderRepository
+import com.android.settingslib.spa.framework.common.SpaEnvironment
import com.android.settingslib.spa.framework.compose.localNavController
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.framework.util.navRoute
@@ -48,9 +48,9 @@
* $ adb shell am start -n <BrowseActivityComponent> -e spa:SpaActivity:destination HOME
* $ adb shell am start -n <BrowseActivityComponent> -e spa:SpaActivity:destination ARGUMENT/bar/5
*/
-open class BrowseActivity(
- private val sppRepository: SettingsPageProviderRepository,
-) : ComponentActivity() {
+open class BrowseActivity(spaEnvironment: SpaEnvironment) : ComponentActivity() {
+ private val sppRepository by spaEnvironment.pageProviderRepository
+
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(R.style.Theme_SpaLib_DayNight)
super.onCreate(savedInstanceState)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt
index c698d9c..85fc366 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt
@@ -35,8 +35,8 @@
import com.android.settingslib.spa.R
import com.android.settingslib.spa.framework.BrowseActivity.Companion.KEY_DESTINATION
import com.android.settingslib.spa.framework.common.SettingsEntry
-import com.android.settingslib.spa.framework.common.SettingsEntryRepository
import com.android.settingslib.spa.framework.common.SettingsPage
+import com.android.settingslib.spa.framework.common.SpaEnvironment
import com.android.settingslib.spa.framework.compose.localNavController
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.compose.toState
@@ -61,11 +61,9 @@
* For gallery, Activity = com.android.settingslib.spa.gallery/.GalleryDebugActivity
* For SettingsGoogle, Activity = com.android.settings/.spa.SpaDebugActivity
*/
-open class DebugActivity(
- private val entryRepository: SettingsEntryRepository,
- private val browseActivityClass: Class<*>,
- private val entryProviderAuthorities: String? = null,
-) : ComponentActivity() {
+open class DebugActivity(private val spaEnvironment: SpaEnvironment) : ComponentActivity() {
+ private val entryRepository by spaEnvironment.entryRepository
+
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(R.style.Theme_SpaLib_DayNight)
super.onCreate(savedInstanceState)
@@ -79,7 +77,7 @@
}
private fun displayDebugMessage() {
- if (entryProviderAuthorities == null) return
+ val entryProviderAuthorities = spaEnvironment.entryProviderAuthorities ?: return
try {
val query = EntryProvider.QueryEnum.PAGE_INFO_QUERY
@@ -216,7 +214,7 @@
if (page.hasRuntimeParam()) return null
val context = LocalContext.current
val route = page.buildRoute()
- val intent = Intent(context, browseActivityClass).apply {
+ val intent = Intent(context, spaEnvironment.browseActivityClass).apply {
putExtra(KEY_DESTINATION, route)
}
return {
@@ -230,7 +228,7 @@
if (entry.hasRuntimeParam()) return null
val context = LocalContext.current
val route = entry.buildRoute()
- val intent = Intent(context, browseActivityClass).apply {
+ val intent = Intent(context, spaEnvironment.browseActivityClass).apply {
putExtra(KEY_DESTINATION, route)
}
return {
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/EntryProvider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/EntryProvider.kt
index d923c1c..f0ec83b 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/EntryProvider.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/EntryProvider.kt
@@ -28,8 +28,8 @@
import android.database.MatrixCursor
import android.net.Uri
import android.util.Log
-import com.android.settingslib.spa.framework.common.SettingsEntryRepository
import com.android.settingslib.spa.framework.common.SettingsPage
+import com.android.settingslib.spa.framework.common.SpaEnvironment
/**
* The content provider to return entry related data, which can be used for search and hierarchy.
@@ -42,10 +42,9 @@
* $ adb shell content query --uri content://<AuthorityPath>/page_info
* $ adb shell content query --uri content://<AuthorityPath>/entry_info
*/
-open class EntryProvider(
- private val entryRepository: SettingsEntryRepository,
- private val browseActivityClass: Class<*>? = null,
-) : ContentProvider() {
+open class EntryProvider(spaEnvironment: SpaEnvironment) : ContentProvider() {
+ private val entryRepository by spaEnvironment.entryRepository
+ private val browseActivityClass = spaEnvironment.browseActivityClass
/**
* Enum to define all column names in provider.
@@ -220,7 +219,7 @@
}
private fun createBrowsePageIntent(page: SettingsPage): Intent {
- if (context == null || browseActivityClass == null || page.hasRuntimeParam())
+ if (context == null || page.hasRuntimeParam())
return Intent()
return Intent().setComponent(ComponentName(context!!, browseActivityClass)).apply {
@@ -231,8 +230,7 @@
private fun createBrowsePageAdbCommand(page: SettingsPage): String? {
if (context == null || page.hasRuntimeParam()) return null
val packageName = context!!.packageName
- val activityName =
- browseActivityClass?.name?.replace(packageName, "") ?: "<browse-activity-class>"
+ val activityName = browseActivityClass.name.replace(packageName, "")
return "adb shell am start -n $packageName/$activityName" +
" -e ${BrowseActivity.KEY_DESTINATION} ${page.buildRoute()}"
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt
new file mode 100644
index 0000000..111555b
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.framework.common
+
+import android.app.Activity
+
+abstract class SpaEnvironment {
+ abstract val pageProviderRepository: Lazy<SettingsPageProviderRepository>
+
+ val entryRepository = lazy { SettingsEntryRepository(pageProviderRepository.value) }
+
+ abstract val browseActivityClass: Class<out Activity>
+
+ open val entryProviderAuthorities: String? = null
+
+ // TODO: add other environment setup here.
+}
diff --git a/packages/SettingsLib/SpaPrivileged/Android.bp b/packages/SettingsLib/SpaPrivileged/Android.bp
index 082ce97..e7e37e4 100644
--- a/packages/SettingsLib/SpaPrivileged/Android.bp
+++ b/packages/SettingsLib/SpaPrivileged/Android.bp
@@ -45,3 +45,9 @@
"-J-Xmx4G",
],
}
+
+// Expose the srcs to tests, so the tests can access the internal classes.
+filegroup {
+ name: "SpaPrivilegedLib_srcs",
+ srcs: ["src/**/*.kt"],
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppsRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt
similarity index 80%
rename from packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppsRepository.kt
rename to packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt
index bb94b33..ee89003 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppsRepository.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt
@@ -21,7 +21,6 @@
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
-import android.content.pm.UserInfo
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
@@ -30,14 +29,25 @@
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
-class AppsRepository(context: Context) {
+/**
+ * The config used to load the App List.
+ */
+internal data class AppListConfig(
+ val userId: Int,
+ val showInstantApps: Boolean,
+)
+
+/**
+ * The repository to load the App List data.
+ */
+internal class AppListRepository(context: Context) {
private val packageManager = context.packageManager
- fun loadApps(userInfoFlow: Flow<UserInfo>): Flow<List<ApplicationInfo>> = userInfoFlow
+ fun loadApps(configFlow: Flow<AppListConfig>): Flow<List<ApplicationInfo>> = configFlow
.map { loadApps(it) }
.flowOn(Dispatchers.Default)
- private suspend fun loadApps(userInfo: UserInfo): List<ApplicationInfo> {
+ private suspend fun loadApps(config: AppListConfig): List<ApplicationInfo> {
return coroutineScope {
val hiddenSystemModulesDeferred = async {
packageManager.getInstalledModules(0)
@@ -50,11 +60,11 @@
PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS).toLong()
)
val installedApplicationsAsUser =
- packageManager.getInstalledApplicationsAsUser(flags, userInfo.id)
+ packageManager.getInstalledApplicationsAsUser(flags, config.userId)
val hiddenSystemModules = hiddenSystemModulesDeferred.await()
installedApplicationsAsUser.filter { app ->
- app.isInAppList(hiddenSystemModules)
+ app.isInAppList(config.showInstantApps, hiddenSystemModules)
}
}
}
@@ -63,9 +73,7 @@
userIdFlow: Flow<Int>,
showSystemFlow: Flow<Boolean>,
): Flow<(app: ApplicationInfo) -> Boolean> =
- userIdFlow.combine(showSystemFlow) { userId, showSystem ->
- showSystemPredicate(userId, showSystem)
- }
+ userIdFlow.combine(showSystemFlow, ::showSystemPredicate)
private suspend fun showSystemPredicate(
userId: Int,
@@ -102,12 +110,15 @@
}
companion object {
- private fun ApplicationInfo.isInAppList(hiddenSystemModules: Set<String>) =
- when {
- packageName in hiddenSystemModules -> false
- enabled -> true
- enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER -> true
- else -> false
- }
+ private fun ApplicationInfo.isInAppList(
+ showInstantApps: Boolean,
+ hiddenSystemModules: Set<String>,
+ ) = when {
+ !showInstantApps && isInstantApp -> false
+ packageName in hiddenSystemModules -> false
+ enabled -> true
+ isDisabledUntilUsed -> true
+ else -> false
+ }
}
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListViewModel.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListViewModel.kt
index 9265158..1e487da 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListViewModel.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListViewModel.kt
@@ -18,7 +18,6 @@
import android.app.Application
import android.content.pm.ApplicationInfo
-import android.content.pm.UserInfo
import android.icu.text.Collator
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
@@ -48,28 +47,29 @@
internal class AppListViewModel<T : AppRecord>(
application: Application,
) : AndroidViewModel(application) {
- val userInfo = StateFlowBridge<UserInfo>()
+ val appListConfig = StateFlowBridge<AppListConfig>()
val listModel = StateFlowBridge<AppListModel<T>>()
val showSystem = StateFlowBridge<Boolean>()
val option = StateFlowBridge<Int>()
val searchQuery = StateFlowBridge<String>()
- private val appsRepository = AppsRepository(application)
+ private val appListRepository = AppListRepository(application)
private val appRepository = AppRepositoryImpl(application)
private val collator = Collator.getInstance().freeze()
private val labelMap = ConcurrentHashMap<String, String>()
private val scope = viewModelScope + Dispatchers.Default
- private val userIdFlow = userInfo.flow.map { it.id }
+ private val userIdFlow = appListConfig.flow.map { it.userId }
private val recordListFlow = listModel.flow
- .flatMapLatest { it.transform(userIdFlow, appsRepository.loadApps(userInfo.flow)) }
+ .flatMapLatest { it.transform(userIdFlow, appListRepository.loadApps(appListConfig.flow)) }
.shareIn(scope = scope, started = SharingStarted.Eagerly, replay = 1)
- private val systemFilteredFlow = appsRepository.showSystemPredicate(userIdFlow, showSystem.flow)
- .combine(recordListFlow) { showAppPredicate, recordList ->
- recordList.filter { showAppPredicate(it.app) }
- }
+ private val systemFilteredFlow =
+ appListRepository.showSystemPredicate(userIdFlow, showSystem.flow)
+ .combine(recordListFlow) { showAppPredicate, recordList ->
+ recordList.filter { showAppPredicate(it.app) }
+ }
val appListDataFlow = option.flow.flatMapLatest(::filterAndSort)
.combine(searchQuery.flow) { appListData, searchQuery ->
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/Apps.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/ApplicationInfos.kt
similarity index 90%
rename from packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/Apps.kt
rename to packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/ApplicationInfos.kt
index 96e3e77..f9f75fb 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/Apps.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/ApplicationInfos.kt
@@ -32,8 +32,8 @@
fun ApplicationInfo.hasFlag(flag: Int): Boolean = (flags and flag) > 0
/** Checks whether the application is disabled until used. */
-fun ApplicationInfo.isDisabledUntilUsed(): Boolean =
- enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
+val ApplicationInfo.isDisabledUntilUsed: Boolean
+ get() = enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
/** Converts to the route string which used in navigation. */
fun ApplicationInfo.toRoute() = "$packageName/$userId"
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
index 315dc5d..6318b4e 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
@@ -16,7 +16,6 @@
package com.android.settingslib.spaprivileged.template.app
-import android.content.pm.UserInfo
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
@@ -33,6 +32,7 @@
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.widget.ui.PlaceholderTitle
import com.android.settingslib.spaprivileged.R
+import com.android.settingslib.spaprivileged.model.app.AppListConfig
import com.android.settingslib.spaprivileged.model.app.AppListData
import com.android.settingslib.spaprivileged.model.app.AppListModel
import com.android.settingslib.spaprivileged.model.app.AppListViewModel
@@ -41,17 +41,22 @@
private const val TAG = "AppList"
+/**
+ * The template to render an App List.
+ *
+ * This UI element will take the remaining space on the screen to show the App List.
+ */
@Composable
internal fun <T : AppRecord> AppList(
- userInfo: UserInfo,
+ appListConfig: AppListConfig,
listModel: AppListModel<T>,
showSystem: State<Boolean>,
option: State<Int>,
searchQuery: State<String>,
appItem: @Composable (itemState: AppListItemModel<T>) -> Unit,
) {
- LogCompositions(TAG, userInfo.id.toString())
- val appListData = loadAppEntries(userInfo, listModel, showSystem, option, searchQuery)
+ LogCompositions(TAG, appListConfig.userId.toString())
+ val appListData = loadAppEntries(appListConfig, listModel, showSystem, option, searchQuery)
AppListWidget(appListData, listModel, appItem)
}
@@ -85,14 +90,14 @@
@Composable
private fun <T : AppRecord> loadAppEntries(
- userInfo: UserInfo,
+ appListConfig: AppListConfig,
listModel: AppListModel<T>,
showSystem: State<Boolean>,
option: State<Int>,
searchQuery: State<String>,
): State<AppListData<T>?> {
- val viewModel: AppListViewModel<T> = viewModel(key = userInfo.id.toString())
- viewModel.userInfo.setIfAbsent(userInfo)
+ val viewModel: AppListViewModel<T> = viewModel(key = appListConfig.userId.toString())
+ viewModel.appListConfig.setIfAbsent(appListConfig)
viewModel.listModel.setIfAbsent(listModel)
viewModel.showSystem.Sync(showSystem)
viewModel.option.Sync(option)
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt
index d537ec2..fb03d2c 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt
@@ -36,14 +36,19 @@
import com.android.settingslib.spa.widget.scaffold.SettingsScaffold
import com.android.settingslib.spa.widget.ui.Spinner
import com.android.settingslib.spaprivileged.R
+import com.android.settingslib.spaprivileged.model.app.AppListConfig
import com.android.settingslib.spaprivileged.model.app.AppListModel
import com.android.settingslib.spaprivileged.model.app.AppRecord
import com.android.settingslib.spaprivileged.template.common.WorkProfilePager
+/**
+ * The full screen template for an App List page.
+ */
@Composable
fun <T : AppRecord> AppListPage(
title: String,
listModel: AppListModel<T>,
+ showInstantApps: Boolean = false,
primaryUserOnly: Boolean = false,
appItem: @Composable (itemState: AppListItemModel<T>) -> Unit,
) {
@@ -62,7 +67,10 @@
val selectedOption = rememberSaveable { mutableStateOf(0) }
Spinner(options, selectedOption.value) { selectedOption.value = it }
AppList(
- userInfo = userInfo,
+ appListConfig = AppListConfig(
+ userId = userInfo.id,
+ showInstantApps = showInstantApps,
+ ),
listModel = listModel,
showSystem = showSystem,
option = selectedOption,
diff --git a/packages/SettingsLib/SpaPrivileged/tests/Android.bp b/packages/SettingsLib/SpaPrivileged/tests/Android.bp
new file mode 100644
index 0000000..940a1fe
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/Android.bp
@@ -0,0 +1,46 @@
+//
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "SpaPrivilegedLibTests",
+ certificate: "platform",
+ platform_apis: true,
+ test_suites: ["device-tests"],
+
+ srcs: [
+ ":SpaPrivilegedLib_srcs",
+ "src/**/*.kt",
+ ],
+
+ static_libs: [
+ "SpaPrivilegedLib",
+ "androidx.compose.runtime_runtime",
+ "androidx.compose.ui_ui-test-junit4",
+ "androidx.compose.ui_ui-test-manifest",
+ "androidx.test.ext.junit",
+ "androidx.test.runner",
+ "mockito-target-minus-junit4",
+ "truth-prebuilt",
+ ],
+ kotlincflags: [
+ "-Xjvm-default=all",
+ "-Xopt-in=kotlin.RequiresOptIn",
+ ],
+}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/AndroidManifest.xml b/packages/SettingsLib/SpaPrivileged/tests/AndroidManifest.xml
new file mode 100644
index 0000000..c4f490e
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.spaprivileged.tests">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:label="Tests for SpaPrivilegedLib"
+ android:targetPackage="com.android.settingslib.spaprivileged.tests" />
+</manifest>
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt
new file mode 100644
index 0000000..c010c68
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spaprivileged.model.app
+
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.ApplicationInfoFlags
+import android.content.pm.PackageManager.ResolveInfoFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+
+private const val USER_ID = 0
+
+@RunWith(AndroidJUnit4::class)
+class AppListRepositoryTest {
+
+ @JvmField
+ @Rule
+ val mockito: MockitoRule = MockitoJUnit.rule()
+
+ @Mock
+ private lateinit var context: Context
+
+ @Mock
+ private lateinit var packageManager: PackageManager
+
+ private lateinit var repository: AppListRepository
+
+ private val normalApp = ApplicationInfo().apply {
+ packageName = "normal"
+ enabled = true
+ }
+
+ private val instantApp = ApplicationInfo().apply {
+ packageName = "instant"
+ enabled = true
+ privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT
+ }
+
+ @Before
+ fun setUp() {
+ whenever(context.packageManager).thenReturn(packageManager)
+ whenever(packageManager.getInstalledModules(anyInt())).thenReturn(emptyList())
+ whenever(
+ packageManager.getInstalledApplicationsAsUser(any<ApplicationInfoFlags>(), eq(USER_ID))
+ ).thenReturn(listOf(normalApp, instantApp))
+ whenever(
+ packageManager.queryIntentActivitiesAsUser(any(), any<ResolveInfoFlags>(), eq(USER_ID))
+ ).thenReturn(emptyList())
+
+ repository = AppListRepository(context)
+ }
+
+ @Test
+ fun notShowInstantApps(): Unit = runBlocking {
+ val appListConfig = AppListConfig(userId = USER_ID, showInstantApps = false)
+
+ val appListFlow = repository.loadApps(flowOf(appListConfig))
+
+ launch {
+ val flowValues = mutableListOf<List<ApplicationInfo>>()
+ appListFlow.toList(flowValues)
+ assertThat(flowValues).hasSize(1)
+
+ assertThat(flowValues[0]).containsExactly(normalApp)
+ }
+ }
+
+ @Test
+ fun showInstantApps(): Unit = runBlocking {
+ val appListConfig = AppListConfig(userId = USER_ID, showInstantApps = true)
+
+ val appListFlow = repository.loadApps(flowOf(appListConfig))
+
+ launch {
+ val flowValues = mutableListOf<List<ApplicationInfo>>()
+ appListFlow.toList(flowValues)
+ assertThat(flowValues).hasSize(1)
+
+ assertThat(flowValues[0]).containsExactly(normalApp, instantApp)
+ }
+ }
+}
diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt
index cdedc64..9766514 100644
--- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt
+++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt
@@ -93,8 +93,8 @@
Futures.addCallback(
captureToBitmap(window),
object : FutureCallback<Bitmap> {
- override fun onSuccess(result: Bitmap) {
- continuation.resumeWith(Result.success(result))
+ override fun onSuccess(result: Bitmap?) {
+ continuation.resumeWith(Result.success(result!!))
}
override fun onFailure(t: Throwable) {
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
index 59c6635..2b381a9 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
@@ -42,7 +42,7 @@
}
private fun List<RecentTask>.sortTasks(): List<RecentTask> =
- asReversed().sortedBy {
+ sortedBy {
// Show normal tasks first and only then tasks with opened app selector
it.topActivityComponent == appSelectorComponentName
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt
index 0bdddfe..0927f3b 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt
@@ -17,11 +17,17 @@
package com.android.systemui.mediaprojection.appselector.data
import android.content.ComponentName
+import android.content.Context
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.ComponentInfoFlags
import android.graphics.drawable.Drawable
+import android.os.UserHandle
+import com.android.launcher3.icons.BaseIconFactory.IconOptions
+import com.android.launcher3.icons.IconFactory
import com.android.systemui.dagger.qualifiers.Background
+import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
-import javax.inject.Inject
interface AppIconLoader {
suspend fun loadIcon(userId: Int, component: ComponentName): Drawable?
@@ -31,11 +37,20 @@
@Inject
constructor(
@Background private val backgroundDispatcher: CoroutineDispatcher,
+ private val context: Context,
+ private val packageManager: PackageManager
) : AppIconLoader {
override suspend fun loadIcon(userId: Int, component: ComponentName): Drawable? =
withContext(backgroundDispatcher) {
- // TODO(b/240924731): add a blocking call to load an icon using iconloaderlib
- null
+ IconFactory.obtain(context).use<IconFactory, Drawable?> { iconFactory ->
+ val activityInfo = packageManager
+ .getActivityInfo(component, ComponentInfoFlags.of(0))
+ val icon = activityInfo.loadIcon(packageManager) ?: return@withContext null
+ val userHandler = UserHandle.of(userId)
+ val options = IconOptions().apply { setUser(userHandler) }
+ val badgedIcon = iconFactory.createBadgedIconBitmap(icon, options)
+ badgedIcon.newIcon(context)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
index 6d67e28..cd994b8 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
@@ -16,11 +16,13 @@
package com.android.systemui.mediaprojection.appselector.data
+import android.annotation.ColorInt
import android.content.ComponentName
data class RecentTask(
val taskId: Int,
val userId: Int,
val topActivityComponent: ComponentName?,
- val baseIntentComponent: ComponentName?
+ val baseIntentComponent: ComponentName?,
+ @ColorInt val colorBackground: Int?
)
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
index 5a09435..e8b49cd 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
@@ -16,23 +16,61 @@
package com.android.systemui.mediaprojection.appselector.data
+import android.app.ActivityManager
+import android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.util.kotlin.getOrNull
+import com.android.wm.shell.recents.RecentTasks
+import com.android.wm.shell.util.GroupedRecentTaskInfo
+import java.util.Optional
+import javax.inject.Inject
+import kotlin.coroutines.resume
+import kotlin.coroutines.suspendCoroutine
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
-import javax.inject.Inject
+import java.util.concurrent.Executor
interface RecentTaskListProvider {
+ /** Loads recent tasks, the returned task list is from the most-recent to least-recent order */
suspend fun loadRecentTasks(): List<RecentTask>
}
class ShellRecentTaskListProvider
@Inject
-constructor(@Background private val coroutineDispatcher: CoroutineDispatcher) :
- RecentTaskListProvider {
+constructor(
+ @Background private val coroutineDispatcher: CoroutineDispatcher,
+ @Background private val backgroundExecutor: Executor,
+ private val recentTasks: Optional<RecentTasks>
+) : RecentTaskListProvider {
+
+ private val recents by lazy { recentTasks.getOrNull() }
override suspend fun loadRecentTasks(): List<RecentTask> =
withContext(coroutineDispatcher) {
- // TODO(b/240924731): add blocking call to load the recents
- emptyList()
+ val rawRecentTasks: List<GroupedRecentTaskInfo> = recents?.getTasks() ?: emptyList()
+
+ rawRecentTasks
+ .flatMap { listOfNotNull(it.taskInfo1, it.taskInfo2) }
+ .map {
+ RecentTask(
+ it.taskId,
+ it.userId,
+ it.topActivity,
+ it.baseIntent?.component,
+ it.taskDescription?.backgroundColor
+ )
+ }
+ }
+
+ private suspend fun RecentTasks.getTasks(): List<GroupedRecentTaskInfo> =
+ suspendCoroutine { continuation ->
+ getRecentTasks(
+ Integer.MAX_VALUE,
+ RECENT_IGNORE_UNAVAILABLE,
+ ActivityManager.getCurrentUser(),
+ backgroundExecutor
+ ) { tasks ->
+ continuation.resume(tasks)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskThumbnailLoader.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskThumbnailLoader.kt
index 4291280..47faaed 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskThumbnailLoader.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskThumbnailLoader.kt
@@ -18,6 +18,7 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.shared.recents.model.ThumbnailData
+import com.android.systemui.shared.system.ActivityManagerWrapper
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
@@ -30,12 +31,13 @@
@Inject
constructor(
@Background private val coroutineDispatcher: CoroutineDispatcher,
-) :
- RecentTaskThumbnailLoader {
+ private val activityManager: ActivityManagerWrapper
+) : RecentTaskThumbnailLoader {
override suspend fun loadThumbnail(taskId: Int): ThumbnailData? =
withContext(coroutineDispatcher) {
- // TODO(b/240924731): add blocking call to load a thumbnail
- null
+ val thumbnailData =
+ activityManager.getTaskThumbnail(taskId, /* isLowResolution= */ false)
+ if (thumbnailData.thumbnail == null) null else thumbnailData
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
index 37b7f47..00b1f32 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
@@ -54,7 +54,7 @@
}
@Test
- fun initMultipleRecentTasksWithoutAppSelectorTask_bindsListInReverse() {
+ fun initMultipleRecentTasksWithoutAppSelectorTask_bindsListInTheSameOrder() {
val tasks = listOf(
createRecentTask(taskId = 1),
createRecentTask(taskId = 2),
@@ -66,15 +66,15 @@
verify(view).bind(
listOf(
- createRecentTask(taskId = 3),
- createRecentTask(taskId = 2),
createRecentTask(taskId = 1),
+ createRecentTask(taskId = 2),
+ createRecentTask(taskId = 3),
)
)
}
@Test
- fun initRecentTasksWithAppSelectorTasks_bindsListInReverseAndAppSelectorTasksAtTheEnd() {
+ fun initRecentTasksWithAppSelectorTasks_bindsAppSelectorTasksAtTheEnd() {
val tasks = listOf(
createRecentTask(taskId = 1),
createRecentTask(taskId = 2, topActivityComponent = appSelectorComponentName),
@@ -88,11 +88,11 @@
verify(view).bind(
listOf(
- createRecentTask(taskId = 5),
- createRecentTask(taskId = 3),
createRecentTask(taskId = 1),
- createRecentTask(taskId = 4, topActivityComponent = appSelectorComponentName),
+ createRecentTask(taskId = 3),
+ createRecentTask(taskId = 5),
createRecentTask(taskId = 2, topActivityComponent = appSelectorComponentName),
+ createRecentTask(taskId = 4, topActivityComponent = appSelectorComponentName),
)
)
}
@@ -105,7 +105,8 @@
taskId = taskId,
topActivityComponent = topActivityComponent,
baseIntentComponent = ComponentName("com", "Test"),
- userId = 0
+ userId = 0,
+ colorBackground = 0
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
new file mode 100644
index 0000000..939af16
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
@@ -0,0 +1,112 @@
+package com.android.systemui.mediaprojection.appselector.data
+
+import android.app.ActivityManager.RecentTaskInfo
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.android.wm.shell.recents.RecentTasks
+import com.android.wm.shell.util.GroupedRecentTaskInfo
+import com.google.common.truth.Truth.assertThat
+import java.util.*
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.util.function.Consumer
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class ShellRecentTaskListProviderTest : SysuiTestCase() {
+
+ private val dispatcher = Dispatchers.Unconfined
+ private val recentTasks: RecentTasks = mock()
+ private val recentTaskListProvider =
+ ShellRecentTaskListProvider(dispatcher, Runnable::run, Optional.of(recentTasks))
+
+ @Test
+ fun loadRecentTasks_oneTask_returnsTheSameTask() {
+ givenRecentTasks(createSingleTask(taskId = 1))
+
+ val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
+
+ assertThat(result).containsExactly(createRecentTask(taskId = 1))
+ }
+
+ @Test
+ fun loadRecentTasks_multipleTasks_returnsTheSameTasks() {
+ givenRecentTasks(
+ createSingleTask(taskId = 1),
+ createSingleTask(taskId = 2),
+ createSingleTask(taskId = 3),
+ )
+
+ val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
+
+ assertThat(result)
+ .containsExactly(
+ createRecentTask(taskId = 1),
+ createRecentTask(taskId = 2),
+ createRecentTask(taskId = 3),
+ )
+ }
+
+ @Test
+ fun loadRecentTasks_groupedTask_returnsUngroupedTasks() {
+ givenRecentTasks(createTaskPair(taskId1 = 1, taskId2 = 2))
+
+ val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
+
+ assertThat(result)
+ .containsExactly(createRecentTask(taskId = 1), createRecentTask(taskId = 2))
+ }
+
+ @Test
+ fun loadRecentTasks_mixedSingleAndGroupedTask_returnsUngroupedTasks() {
+ givenRecentTasks(
+ createSingleTask(taskId = 1),
+ createTaskPair(taskId1 = 2, taskId2 = 3),
+ createSingleTask(taskId = 4),
+ createTaskPair(taskId1 = 5, taskId2 = 6),
+ )
+
+ val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
+
+ assertThat(result)
+ .containsExactly(
+ createRecentTask(taskId = 1),
+ createRecentTask(taskId = 2),
+ createRecentTask(taskId = 3),
+ createRecentTask(taskId = 4),
+ createRecentTask(taskId = 5),
+ createRecentTask(taskId = 6),
+ )
+ }
+
+ @Suppress("UNCHECKED_CAST")
+ private fun givenRecentTasks(vararg tasks: GroupedRecentTaskInfo) {
+ whenever(recentTasks.getRecentTasks(any(), any(), any(), any(), any())).thenAnswer {
+ val consumer = it.arguments.last() as Consumer<List<GroupedRecentTaskInfo>>
+ consumer.accept(tasks.toList())
+ }
+ }
+
+ private fun createRecentTask(taskId: Int): RecentTask =
+ RecentTask(
+ taskId = taskId,
+ userId = 0,
+ topActivityComponent = null,
+ baseIntentComponent = null,
+ colorBackground = null
+ )
+
+ private fun createSingleTask(taskId: Int): GroupedRecentTaskInfo =
+ GroupedRecentTaskInfo.forSingleTask(createTaskInfo(taskId))
+
+ private fun createTaskPair(taskId1: Int, taskId2: Int): GroupedRecentTaskInfo =
+ GroupedRecentTaskInfo.forSplitTasks(createTaskInfo(taskId1), createTaskInfo(taskId2), null)
+
+ private fun createTaskInfo(taskId: Int) = RecentTaskInfo().apply { this.taskId = taskId }
+}
diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java
index 838cbd9..ec30369b 100644
--- a/services/companion/java/com/android/server/companion/virtual/InputController.java
+++ b/services/companion/java/com/android/server/companion/virtual/InputController.java
@@ -24,7 +24,6 @@
import android.hardware.display.DisplayManagerInternal;
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
-import android.hardware.input.InputManagerInternal;
import android.hardware.input.VirtualKeyEvent;
import android.hardware.input.VirtualMouseButtonEvent;
import android.hardware.input.VirtualMouseRelativeEvent;
@@ -42,6 +41,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
+import com.android.server.input.InputManagerInternal;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 50b47a6..7d85c13 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -22,6 +22,7 @@
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS;
+import static android.Manifest.permission.MANAGE_USERS;
import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND;
import static android.app.ActivityManager.INSTR_FLAG_ALWAYS_CHECK_SIGNATURE;
@@ -258,6 +259,7 @@
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
import android.media.audiofx.AudioEffect;
import android.net.ConnectivityManager;
@@ -332,6 +334,7 @@
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.util.proto.ProtoUtils;
+import android.view.Display;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -2366,6 +2369,7 @@
mUiHandler = injector.getUiHandler(null /* service */);
mUidObserverController = new UidObserverController(mUiHandler);
mUserController = new UserController(this);
+ mInjector.mUserController = mUserController;
mPendingIntentController =
new PendingIntentController(handlerThread.getLooper(), mUserController, mConstants);
mAppRestrictionController = new AppRestrictionController(mContext, this);
@@ -2480,6 +2484,7 @@
mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
mUserController = new UserController(this);
+ mInjector.mUserController = mUserController;
mPendingIntentController = new PendingIntentController(
mHandlerThread.getLooper(), mUserController, mConstants);
@@ -6057,6 +6062,24 @@
* This can be called with or without the global lock held.
*/
@PermissionMethod
+ private void enforceCallingHasAtLeastOnePermission(String func, String... permissions) {
+ for (String permission : permissions) {
+ if (checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ }
+
+ String msg = "Permission Denial: " + func + " from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires one of " + Arrays.toString(permissions);
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+
+ /**
+ * This can be called with or without the global lock held.
+ */
void enforcePermission(String permission, int pid, int uid, String func) {
if (checkPermission(permission, pid, uid) == PackageManager.PERMISSION_GRANTED) {
return;
@@ -16333,8 +16356,34 @@
@Override
public boolean startUserInBackgroundOnSecondaryDisplay(int userId, int displayId) {
+ int[] displayIds = getSecondaryDisplayIdsForStartingBackgroundUsers();
+ boolean validDisplay = false;
+ if (displayIds != null) {
+ for (int i = 0; i < displayIds.length; i++) {
+ if (displayId == displayIds[i]) {
+ validDisplay = true;
+ break;
+ }
+ }
+ }
+ if (!validDisplay) {
+ throw new IllegalArgumentException("Invalid display (" + displayId + ") to start user. "
+ + "Valid options are: " + Arrays.toString(displayIds));
+ }
+
+ if (DEBUG_MU) {
+ Slogf.d(TAG_MU, "Calling startUserOnSecondaryDisplay(%d, %d) using injector %s", userId,
+ displayId, mInjector);
+ }
// Permission check done inside UserController.
- return mUserController.startUserOnSecondaryDisplay(userId, displayId);
+ return mInjector.startUserOnSecondaryDisplay(userId, displayId);
+ }
+
+ @Override
+ public int[] getSecondaryDisplayIdsForStartingBackgroundUsers() {
+ enforceCallingHasAtLeastOnePermission("getSecondaryDisplayIdsForStartingBackgroundUsers()",
+ MANAGE_USERS, INTERACT_ACROSS_USERS);
+ return mInjector.getSecondaryDisplayIdsForStartingBackgroundUsers();
}
/**
@@ -18343,8 +18392,10 @@
@VisibleForTesting
public static class Injector {
+ private final Context mContext;
private NetworkManagementInternal mNmi;
- private Context mContext;
+
+ private UserController mUserController;
public Injector(Context context) {
mContext = context;
@@ -18370,6 +18421,103 @@
}
/**
+ * Called by {@code AMS.getSecondaryDisplayIdsForStartingBackgroundUsers()}.
+ */
+ // NOTE: ideally Injector should have no complex logic, but if this logic was moved to AMS,
+ // it could not be tested with the existing ActivityManagerServiceTest (as DisplayManager,
+ // DisplayInfo, etc... are final and UserManager.isUsersOnSecondaryDisplaysEnabled is
+ // static).
+ // So, the logic was added here, and tested on ActivityManagerServiceInjectorTest (which
+ // was added on FrameworksMockingServicesTests and hence uses Extended Mockito to mock
+ // final and static stuff)
+ @Nullable
+ public int[] getSecondaryDisplayIdsForStartingBackgroundUsers() {
+ if (!UserManager.isUsersOnSecondaryDisplaysEnabled()) {
+ Slogf.w(TAG, "getSecondaryDisplayIdsForStartingBackgroundUsers(): not supported");
+ return null;
+ }
+
+ // NOTE: DisplayManagerInternal doesn't have a method to list all displays
+ DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+
+ Display[] allDisplays = displayManager.getDisplays();
+
+ // allDisplays should contain at least Display.DEFAULT_DISPLAY, but it's better to
+ // double check, just in case...
+ if (allDisplays == null || allDisplays.length == 0) {
+ Slogf.wtf(TAG, "displayManager (%s) returned no displays", displayManager);
+ return null;
+ }
+ boolean hasDefaultDisplay = false;
+ for (Display display : allDisplays) {
+ if (display.getDisplayId() == Display.DEFAULT_DISPLAY) {
+ hasDefaultDisplay = true;
+ break;
+ }
+ }
+ if (!hasDefaultDisplay) {
+ Slogf.wtf(TAG, "displayManager (%s) has %d displays (%s), but none has id "
+ + "DEFAULT_DISPLAY (%d)", displayManager, allDisplays.length,
+ Arrays.toString(allDisplays), Display.DEFAULT_DISPLAY);
+ return null;
+ }
+
+ // Starts with all displays but DEFAULT_DISPLAY
+ int[] displayIds = new int[allDisplays.length - 1];
+
+ // TODO(b/247592632): check for other properties like isSecure or proper display type
+ int numberValidDisplays = 0;
+ for (Display display : allDisplays) {
+ int displayId = display.getDisplayId();
+ if (display.isValid() && displayId != Display.DEFAULT_DISPLAY) {
+ displayIds[numberValidDisplays++] = displayId;
+ }
+ }
+
+ if (numberValidDisplays == 0) {
+ // TODO(b/247580038): remove this workaround once a virtual display on Car's
+ // KitchenSink (or other app) can be used while running CTS tests on devices that
+ // don't have a real display.
+ // STOPSHIP: if not removed, it should at least be unit tested
+ String testingProp = "fw.secondary_display_for_starting_users_for_testing_purposes";
+ int displayId = SystemProperties.getInt(testingProp, Display.DEFAULT_DISPLAY);
+ if (displayId != Display.DEFAULT_DISPLAY && displayId > 0) {
+ Slogf.w(TAG, "getSecondaryDisplayIdsForStartingBackgroundUsers(): no valid "
+ + "display found, but returning %d as set by property %s", displayId,
+ testingProp);
+ return new int[] { displayId };
+ }
+ Slogf.e(TAG, "getSecondaryDisplayIdsForStartingBackgroundUsers(): no valid display"
+ + " on %s", Arrays.toString(allDisplays));
+ return null;
+ }
+
+ if (numberValidDisplays != displayIds.length) {
+ int[] validDisplayIds = new int[numberValidDisplays];
+ System.arraycopy(displayIds, 0, validDisplayIds, 0, numberValidDisplays);
+ if (DEBUG_MU) {
+ Slogf.d(TAG, "getSecondaryDisplayIdsForStartingBackgroundUsers(): returning "
+ + "only valid displays (%d instead of %d): %s", numberValidDisplays,
+ displayIds.length, Arrays.toString(validDisplayIds));
+ }
+ return validDisplayIds;
+ }
+
+ if (DEBUG_MU) {
+ Slogf.d(TAG, "getSecondaryDisplayIdsForStartingBackgroundUsers(): returning all "
+ + "(but DEFAULT_DISPLAY) displays : %s", Arrays.toString(displayIds));
+ }
+ return displayIds;
+ }
+
+ /**
+ * Called by {@code AMS.startUserOnSecondaryDisplay()}.
+ */
+ public boolean startUserOnSecondaryDisplay(int userId, int displayId) {
+ return mUserController.startUserOnSecondaryDisplay(userId, displayId);
+ }
+
+ /**
* Return the process list instance
*/
public ProcessList getProcessList(ActivityManagerService service) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index b4f6e35..10e2aae 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -367,6 +367,8 @@
return runGetCurrentForegroundProcess(pw, mInternal, mTaskInterface);
case "reset-dropbox-rate-limiter":
return runResetDropboxRateLimiter();
+ case "list-secondary-displays-for-starting-users":
+ return runListSecondaryDisplaysForStartingUsers(pw);
default:
return handleDefaultCommands(cmd);
}
@@ -2068,6 +2070,10 @@
success = mInterface.startUserInBackgroundWithListener(userId, waiter);
displaySuffix = "";
} else {
+ if (!UserManager.isUsersOnSecondaryDisplaysEnabled()) {
+ pw.println("Not supported");
+ return -1;
+ }
success = mInterface.startUserInBackgroundOnSecondaryDisplay(userId, displayId);
displaySuffix = " on display " + displayId;
}
@@ -3591,6 +3597,14 @@
return 0;
}
+ int runListSecondaryDisplaysForStartingUsers(PrintWriter pw) throws RemoteException {
+ int[] displayIds = mInterface.getSecondaryDisplayIdsForStartingBackgroundUsers();
+ pw.println(displayIds == null || displayIds.length == 0
+ ? "none"
+ : Arrays.toString(displayIds));
+ return 0;
+ }
+
private Resources getResources(PrintWriter pw) throws RemoteException {
// system resources does not contain all the device configuration, construct it manually.
Configuration config = mInterface.getConfiguration();
@@ -3951,6 +3965,9 @@
pw.println(" Set an app's background restriction level which in turn map to a app standby bucket.");
pw.println(" get-bg-restriction-level [--user <USER_ID>] <PACKAGE>");
pw.println(" Get an app's background restriction level.");
+ pw.println(" list-secondary-displays-for-starting-users");
+ pw.println(" Lists the id of displays that can be used to start users on "
+ + "background.");
pw.println();
Intent.printIntentArgsHelp(pw, "");
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 992d416..d7b3848 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -27,6 +27,7 @@
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import static android.os.Process.ZYGOTE_POLICY_FLAG_EMPTY;
+import static android.os.Process.getAdvertisedMem;
import static android.os.Process.getFreeMemory;
import static android.os.Process.getTotalMemory;
import static android.os.Process.killProcessQuiet;
@@ -1532,6 +1533,7 @@
void getMemoryInfo(ActivityManager.MemoryInfo outInfo) {
final long homeAppMem = getMemLevel(HOME_APP_ADJ);
final long cachedAppMem = getMemLevel(CACHED_APP_MIN_ADJ);
+ outInfo.advertisedMem = getAdvertisedMem();
outInfo.availMem = getFreeMemory();
outInfo.totalMem = getTotalMemory();
outInfo.threshold = homeAppMem;
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 072d17f..403ad9b 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -105,6 +105,7 @@
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.PackageTagsList;
import android.os.Process;
@@ -374,10 +375,17 @@
public AppOpsUidStateTracker getUidStateTracker() {
if (mUidStateTracker == null) {
mUidStateTracker = new AppOpsUidStateTrackerImpl(
- LocalServices.getService(ActivityManagerInternal.class), mHandler,
+ LocalServices.getService(ActivityManagerInternal.class),
+ mHandler,
+ r -> {
+ synchronized (AppOpsService.this) {
+ r.run();
+ }
+ },
Clock.SYSTEM_CLOCK, mConstants);
- mUidStateTracker.addUidStateChangedCallback(mHandler, this::onUidStateChanged);
+ mUidStateTracker.addUidStateChangedCallback(new HandlerExecutor(mHandler),
+ this::onUidStateChanged);
}
return mUidStateTracker;
}
@@ -4809,6 +4817,8 @@
pw.println(" Only output the watcher sections.");
pw.println(" --history");
pw.println(" Only output history.");
+ pw.println(" --uid-state-change-logs");
+ pw.println(" Include logs about uid state changes.");
}
private void dumpStatesLocked(@NonNull PrintWriter pw, @Nullable String filterAttributionTag,
@@ -4946,6 +4956,7 @@
// TODO ntmyren: Remove the dumpHistory and dumpFilter
boolean dumpHistory = false;
boolean includeDiscreteOps = false;
+ boolean dumpUidStateChangeLogs = false;
int nDiscreteOps = 10;
@HistoricalOpsRequestFilter int dumpFilter = 0;
boolean dumpAll = false;
@@ -5028,6 +5039,8 @@
} else if (arg.length() > 0 && arg.charAt(0) == '-') {
pw.println("Unknown option: " + arg);
return;
+ } else if ("--uid-state-change-logs".equals(arg)) {
+ dumpUidStateChangeLogs = true;
} else {
pw.println("Unknown command: " + arg);
return;
@@ -5363,6 +5376,12 @@
pw.println(" AppOps policy not set.");
}
}
+
+ if (dumpAll || dumpUidStateChangeLogs) {
+ pw.println();
+ pw.println("Uid State Changes Event Log:");
+ getUidStateTracker().dumpEvents(pw);
+ }
}
// Must not hold the appops lock
@@ -5375,14 +5394,6 @@
mHistoricalRegistry.dumpDiscreteData(pw, dumpUid, dumpPackage, dumpAttributionTag,
dumpFilter, dumpOp, sdf, date, " ", nDiscreteOps);
}
-
- if (dumpAll) {
- pw.println();
- pw.println("Uid State Changes Event Log:");
- if (mUidStateTracker != null) {
- mUidStateTracker.dumpEvents(pw);
- }
- }
}
@Override
diff --git a/services/core/java/com/android/server/appop/AppOpsUidStateTracker.java b/services/core/java/com/android/server/appop/AppOpsUidStateTracker.java
index 3da121b..742bf4b 100644
--- a/services/core/java/com/android/server/appop/AppOpsUidStateTracker.java
+++ b/services/core/java/com/android/server/appop/AppOpsUidStateTracker.java
@@ -30,10 +30,11 @@
import static android.app.AppOpsManager.UID_STATE_PERSISTENT;
import static android.app.AppOpsManager.UID_STATE_TOP;
-import android.os.Handler;
+import android.annotation.CallbackExecutor;
import android.util.SparseArray;
import java.io.PrintWriter;
+import java.util.concurrent.Executor;
interface AppOpsUidStateTracker {
@@ -95,7 +96,7 @@
/**
* Listen to changes in {@link android.app.AppOpsManager.UidState}
*/
- void addUidStateChangedCallback(Handler handler,
+ void addUidStateChangedCallback(@CallbackExecutor Executor executor,
UidStateChangedCallback callback);
/**
diff --git a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
index 52b67b5..3c281d1 100644
--- a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
+++ b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
@@ -44,17 +44,18 @@
import android.util.SparseLongArray;
import android.util.TimeUtils;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.Clock;
import com.android.internal.util.function.pooled.PooledLambda;
import java.io.PrintWriter;
-import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {
private static final String LOG_TAG = AppOpsUidStateTrackerImpl.class.getSimpleName();
- private final Handler mHandler;
+ private final DelayableExecutor mExecutor;
private final Clock mClock;
private ActivityManagerInternal mActivityManagerInternal;
private AppOpsService.Constants mConstants;
@@ -68,18 +69,46 @@
private SparseLongArray mPendingCommitTime = new SparseLongArray();
private SparseBooleanArray mPendingGone = new SparseBooleanArray();
- private ArrayMap<UidStateChangedCallback, Handler> mUidStateChangedCallbacks = new ArrayMap<>();
+ private ArrayMap<UidStateChangedCallback, Executor>
+ mUidStateChangedCallbacks = new ArrayMap<>();
private final EventLog mEventLog;
+ @VisibleForTesting
+ interface DelayableExecutor extends Executor {
+
+ void execute(Runnable runnable);
+
+ void executeDelayed(Runnable runnable, long delay);
+ }
+
AppOpsUidStateTrackerImpl(ActivityManagerInternal activityManagerInternal,
- Handler handler, Clock clock, AppOpsService.Constants constants) {
+ Handler handler, Executor lockingExecutor, Clock clock,
+ AppOpsService.Constants constants) {
+
+ this(activityManagerInternal, new DelayableExecutor() {
+ @Override
+ public void execute(Runnable runnable) {
+ handler.post(() -> lockingExecutor.execute(runnable));
+ }
+
+ @Override
+ public void executeDelayed(Runnable runnable, long delay) {
+ handler.postDelayed(() -> lockingExecutor.execute(runnable), delay);
+ }
+ }, clock, constants, handler.getLooper().getThread());
+ }
+
+ @VisibleForTesting
+ AppOpsUidStateTrackerImpl(ActivityManagerInternal activityManagerInternal,
+ DelayableExecutor executor, Clock clock, AppOpsService.Constants constants,
+ Thread executorThread) {
mActivityManagerInternal = activityManagerInternal;
- mHandler = handler;
+ mExecutor = executor;
mClock = clock;
mConstants = constants;
- mEventLog = new EventLog(handler);
+ mEventLog = new EventLog(executor, executorThread);
}
@Override
@@ -157,11 +186,12 @@
}
@Override
- public void addUidStateChangedCallback(Handler handler, UidStateChangedCallback callback) {
+ public void addUidStateChangedCallback(Executor executor, UidStateChangedCallback callback) {
if (mUidStateChangedCallbacks.containsKey(callback)) {
throw new IllegalStateException("Callback is already registered.");
}
- mUidStateChangedCallbacks.put(callback, handler);
+
+ mUidStateChangedCallbacks.put(callback, executor);
}
@Override
@@ -232,7 +262,7 @@
final long commitTime = mClock.elapsedRealtime() + settleTime;
mPendingCommitTime.put(uid, commitTime);
- mHandler.sendMessageDelayed(PooledLambda.obtainMessage(
+ mExecutor.executeDelayed(PooledLambda.obtainRunnable(
AppOpsUidStateTrackerImpl::updateUidPendingStateIfNeeded, this,
uid), settleTime + 1);
}
@@ -323,10 +353,11 @@
for (int i = 0; i < mUidStateChangedCallbacks.size(); i++) {
UidStateChangedCallback cb = mUidStateChangedCallbacks.keyAt(i);
- Handler h = mUidStateChangedCallbacks.valueAt(i);
+ Executor executor = mUidStateChangedCallbacks.valueAt(i);
- h.sendMessage(PooledLambda.obtainMessage(UidStateChangedCallback::onUidStateChanged,
- cb, uid, pendingUidState, foregroundChange));
+ executor.execute(PooledLambda.obtainRunnable(
+ UidStateChangedCallback::onUidStateChanged, cb, uid, pendingUidState,
+ foregroundChange));
}
}
@@ -366,7 +397,8 @@
// Memory usage: 24 * size bytes
private static final int EVAL_FOREGROUND_MODE_MAX_SIZE = 200;
- private final Handler mHandler;
+ private final DelayableExecutor mExecutor;
+ private final Thread mExecutorThread;
private int[][] mUpdateUidProcStateLog = new int[UPDATE_UID_PROC_STATE_LOG_MAX_SIZE][3];
private long[] mUpdateUidProcStateLogTimestamps =
@@ -384,15 +416,16 @@
private int mEvalForegroundModeLogSize = 0;
private int mEvalForegroundModeLogHead = 0;
- EventLog(Handler handler) {
- mHandler = handler;
+ EventLog(DelayableExecutor executor, Thread executorThread) {
+ mExecutor = executor;
+ mExecutorThread = executorThread;
}
void logUpdateUidProcState(int uid, int procState, int capability) {
if (UPDATE_UID_PROC_STATE_LOG_MAX_SIZE == 0) {
return;
}
- mHandler.sendMessage(PooledLambda.obtainMessage(EventLog::logUpdateUidProcStateAsync,
+ mExecutor.execute(PooledLambda.obtainRunnable(EventLog::logUpdateUidProcStateAsync,
this, System.currentTimeMillis(), uid, procState, capability));
}
@@ -416,7 +449,7 @@
if (COMMIT_UID_STATE_LOG_MAX_SIZE == 0) {
return;
}
- mHandler.sendMessage(PooledLambda.obtainMessage(EventLog::logCommitUidStateAsync,
+ mExecutor.execute(PooledLambda.obtainRunnable(EventLog::logCommitUidStateAsync,
this, System.currentTimeMillis(), uid, uidState, capability, visible));
}
@@ -442,7 +475,7 @@
if (EVAL_FOREGROUND_MODE_MAX_SIZE == 0) {
return;
}
- mHandler.sendMessage(PooledLambda.obtainMessage(EventLog::logEvalForegroundModeAsync,
+ mExecutor.execute(PooledLambda.obtainRunnable(EventLog::logEvalForegroundModeAsync,
this, System.currentTimeMillis(), uid, uidState, capability, code, result));
}
@@ -466,22 +499,6 @@
}
void dumpEvents(PrintWriter pw) {
- if (Thread.currentThread() != mHandler.getLooper().getThread()) {
- // All operations are done on the handler's thread
- CountDownLatch latch = new CountDownLatch(1);
- mHandler.post(() -> {
- dumpEvents(pw);
- latch.countDown();
- });
-
- try {
- latch.await();
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- return;
- }
-
int updateIdx = 0;
int commitIdx = 0;
int evalIdx = 0;
@@ -536,13 +553,13 @@
pw.print(" UPDATE_UID_PROC_STATE");
pw.print(" uid=");
- pw.print(uid);
+ pw.print(String.format("%-8d", uid));
pw.print(" procState=");
pw.print(String.format("%-30s", ActivityManager.procStateToString(procState)));
pw.print(" capability=");
- pw.print(ActivityManager.getCapabilitiesSummary(capability));
+ pw.print(ActivityManager.getCapabilitiesSummary(capability) + " ");
pw.println();
}
@@ -559,13 +576,13 @@
pw.print(" COMMIT_UID_STATE ");
pw.print(" uid=");
- pw.print(uid);
+ pw.print(String.format("%-8d", uid));
pw.print(" uidState=");
pw.print(String.format("%-30s", AppOpsManager.uidStateToString(uidState)));
pw.print(" capability=");
- pw.print(ActivityManager.getCapabilitiesSummary(capability));
+ pw.print(ActivityManager.getCapabilitiesSummary(capability) + " ");
pw.print(" visibleAppWidget=");
pw.print(visibleAppWidget);
@@ -586,13 +603,13 @@
pw.print(" EVAL_FOREGROUND_MODE ");
pw.print(" uid=");
- pw.print(uid);
+ pw.print(String.format("%-8d", uid));
pw.print(" uidState=");
pw.print(String.format("%-30s", AppOpsManager.uidStateToString(uidState)));
pw.print(" capability=");
- pw.print(ActivityManager.getCapabilitiesSummary(capability));
+ pw.print(ActivityManager.getCapabilitiesSummary(capability) + " ");
pw.print(" code=");
pw.print(String.format("%-20s", AppOpsManager.opToName(code)));
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 2cde526..e851f03 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -80,7 +80,6 @@
import android.hardware.display.VirtualDisplayConfig;
import android.hardware.display.WifiDisplayStatus;
import android.hardware.graphics.common.DisplayDecorationSupport;
-import android.hardware.input.InputManagerInternal;
import android.media.projection.IMediaProjection;
import android.media.projection.IMediaProjectionManager;
import android.net.Uri;
@@ -134,6 +133,7 @@
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.display.DisplayDeviceConfig.SensorData;
import com.android.server.display.utils.SensorUtils;
+import com.android.server.input.InputManagerInternal;
import com.android.server.wm.SurfaceAnimationThread;
import com.android.server.wm.WindowManagerInternal;
diff --git a/services/core/java/com/android/server/display/TEST_MAPPING b/services/core/java/com/android/server/display/TEST_MAPPING
index 66ec5c4..57c2e01 100644
--- a/services/core/java/com/android/server/display/TEST_MAPPING
+++ b/services/core/java/com/android/server/display/TEST_MAPPING
@@ -7,6 +7,15 @@
{"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
+ },
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {"include-filter": "com.android.server.display"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+ {"exclude-annotation": "org.junit.Ignore"}
+ ]
}
]
}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index fee1f5c..e1b18f2 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -38,7 +38,6 @@
import android.content.pm.ServiceInfo;
import android.database.ContentObserver;
import android.hardware.display.AmbientDisplayConfiguration;
-import android.hardware.input.InputManagerInternal;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
@@ -66,6 +65,7 @@
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.input.InputManagerInternal;
import com.android.server.wm.ActivityInterceptorCallback;
import com.android.server.wm.ActivityTaskManagerInternal;
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index e03a46c..3ee3503 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -266,10 +266,6 @@
// Make sure HdmiCecConfig is instantiated and the XMLs are read.
private HdmiCecConfig mHdmiCecConfig;
- // Last return value of getPhysicalAddress(). Only updated on calls of getPhysicalAddress().
- // Does not represent the current physical address at all times. Not to be used as a cache.
- private int mPhysicalAddress = Constants.INVALID_PHYSICAL_ADDRESS;
-
/**
* Interface to report send result.
*/
@@ -2082,15 +2078,9 @@
@Override
public int getPhysicalAddress() {
initBinderCall();
- runOnServiceThread(new Runnable() {
- @Override
- public void run() {
- synchronized (mLock) {
- mPhysicalAddress = mHdmiCecNetwork.getPhysicalAddress();
- }
- }
- });
- return mPhysicalAddress;
+ synchronized (mLock) {
+ return mHdmiCecNetwork.getPhysicalAddress();
+ }
}
@Override
diff --git a/core/java/android/hardware/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java
similarity index 98%
rename from core/java/android/hardware/input/InputManagerInternal.java
rename to services/core/java/com/android/server/input/InputManagerInternal.java
index fc6bc55..36099b0 100644
--- a/core/java/android/hardware/input/InputManagerInternal.java
+++ b/services/core/java/com/android/server/input/InputManagerInternal.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.hardware.input;
+package com.android.server.input;
import android.annotation.NonNull;
import android.graphics.PointF;
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 05c4f77..71feb95 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -55,8 +55,6 @@
import android.hardware.input.ITabletModeChangedListener;
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
-import android.hardware.input.InputManagerInternal;
-import android.hardware.input.InputManagerInternal.LidSwitchCallback;
import android.hardware.input.InputSensorInfo;
import android.hardware.input.KeyboardLayout;
import android.hardware.input.TouchCalibration;
@@ -123,6 +121,7 @@
import com.android.server.DisplayThread;
import com.android.server.LocalServices;
import com.android.server.Watchdog;
+import com.android.server.input.InputManagerInternal.LidSwitchCallback;
import com.android.server.policy.WindowManagerPolicy;
import libcore.io.IoUtils;
diff --git a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
index f89b6ae..a4830be 100644
--- a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
+++ b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
@@ -21,7 +21,6 @@
import android.annotation.AnyThread;
import android.annotation.Nullable;
import android.annotation.UiThread;
-import android.hardware.input.InputManagerInternal;
import android.os.IBinder;
import android.os.Looper;
import android.util.Slog;
@@ -35,6 +34,7 @@
import android.view.SurfaceControl;
import com.android.server.LocalServices;
+import com.android.server.input.InputManagerInternal;
import com.android.server.wm.WindowManagerInternal;
import java.util.ArrayList;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index ed17b9ca..4571546 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -92,7 +92,6 @@
import android.graphics.Matrix;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.input.InputManager;
-import android.hardware.input.InputManagerInternal;
import android.inputmethodservice.InputMethodService;
import android.media.AudioManagerInternal;
import android.net.Uri;
@@ -188,6 +187,7 @@
import com.android.server.ServiceThread;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
+import com.android.server.input.InputManagerInternal;
import com.android.server.inputmethod.InputMethodManagerInternal.InputMethodListListener;
import com.android.server.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
import com.android.server.inputmethod.InputMethodUtils.InputMethodSettings;
diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
index 9b7d19a..f8fcaff 100644
--- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
@@ -26,7 +26,6 @@
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
-import android.hardware.input.InputManagerInternal;
import android.os.Environment;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -39,6 +38,7 @@
import com.android.server.LocalServices;
import com.android.server.devicestate.DeviceState;
import com.android.server.devicestate.DeviceStateProvider;
+import com.android.server.input.InputManagerInternal;
import com.android.server.policy.devicestate.config.Conditions;
import com.android.server.policy.devicestate.config.DeviceStateConfig;
import com.android.server.policy.devicestate.config.Flags;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 02d7074..07f5bcf 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -121,7 +121,6 @@
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPlaybackClient;
import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
-import android.hardware.input.InputManagerInternal;
import android.media.AudioManager;
import android.media.AudioManagerInternal;
import android.media.AudioSystem;
@@ -204,6 +203,7 @@
import com.android.server.GestureLauncherService;
import com.android.server.LocalServices;
import com.android.server.SystemServiceManager;
+import com.android.server.input.InputManagerInternal;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.policy.KeyCombinationManager.TwoKeysCombinationRule;
import com.android.server.policy.keyguard.KeyguardServiceDelegate;
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 9336be5..69fb22c 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -27,7 +27,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.display.DisplayManagerInternal;
-import android.hardware.input.InputManagerInternal;
import android.media.AudioManager;
import android.media.Ringtone;
import android.media.RingtoneManager;
@@ -62,6 +61,7 @@
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
+import com.android.server.input.InputManagerInternal;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.statusbar.StatusBarManagerInternal;
diff --git a/services/core/java/com/android/server/updates/ConfigUpdateInstallReceiver.java b/services/core/java/com/android/server/updates/ConfigUpdateInstallReceiver.java
index 948439d..2d022ae 100644
--- a/services/core/java/com/android/server/updates/ConfigUpdateInstallReceiver.java
+++ b/services/core/java/com/android/server/updates/ConfigUpdateInstallReceiver.java
@@ -189,6 +189,13 @@
if (!parent.exists()) {
throw new IOException("Failed to create directory " + parent.getCanonicalPath());
}
+
+ // Give executable permissions to parent folders.
+ while (!(parent.equals(updateDir))) {
+ parent.setExecutable(true, false);
+ parent = parent.getParentFile();
+ }
+
// create the temporary file
tmp = File.createTempFile("journal", "", dir);
// mark tmp -rw-r--r--
diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java
index acbf1a4..6e23ed9 100644
--- a/services/core/java/com/android/server/wm/ContentRecorder.java
+++ b/services/core/java/com/android/server/wm/ContentRecorder.java
@@ -391,8 +391,7 @@
* </p>
*/
private void handleStartRecordingFailed() {
- final boolean shouldExitTaskRecording = mContentRecordingSession != null
- && mContentRecordingSession.getContentToRecord() == RECORD_CONTENT_TASK;
+ final boolean shouldExitTaskRecording = isRecordingContentTask();
clearContentRecordingSession();
if (shouldExitTaskRecording) {
// Clean up the cached session first to ensure recording doesn't re-start, since
@@ -478,9 +477,10 @@
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
"Recorded task is removed, so stop recording on display %d",
mDisplayContent.getDisplayId());
- Task recordedTask = mRecordedWindowContainer.asTask();
- if (recordedTask == null
- || mContentRecordingSession.getContentToRecord() != RECORD_CONTENT_TASK) {
+
+ Task recordedTask = mRecordedWindowContainer != null
+ ? mRecordedWindowContainer.asTask() : null;
+ if (recordedTask == null || !isRecordingContentTask()) {
return;
}
recordedTask.unregisterWindowContainerListener(this);
@@ -504,4 +504,9 @@
@VisibleForTesting interface MediaProjectionManagerWrapper {
void stopActiveProjection();
}
+
+ private boolean isRecordingContentTask() {
+ return mContentRecordingSession != null
+ && mContentRecordingSession.getContentToRecord() == RECORD_CONTENT_TASK;
+ }
}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 78b4ce2..3f380e7 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -168,8 +168,9 @@
jmethodID constructor;
jfieldID lightTypeInput;
jfieldID lightTypePlayerId;
+ jfieldID lightTypeKeyboardBacklight;
jfieldID lightCapabilityBrightness;
- jfieldID lightCapabilityRgb;
+ jfieldID lightCapabilityColorRgb;
} gLightClassInfo;
static struct {
@@ -2011,25 +2012,28 @@
jint jTypeId =
env->GetStaticIntField(gLightClassInfo.clazz, gLightClassInfo.lightTypeInput);
- jint jCapability = 0;
-
- if (lightInfo.type == InputDeviceLightType::MONO) {
- jCapability = env->GetStaticIntField(gLightClassInfo.clazz,
- gLightClassInfo.lightCapabilityBrightness);
- } else if (lightInfo.type == InputDeviceLightType::RGB ||
- lightInfo.type == InputDeviceLightType::MULTI_COLOR) {
- jCapability =
- env->GetStaticIntField(gLightClassInfo.clazz,
- gLightClassInfo.lightCapabilityBrightness) |
- env->GetStaticIntField(gLightClassInfo.clazz,
- gLightClassInfo.lightCapabilityRgb);
+ if (lightInfo.type == InputDeviceLightType::INPUT) {
+ jTypeId = env->GetStaticIntField(gLightClassInfo.clazz, gLightClassInfo.lightTypeInput);
} else if (lightInfo.type == InputDeviceLightType::PLAYER_ID) {
jTypeId = env->GetStaticIntField(gLightClassInfo.clazz,
gLightClassInfo.lightTypePlayerId);
+ } else if (lightInfo.type == InputDeviceLightType::KEYBOARD_BACKLIGHT) {
+ jTypeId = env->GetStaticIntField(gLightClassInfo.clazz,
+ gLightClassInfo.lightTypeKeyboardBacklight);
} else {
ALOGW("Unknown light type %d", lightInfo.type);
continue;
}
+
+ jint jCapability = 0;
+ if (lightInfo.capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS)) {
+ jCapability |= env->GetStaticIntField(gLightClassInfo.clazz,
+ gLightClassInfo.lightCapabilityBrightness);
+ }
+ if (lightInfo.capabilityFlags.test(InputDeviceLightCapability::RGB)) {
+ jCapability |= env->GetStaticIntField(gLightClassInfo.clazz,
+ gLightClassInfo.lightCapabilityColorRgb);
+ }
ScopedLocalRef<jobject> lightObj(env,
env->NewObject(gLightClassInfo.clazz,
gLightClassInfo.constructor,
@@ -2596,10 +2600,12 @@
env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_TYPE_INPUT", "I");
gLightClassInfo.lightTypePlayerId =
env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_TYPE_PLAYER_ID", "I");
+ gLightClassInfo.lightTypeKeyboardBacklight =
+ env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_TYPE_KEYBOARD_BACKLIGHT", "I");
gLightClassInfo.lightCapabilityBrightness =
env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_CAPABILITY_BRIGHTNESS", "I");
- gLightClassInfo.lightCapabilityRgb =
- env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_CAPABILITY_RGB", "I");
+ gLightClassInfo.lightCapabilityColorRgb =
+ env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_CAPABILITY_COLOR_RGB", "I");
// ArrayList
FIND_CLASS(gArrayListClassInfo.clazz, "java/util/ArrayList");
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 6196c49..9c9b363 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -1287,8 +1287,8 @@
bp.set_allocated_dest_path(&target);
bp.set_allocated_source_subdir(&source);
const auto metadata = bp.SerializeAsString();
- bp.release_dest_path();
- bp.release_source_subdir();
+ static_cast<void>(bp.release_dest_path());
+ static_cast<void>(bp.release_source_subdir());
mdFileName = makeBindMdName();
metadataFullPath = path::join(ifs.root, constants().mount, mdFileName);
auto node = mIncFs->makeFile(ifs.control, metadataFullPath, 0444, idFromMetadata(metadata),
diff --git a/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java
index 525a931..c8d153a 100644
--- a/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java
+++ b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java
@@ -23,7 +23,6 @@
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
-import android.hardware.input.InputManagerInternal;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -35,6 +34,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.server.LocalServices;
import com.android.server.infra.AbstractPerUserSystemService;
+import com.android.server.input.InputManagerInternal;
final class SelectionToolbarManagerServiceImpl extends
AbstractPerUserSystemService<SelectionToolbarManagerServiceImpl,
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceInjectorTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceInjectorTest.java
new file mode 100644
index 0000000..09df96f
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceInjectorTest.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.os.UserManager;
+import android.util.Log;
+import android.view.Display;
+
+import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
+import com.android.server.ExtendedMockitoTestCase;
+import com.android.server.am.ActivityManagerService.Injector;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+import java.util.Arrays;
+
+/**
+ * Run as {@code atest
+ * FrameworksMockingServicesTests:com.android.server.am.ActivityManagerServiceInjectorTest}
+ */
+public final class ActivityManagerServiceInjectorTest extends ExtendedMockitoTestCase {
+
+ private static final String TAG = ActivityManagerServiceInjectorTest.class.getSimpleName();
+
+ private final Display mDefaultDisplay = validDisplay(DEFAULT_DISPLAY);
+
+ @Mock private Context mContext;
+ @Mock private DisplayManager mDisplayManager;
+
+ private Injector mInjector;
+
+ @Before
+ public void setFixture() {
+ mInjector = new Injector(mContext);
+
+ when(mContext.getSystemService(DisplayManager.class)).thenReturn(mDisplayManager);
+ }
+
+ @Override
+ protected void initializeSession(StaticMockitoSessionBuilder builder) {
+ builder.spyStatic(UserManager.class);
+ }
+
+ @Test
+ public void testGetSecondaryDisplayIdsForStartingBackgroundUsers_notSupported() {
+ mockUmIsUsersOnSecondaryDisplaysEnabled(false);
+
+ int [] displayIds = mInjector.getSecondaryDisplayIdsForStartingBackgroundUsers();
+
+ assertWithMessage("mAms.getSecondaryDisplayIdsForStartingBackgroundUsers()")
+ .that(displayIds).isNull();
+ }
+
+ @Test
+ public void testGetSecondaryDisplayIdsForStartingBackgroundUsers_noDisplaysAtAll() {
+ mockUmIsUsersOnSecondaryDisplaysEnabled(true);
+ mockGetDisplays();
+
+ int[] displayIds = mInjector.getSecondaryDisplayIdsForStartingBackgroundUsers();
+
+ assertWithMessage("mAms.getSecondaryDisplayIdsForStartingBackgroundUsers()")
+ .that(displayIds).isNull();
+ }
+
+ @Test
+ public void testGetSecondaryDisplayIdsForStartingBackgroundUsers_defaultDisplayOnly() {
+ mockUmIsUsersOnSecondaryDisplaysEnabled(true);
+ mockGetDisplays(mDefaultDisplay);
+
+ int[] displayIds = mInjector.getSecondaryDisplayIdsForStartingBackgroundUsers();
+
+ assertWithMessage("mAms.getSecondaryDisplayIdsForStartingBackgroundUsers()")
+ .that(displayIds).isNull();
+ }
+
+ @Test
+ public void testGetSecondaryDisplayIdsForStartingBackgroundUsers_noDefaultDisplay() {
+ mockUmIsUsersOnSecondaryDisplaysEnabled(true);
+ mockGetDisplays(validDisplay(42));
+
+ int[] displayIds = mInjector.getSecondaryDisplayIdsForStartingBackgroundUsers();
+
+ assertWithMessage("mAms.getSecondaryDisplayIdsForStartingBackgroundUsers()")
+ .that(displayIds).isNull();
+ }
+
+ @Test
+ public void testGetSecondaryDisplayIdsForStartingBackgroundUsers_mixed() {
+ mockUmIsUsersOnSecondaryDisplaysEnabled(true);
+ mockGetDisplays(mDefaultDisplay, validDisplay(42), invalidDisplay(108));
+
+ int[] displayIds = mInjector.getSecondaryDisplayIdsForStartingBackgroundUsers();
+
+ assertWithMessage("mAms.getSecondaryDisplayIdsForStartingBackgroundUsers()")
+ .that(displayIds).isNotNull();
+ assertWithMessage("mAms.getSecondaryDisplayIdsForStartingBackgroundUsers()")
+ .that(displayIds).asList().containsExactly(42);
+ }
+
+ // Extra test to make sure the array is properly copied...
+ @Test
+ public void testGetSecondaryDisplayIdsForStartingBackgroundUsers_mixed_invalidFirst() {
+ mockUmIsUsersOnSecondaryDisplaysEnabled(true);
+ mockGetDisplays(invalidDisplay(108), mDefaultDisplay, validDisplay(42));
+
+ int[] displayIds = mInjector.getSecondaryDisplayIdsForStartingBackgroundUsers();
+
+ assertWithMessage("mAms.getSecondaryDisplayIdsForStartingBackgroundUsers()")
+ .that(displayIds).asList().containsExactly(42);
+ }
+
+ private Display validDisplay(int displayId) {
+ return mockDisplay(displayId, /* valid= */ true);
+ }
+
+ private Display invalidDisplay(int displayId) {
+ return mockDisplay(displayId, /* valid= */ false);
+ }
+
+ private Display mockDisplay(int displayId, boolean valid) {
+ Display display = mock(Display.class);
+
+ when(display.getDisplayId()).thenReturn(displayId);
+ when(display.isValid()).thenReturn(valid);
+
+ return display;
+ }
+
+ private void mockGetDisplays(Display... displays) {
+ Log.d(TAG, "mockGetDisplays(): " + Arrays.toString(displays));
+ when(mDisplayManager.getDisplays()).thenReturn(displays);
+ }
+
+ private void mockUmIsUsersOnSecondaryDisplaysEnabled(boolean enabled) {
+ Log.d(TAG, "Mocking UserManager.isUsersOnSecondaryDisplaysEnabled() to return " + enabled);
+ doReturn(enabled).when(() -> UserManager.isUsersOnSecondaryDisplaysEnabled());
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java
index 86e12647..e1713b0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java
@@ -34,12 +34,9 @@
import static com.android.server.appop.AppOpsUidStateTracker.processStateToUidState;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
@@ -49,25 +46,22 @@
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
-import android.os.Handler;
-import android.os.Message;
import android.util.SparseArray;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.internal.os.Clock;
import com.android.server.appop.AppOpsUidStateTracker.UidStateChangedCallback;
+import com.android.server.appop.AppOpsUidStateTrackerImpl.DelayableExecutor;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.quality.Strictness;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.concurrent.atomic.AtomicReference;
+import java.util.PriorityQueue;
public class AppOpsUidStateTrackerTest {
@@ -81,12 +75,11 @@
ActivityManagerInternal mAmi;
@Mock
- Handler mHandler;
-
- @Mock
AppOpsService.Constants mConstants;
- AppOpsUidStateTrackerTestClock mClock = new AppOpsUidStateTrackerTestClock();
+ AppOpsUidStateTrackerTestExecutor mExecutor = new AppOpsUidStateTrackerTestExecutor();
+
+ AppOpsUidStateTrackerTestClock mClock = new AppOpsUidStateTrackerTestClock(mExecutor);
AppOpsUidStateTracker mIntf;
@@ -101,7 +94,8 @@
mConstants.TOP_STATE_SETTLE_TIME = 10 * 1000L;
mConstants.FG_SERVICE_STATE_SETTLE_TIME = 5 * 1000L;
mConstants.BG_STATE_SETTLE_TIME = 1 * 1000L;
- mIntf = new AppOpsUidStateTrackerImpl(mAmi, mHandler, mClock, mConstants);
+ mIntf = new AppOpsUidStateTrackerImpl(mAmi, mExecutor, mClock, mConstants,
+ Thread.currentThread());
}
@After
@@ -263,18 +257,10 @@
// Still in foreground due to settle time
assertForeground(UID);
- AtomicReference<Message> messageAtomicReference = new AtomicReference<>();
- AtomicLong delayAtomicReference = new AtomicLong();
+ mClock.advanceTime(mConstants.TOP_STATE_SETTLE_TIME - 1);
+ assertForeground(UID);
- getPostDelayedMessageArguments(messageAtomicReference, delayAtomicReference);
- Message message = messageAtomicReference.get();
- long delay = delayAtomicReference.get();
-
- assertNotNull(message);
- assertEquals(mConstants.TOP_STATE_SETTLE_TIME + 1, delay);
-
- mClock.advanceTime(mConstants.TOP_STATE_SETTLE_TIME + 1);
- message.getCallback().run();
+ mClock.advanceTime(1);
assertBackground(UID);
}
@@ -291,18 +277,10 @@
// Still in foreground due to settle time
assertForeground(UID);
- AtomicReference<Message> messageAtomicReference = new AtomicReference<>();
- AtomicLong delayAtomicReference = new AtomicLong();
+ mClock.advanceTime(mConstants.FG_SERVICE_STATE_SETTLE_TIME - 1);
+ assertForeground(UID);
- getPostDelayedMessageArguments(messageAtomicReference, delayAtomicReference);
- Message message = messageAtomicReference.get();
- long delay = delayAtomicReference.get();
-
- assertNotNull(message);
- assertEquals(mConstants.FG_SERVICE_STATE_SETTLE_TIME + 1, delay);
-
- mClock.advanceTime(mConstants.FG_SERVICE_STATE_SETTLE_TIME + 1);
- message.getCallback().run();
+ mClock.advanceTime(1);
assertBackground(UID);
}
@@ -319,14 +297,8 @@
// Still in foreground due to settle time
assertForeground(UID);
- AtomicReference<Message> messageAtomicReference = new AtomicReference<>();
-
- getPostDelayedMessageArguments(messageAtomicReference, null);
- Message message = messageAtomicReference.get();
-
// 1 ms short of settle time
mClock.advanceTime(mConstants.FG_SERVICE_STATE_SETTLE_TIME - 1);
- message.getCallback().run();
assertForeground(UID);
}
@@ -471,8 +443,6 @@
.topState()
.update();
- getLatestPostMessageArgument().getCallback().run();
-
verify(cb).onUidStateChanged(eq(UID), eq(UID_STATE_TOP), eq(true));
}
@@ -484,8 +454,6 @@
.foregroundServiceState()
.update();
- getLatestPostMessageArgument().getCallback().run();
-
verify(cb).onUidStateChanged(eq(UID), eq(UID_STATE_FOREGROUND_SERVICE), eq(true));
}
@@ -497,8 +465,6 @@
.foregroundState()
.update();
- getLatestPostMessageArgument().getCallback().run();
-
verify(cb).onUidStateChanged(eq(UID), eq(UID_STATE_FOREGROUND), eq(true));
}
@@ -510,8 +476,6 @@
.backgroundState()
.update();
- getLatestPostMessageArgument().getCallback().run();
-
verify(cb).onUidStateChanged(eq(UID), eq(UID_STATE_BACKGROUND), eq(false));
}
@@ -679,7 +643,6 @@
.nonExistentState()
.update();
- verify(mHandler, never()).post(any());
verify(cb, never()).onUidStateChanged(anyInt(), anyInt(), anyBoolean());
}
@@ -695,7 +658,6 @@
.nonExistentState()
.update();
- getLatestPostMessageArgument().getCallback().run();
verify(cb, atLeastOnce()).onUidStateChanged(eq(UID), eq(UID_STATE_CACHED), eq(false));
}
@@ -711,7 +673,6 @@
.nonExistentState()
.update();
- getLatestPostMessageArgument().getCallback().run();
verify(cb, atLeastOnce()).onUidStateChanged(eq(UID), eq(UID_STATE_CACHED), eq(true));
}
@@ -727,7 +688,6 @@
.nonExistentState()
.update();
- getLatestPostMessageArgument().getCallback().run();
verify(cb, atLeastOnce()).onUidStateChanged(eq(UID), eq(UID_STATE_CACHED), eq(true));
}
@@ -743,10 +703,32 @@
.nonExistentState()
.update();
- getLatestPostMessageArgument().getCallback().run();
verify(cb, atLeastOnce()).onUidStateChanged(eq(UID), eq(UID_STATE_CACHED), eq(true));
}
+ @Test
+ public void testUidStateChangedBackgroundThenForegroundImmediately() {
+ procStateBuilder(UID)
+ .topState()
+ .update();
+
+ UidStateChangedCallback cb = addUidStateChangeCallback();
+
+ procStateBuilder(UID)
+ .backgroundState()
+ .update();
+
+ mClock.advanceTime(mConstants.TOP_STATE_SETTLE_TIME - 1);
+
+ procStateBuilder(UID)
+ .topState()
+ .update();
+
+ mClock.advanceTime(1);
+
+ verify(cb, never()).onUidStateChanged(anyInt(), anyInt(), anyBoolean());
+ }
+
public void testUidStateChangedCallback(int initialState, int finalState) {
int initialUidState = processStateToUidState(initialState);
int finalUidState = processStateToUidState(finalState);
@@ -767,13 +749,9 @@
.update();
if (finalUidStateIsBackgroundAndLessImportant) {
- AtomicReference<Message> delayedMessage = new AtomicReference<>();
- getPostDelayedMessageArguments(delayedMessage, new AtomicLong());
mClock.advanceTime(mConstants.TOP_STATE_SETTLE_TIME + 1);
- delayedMessage.get().getCallback().run();
}
- getLatestPostMessageArgument().getCallback().run();
verify(cb, atLeastOnce())
.onUidStateChanged(eq(UID), eq(finalUidState), eq(foregroundChange));
}
@@ -781,7 +759,7 @@
private UidStateChangedCallback addUidStateChangeCallback() {
UidStateChangedCallback cb =
Mockito.mock(UidStateChangedCallback.class);
- mIntf.addUidStateChangedCallback(mHandler, cb);
+ mIntf.addUidStateChangedCallback(r -> r.run(), cb);
return cb;
}
@@ -795,30 +773,6 @@
assertEquals(MODE_IGNORED, mIntf.evalMode(uid, OP_NO_CAPABILITIES, MODE_FOREGROUND));
}
- private void getPostDelayedMessageArguments(AtomicReference<Message> message,
- AtomicLong delay) {
-
- ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
- ArgumentCaptor<Long> delayCaptor = ArgumentCaptor.forClass(Long.class);
-
- verify(mHandler).sendMessageDelayed(messageCaptor.capture(), delayCaptor.capture());
-
- if (message != null) {
- message.set(messageCaptor.getValue());
- }
- if (delay != null) {
- delay.set(delayCaptor.getValue());
- }
- }
-
- private Message getLatestPostMessageArgument() {
- ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
-
- verify(mHandler, atLeast(1)).sendMessage(messageCaptor.capture());
-
- return messageCaptor.getValue();
- }
-
private UidProcStateUpdateBuilder procStateBuilder(int uid) {
return new UidProcStateUpdateBuilder(mIntf, uid);
}
@@ -896,8 +850,14 @@
private static class AppOpsUidStateTrackerTestClock extends Clock {
+ private AppOpsUidStateTrackerTestExecutor mExecutor;
long mElapsedRealTime = 0x5f3759df;
+ AppOpsUidStateTrackerTestClock(AppOpsUidStateTrackerTestExecutor executor) {
+ mExecutor = executor;
+ executor.setUptime(mElapsedRealTime);
+ }
+
@Override
public long elapsedRealtime() {
return mElapsedRealTime;
@@ -905,6 +865,53 @@
void advanceTime(long time) {
mElapsedRealTime += time;
+ mExecutor.setUptime(mElapsedRealTime); // assume uptime == elapsedtime
+ }
+ }
+
+ private static class AppOpsUidStateTrackerTestExecutor implements DelayableExecutor {
+
+ private static class QueueElement implements Comparable<QueueElement> {
+
+ private long mExecutionTime;
+ private Runnable mRunnable;
+
+ private QueueElement(long executionTime, Runnable runnable) {
+ mExecutionTime = executionTime;
+ mRunnable = runnable;
+ }
+
+ @Override
+ public int compareTo(QueueElement queueElement) {
+ return Long.compare(mExecutionTime, queueElement.mExecutionTime);
+ }
+ }
+
+ private long mUptime = 0;
+
+ private PriorityQueue<QueueElement> mDelayedMessages = new PriorityQueue();
+
+ @Override
+ public void execute(Runnable runnable) {
+ runnable.run();
+ }
+
+ @Override
+ public void executeDelayed(Runnable runnable, long delay) {
+ if (delay <= 0) {
+ execute(runnable);
+ }
+
+ mDelayedMessages.add(new QueueElement(mUptime + delay, runnable));
+ }
+
+ private void setUptime(long uptime) {
+ while (!mDelayedMessages.isEmpty()
+ && mDelayedMessages.peek().mExecutionTime <= uptime) {
+ mDelayedMessages.poll().mRunnable.run();
+ }
+
+ mUptime = uptime;
}
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/JobSchedulerEconomicPolicyTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/JobSchedulerEconomicPolicyTest.java
index 19b798d..b7bbcd75 100644
--- a/services/tests/mockingservicestests/src/com/android/server/tare/JobSchedulerEconomicPolicyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/tare/JobSchedulerEconomicPolicyTest.java
@@ -134,15 +134,36 @@
mEconomicPolicy.getInitialSatiatedConsumptionLimit());
assertEquals(EconomyManager.DEFAULT_JS_HARD_CONSUMPTION_LIMIT_CAKES,
mEconomicPolicy.getHardSatiatedConsumptionLimit());
+
final String pkgRestricted = "com.pkg.restricted";
when(mIrs.isPackageRestricted(anyInt(), eq(pkgRestricted))).thenReturn(true);
+ assertEquals(0, mEconomicPolicy.getMinSatiatedBalance(0, pkgRestricted));
assertEquals(0, mEconomicPolicy.getMaxSatiatedBalance(0, pkgRestricted));
- assertEquals(EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES,
- mEconomicPolicy.getMaxSatiatedBalance(0, "com.any.other.app"));
+
final String pkgExempted = "com.pkg.exempted";
when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true);
assertEquals(EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED_CAKES,
mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted));
+ assertEquals(EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES,
+ mEconomicPolicy.getMaxSatiatedBalance(0, pkgExempted));
+
+ final String pkgUpdater = "com.pkg.updater";
+ when(mIrs.getAppUpdateResponsibilityCount(anyInt(), eq(pkgUpdater))).thenReturn(5);
+ assertEquals(5 * EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER_CAKES
+ + EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES,
+ mEconomicPolicy.getMinSatiatedBalance(0, pkgUpdater));
+ assertEquals(EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES,
+ mEconomicPolicy.getMaxSatiatedBalance(0, pkgUpdater));
+ // Make sure it doesn't suggest a min balance greater than max.
+ final int updateCount = (int) (EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES
+ / EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER_CAKES);
+ when(mIrs.getAppUpdateResponsibilityCount(anyInt(), eq(pkgUpdater)))
+ .thenReturn(updateCount);
+ assertEquals(EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES,
+ mEconomicPolicy.getMinSatiatedBalance(0, pkgUpdater));
+
+ assertEquals(EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES,
+ mEconomicPolicy.getMaxSatiatedBalance(0, "com.any.other.app"));
assertEquals(EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES,
mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app"));
}
@@ -152,8 +173,10 @@
setDeviceConfigCakes(EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT, arcToCake(5));
setDeviceConfigCakes(EconomyManager.KEY_JS_HARD_CONSUMPTION_LIMIT, arcToCake(25));
setDeviceConfigCakes(EconomyManager.KEY_JS_MAX_SATIATED_BALANCE, arcToCake(10));
- setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(9));
- setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(7));
+ setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(6));
+ setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(4));
+ setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER,
+ arcToCake(1));
assertEquals(arcToCake(5), mEconomicPolicy.getInitialSatiatedConsumptionLimit());
assertEquals(arcToCake(25), mEconomicPolicy.getHardSatiatedConsumptionLimit());
@@ -163,8 +186,12 @@
assertEquals(arcToCake(10), mEconomicPolicy.getMaxSatiatedBalance(0, "com.any.other.app"));
final String pkgExempted = "com.pkg.exempted";
when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true);
- assertEquals(arcToCake(9), mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted));
- assertEquals(arcToCake(7), mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app"));
+ assertEquals(arcToCake(6), mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted));
+ assertEquals(arcToCake(4), mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app"));
+ final String pkgUpdater = "com.pkg.updater";
+ when(mIrs.getAppUpdateResponsibilityCount(anyInt(), eq(pkgUpdater))).thenReturn(3);
+ assertEquals(arcToCake(4) + 3 * arcToCake(1),
+ mEconomicPolicy.getMinSatiatedBalance(0, pkgUpdater));
}
@Test
@@ -175,6 +202,8 @@
setDeviceConfigCakes(EconomyManager.KEY_JS_MAX_SATIATED_BALANCE, arcToCake(-1));
setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(-2));
setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(-3));
+ setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER,
+ arcToCake(-4));
assertEquals(arcToCake(1), mEconomicPolicy.getInitialSatiatedConsumptionLimit());
assertEquals(arcToCake(1), mEconomicPolicy.getHardSatiatedConsumptionLimit());
@@ -186,6 +215,10 @@
when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true);
assertEquals(arcToCake(0), mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted));
assertEquals(arcToCake(0), mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app"));
+ final String pkgUpdater = "com.pkg.updater";
+ when(mIrs.getAppUpdateResponsibilityCount(anyInt(), eq(pkgUpdater))).thenReturn(5);
+ assertEquals(arcToCake(0) + 5 * arcToCake(0),
+ mEconomicPolicy.getMinSatiatedBalance(0, pkgUpdater));
// Test min+max reversed.
setDeviceConfigCakes(EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT, arcToCake(5));
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 3c17102..0d6f326 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -37,11 +37,14 @@
import static com.android.server.am.ProcessList.NETWORK_STATE_NO_CHANGE;
import static com.android.server.am.ProcessList.NETWORK_STATE_UNBLOCK;
+import static com.google.common.truth.Truth.assertWithMessage;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
@@ -62,7 +65,6 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.os.Handler;
import android.os.HandlerThread;
@@ -74,6 +76,8 @@
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
import android.util.IntArray;
+import android.util.Log;
+import android.util.Pair;
import androidx.test.filters.MediumTest;
import androidx.test.filters.SmallTest;
@@ -116,6 +120,7 @@
private static final String TAG = ActivityManagerServiceTest.class.getSimpleName();
private static final int TEST_UID = 11111;
+ private static final int USER_ID = 666;
private static final long TEST_PROC_STATE_SEQ1 = 555;
private static final long TEST_PROC_STATE_SEQ2 = 556;
@@ -147,8 +152,8 @@
@Rule public ServiceThreadRule mServiceThreadRule = new ServiceThreadRule();
private Context mContext = getInstrumentation().getTargetContext();
+
@Mock private AppOpsService mAppOpsService;
- @Mock private PackageManager mPackageManager;
private TestInjector mInjector;
private ActivityManagerService mAms;
@@ -828,6 +833,57 @@
true); // expectWait
}
+ @Test
+ public void testGetSecondaryDisplayIdsForStartingBackgroundUsers() {
+ mInjector.secondaryDisplayIdsForStartingBackgroundUsers = new int[]{4, 8, 15, 16, 23, 42};
+
+ int [] displayIds = mAms.getSecondaryDisplayIdsForStartingBackgroundUsers();
+
+ assertWithMessage("mAms.getSecondaryDisplayIdsForStartingBackgroundUsers()")
+ .that(displayIds).asList().containsExactly(4, 8, 15, 16, 23, 42);
+ }
+
+ @Test
+ public void testStartUserInBackgroundOnSecondaryDisplay_invalidDisplay() {
+ mInjector.secondaryDisplayIdsForStartingBackgroundUsers = new int[]{4, 8, 15, 16, 23, 42};
+
+ assertThrows(IllegalArgumentException.class,
+ () -> mAms.startUserInBackgroundOnSecondaryDisplay(USER_ID, 666));
+
+ assertWithMessage("UserController.startUserOnSecondaryDisplay() calls")
+ .that(mInjector.usersStartedOnSecondaryDisplays).isEmpty();
+ }
+
+ @Test
+ public void testStartUserInBackgroundOnSecondaryDisplay_validDisplay_failed() {
+ mInjector.secondaryDisplayIdsForStartingBackgroundUsers = new int[]{ 4, 8, 15, 16, 23, 42 };
+ mInjector.returnValueForstartUserOnSecondaryDisplay = false;
+
+ boolean started = mAms.startUserInBackgroundOnSecondaryDisplay(USER_ID, 42);
+ Log.v(TAG, "Started: " + started);
+
+ assertWithMessage("mAms.startUserInBackgroundOnSecondaryDisplay(%s, 42)", USER_ID)
+ .that(started).isFalse();
+ assertWithMessage("UserController.startUserOnSecondaryDisplay() calls")
+ .that(mInjector.usersStartedOnSecondaryDisplays)
+ .containsExactly(new Pair<>(USER_ID, 42));
+ }
+
+ @Test
+ public void testStartUserInBackgroundOnSecondaryDisplay_validDisplay_success() {
+ mInjector.secondaryDisplayIdsForStartingBackgroundUsers = new int[]{ 4, 8, 15, 16, 23, 42 };
+ mInjector.returnValueForstartUserOnSecondaryDisplay = true;
+
+ boolean started = mAms.startUserInBackgroundOnSecondaryDisplay(USER_ID, 42);
+ Log.v(TAG, "Started: " + started);
+
+ assertWithMessage("mAms.startUserInBackgroundOnSecondaryDisplay(%s, 42)", USER_ID)
+ .that(started).isTrue();
+ assertWithMessage("UserController.startUserOnSecondaryDisplay() calls")
+ .that(mInjector.usersStartedOnSecondaryDisplays)
+ .containsExactly(new Pair<>(USER_ID, 42));
+ }
+
private void verifyWaitingForNetworkStateUpdate(long curProcStateSeq,
long lastNetworkUpdatedProcStateSeq,
final long procStateSeqToWait, boolean expectWait) throws Exception {
@@ -922,7 +978,11 @@
}
private class TestInjector extends Injector {
- private boolean mRestricted = true;
+ public boolean restricted = true;
+ public int[] secondaryDisplayIdsForStartingBackgroundUsers;
+
+ public boolean returnValueForstartUserOnSecondaryDisplay;
+ public List<Pair<Integer, Integer>> usersStartedOnSecondaryDisplays = new ArrayList<>();
TestInjector(Context context) {
super(context);
@@ -940,11 +1000,18 @@
@Override
public boolean isNetworkRestrictedForUid(int uid) {
- return mRestricted;
+ return restricted;
}
- public void setNetworkRestrictedForUid(boolean restricted) {
- mRestricted = restricted;
+ @Override
+ public int[] getSecondaryDisplayIdsForStartingBackgroundUsers() {
+ return secondaryDisplayIdsForStartingBackgroundUsers;
+ }
+
+ @Override
+ public boolean startUserOnSecondaryDisplay(int userId, int displayId) {
+ usersStartedOnSecondaryDisplays.add(new Pair<>(userId, displayId));
+ return returnValueForstartUserOnSecondaryDisplay;
}
}
}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
index 5d9d765..6b8c26d 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
@@ -25,7 +25,6 @@
import android.hardware.display.DisplayManagerInternal;
import android.hardware.input.IInputManager;
-import android.hardware.input.InputManagerInternal;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -39,6 +38,7 @@
import androidx.test.InstrumentationRegistry;
import com.android.server.LocalServices;
+import com.android.server.input.InputManagerInternal;
import org.junit.Before;
import org.junit.Test;
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index ef203d0..57ded99 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -56,7 +56,6 @@
import android.graphics.Point;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.input.IInputManager;
-import android.hardware.input.InputManagerInternal;
import android.hardware.input.VirtualKeyEvent;
import android.hardware.input.VirtualMouseButtonEvent;
import android.hardware.input.VirtualMouseRelativeEvent;
@@ -84,6 +83,7 @@
import com.android.internal.app.BlockedAppStreamingActivity;
import com.android.server.LocalServices;
+import com.android.server.input.InputManagerInternal;
import org.junit.Before;
import org.junit.Test;
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index 8280fc6..fc2a4cf 100644
--- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -38,7 +38,6 @@
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
import android.os.Handler;
import android.os.test.TestLooper;
-import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -56,7 +55,6 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public class AutomaticBrightnessControllerTest {
private static final float BRIGHTNESS_MIN_FLOAT = 0.0f;
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java
index 6a6cd6c..800f60b 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java
@@ -35,7 +35,6 @@
import android.os.Temperature;
import android.os.Temperature.ThrottlingStatus;
import android.os.test.TestLooper;
-import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -57,7 +56,6 @@
import java.util.List;
@SmallTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public class BrightnessThrottlerTest {
private static final float EPSILON = 0.000001f;
diff --git a/services/tests/servicestests/src/com/android/server/display/ColorFadeTest.java b/services/tests/servicestests/src/com/android/server/display/ColorFadeTest.java
index 26a83a2..53d8de0c 100644
--- a/services/tests/servicestests/src/com/android/server/display/ColorFadeTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/ColorFadeTest.java
@@ -24,7 +24,6 @@
import android.content.Context;
import android.hardware.display.DisplayManagerInternal;
-import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -39,7 +38,6 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public class ColorFadeTest {
private static final int DISPLAY_ID = 123;
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
index 66420ad..04702c4 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -27,7 +27,6 @@
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
-import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -44,7 +43,6 @@
import java.nio.file.Path;
@SmallTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public final class DisplayDeviceConfigTest {
private DisplayDeviceConfig mDisplayDeviceConfig;
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index 3eb1dea..3c7bb2a 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -53,12 +53,10 @@
import android.hardware.display.IDisplayManagerCallback;
import android.hardware.display.IVirtualDisplayCallback;
import android.hardware.display.VirtualDisplayConfig;
-import android.hardware.input.InputManagerInternal;
import android.os.Handler;
import android.os.IBinder;
import android.os.MessageQueue;
import android.os.Process;
-import android.platform.test.annotations.Presubmit;
import android.view.Display;
import android.view.DisplayCutout;
import android.view.DisplayEventReceiver;
@@ -78,6 +76,7 @@
import com.android.server.SystemService;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.display.DisplayManagerService.SyncRoot;
+import com.android.server.input.InputManagerInternal;
import com.android.server.lights.LightsManager;
import com.android.server.sensors.SensorManagerInternal;
import com.android.server.wm.WindowManagerInternal;
@@ -106,7 +105,6 @@
import java.util.stream.LongStream;
@SmallTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public class DisplayManagerServiceTest {
private static final int MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS = 1;
diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
index 53fa3e2..a1e5ce7 100644
--- a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
@@ -22,14 +22,10 @@
import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT;
-
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED;
-import static com.android.server.display.AutomaticBrightnessController
- .AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE;
-
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE;
import static com.android.server.display.DisplayDeviceConfig.HDR_PERCENT_OF_SCREEN_REQUIRED_DEFAULT;
-
import static com.android.server.display.HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID;
import static org.junit.Assert.assertEquals;
@@ -51,7 +47,6 @@
import android.os.Temperature;
import android.os.Temperature.ThrottlingStatus;
import android.os.test.TestLooper;
-import android.platform.test.annotations.Presubmit;
import android.test.mock.MockContentResolver;
import android.util.MathUtils;
@@ -76,7 +71,6 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public class HighBrightnessModeControllerTest {
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
index cc68ba8..0b33c30 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -45,7 +45,6 @@
import android.os.PowerManager;
import android.os.Process;
import android.os.test.TestLooper;
-import android.platform.test.annotations.Presubmit;
import android.view.Display;
import android.view.DisplayAddress;
import android.view.DisplayInfo;
@@ -67,7 +66,6 @@
import java.util.Set;
@SmallTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public class LogicalDisplayMapperTest {
private static int sUniqueTestDisplayId = 0;
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
index b0738fd..5a43530 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
@@ -26,7 +26,6 @@
import android.app.PropertyInvalidatedCache;
import android.graphics.Point;
-import android.platform.test.annotations.Presubmit;
import android.view.DisplayInfo;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -40,7 +39,6 @@
import java.io.OutputStream;
@SmallTest
-@Presubmit
public class LogicalDisplayTest {
private static final int DISPLAY_ID = 0;
private static final int LAYER_STACK = 0;
diff --git a/services/tests/servicestests/src/com/android/server/display/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/display/TEST_MAPPING
index 9f1a209..92d8abd 100644
--- a/services/tests/servicestests/src/com/android/server/display/TEST_MAPPING
+++ b/services/tests/servicestests/src/com/android/server/display/TEST_MAPPING
@@ -3,18 +3,10 @@
{
"name": "FrameworksServicesTests",
"options": [
- {
- "include-filter": "com.android.server.display."
- },
- {
- "include-annotation": "android.platform.test.annotations.Presubmit"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- },
- {
- "exclude-annotation": "org.junit.Ignore"
- }
+ {"include-filter": "com.android.server.display"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+ {"exclude-annotation": "org.junit.Ignore"}
]
}
]
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java b/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java
index f69c5c2..fabf535 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java
@@ -19,7 +19,6 @@
import static org.junit.Assert.assertEquals;
import android.hardware.display.BrightnessInfo;
-import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -29,7 +28,6 @@
import org.junit.runner.RunWith;
@SmallTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public final class BrightnessEventTest {
private BrightnessEvent mBrightnessEvent;
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessReasonTest.java b/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessReasonTest.java
index ffc2e0d..57aa61a 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessReasonTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessReasonTest.java
@@ -18,7 +18,6 @@
import static org.junit.Assert.assertEquals;
-import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -28,7 +27,6 @@
import org.junit.runner.RunWith;
@SmallTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public final class BrightnessReasonTest {
private BrightnessReason mBrightnessReason;
diff --git a/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt b/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt
index 844f5d4..e390bcc 100644
--- a/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt
+++ b/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt
@@ -19,13 +19,14 @@
import android.content.Context
import android.content.ContextWrapper
import android.hardware.display.DisplayViewport
-import android.hardware.input.InputManagerInternal
import android.os.IInputConstants
import android.os.test.TestLooper
import android.platform.test.annotations.Presubmit
import android.view.Display
import android.view.PointerIcon
import androidx.test.InstrumentationRegistry
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
@@ -35,7 +36,6 @@
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.never
@@ -43,9 +43,8 @@
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.Mockito.`when`
import org.mockito.junit.MockitoJUnit
-import java.util.concurrent.CountDownLatch
-import java.util.concurrent.TimeUnit
/**
* Tests for {@link InputManagerService}.
diff --git a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
index 90b19a4..04ba7d3 100644
--- a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
@@ -35,13 +35,13 @@
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorManager;
-import android.hardware.input.InputManagerInternal;
import androidx.annotation.NonNull;
import com.android.server.LocalServices;
import com.android.server.devicestate.DeviceState;
import com.android.server.devicestate.DeviceStateProvider;
+import com.android.server.input.InputManagerInternal;
import org.junit.After;
import org.junit.Before;
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index fe46c14..a76b82b 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -58,7 +58,6 @@
import android.content.pm.PackageManager;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
-import android.hardware.input.InputManagerInternal;
import android.media.AudioManagerInternal;
import android.os.Handler;
import android.os.HandlerThread;
@@ -76,6 +75,7 @@
import com.android.internal.accessibility.AccessibilityShortcutController;
import com.android.server.GestureLauncherService;
import com.android.server.LocalServices;
+import com.android.server.input.InputManagerInternal;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.vr.VrManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
index e2c94c5..49fd1ab 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
@@ -296,6 +296,14 @@
}
@Test
+ public void testRemoveTask_stopsRecording_nullSessionShouldNotThrowExceptions() {
+ mContentRecorder.setContentRecordingSession(mTaskSession);
+ mContentRecorder.updateRecording();
+ mContentRecorder.setContentRecordingSession(null);
+ mTask.removeImmediately();
+ }
+
+ @Test
public void testUpdateMirroredSurface_capturedAreaResized() {
mContentRecorder.setContentRecordingSession(mDisplaySession);
mContentRecorder.updateRecording();
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 91b868d..be7c112 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -136,9 +136,6 @@
private final RemoteCallbackList<IVoiceInteractionSessionListener>
mVoiceInteractionSessionListeners = new RemoteCallbackList<>();
- // TODO(b/226201975): remove once RoleService supports pre-created users
- private final ArrayList<UserHandle> mIgnoredPreCreatedUsers = new ArrayList<>();
-
public VoiceInteractionManagerService(Context context) {
super(context);
mContext = context;
@@ -309,24 +306,14 @@
return hotwordDetectionConnection.mIdentity;
}
+ // TODO(b/226201975): remove this method once RoleService supports pre-created users
@Override
public void onPreCreatedUserConversion(int userId) {
- Slogf.d(TAG, "onPreCreatedUserConversion(%d)", userId);
-
- for (int i = 0; i < mIgnoredPreCreatedUsers.size(); i++) {
- UserHandle preCreatedUser = mIgnoredPreCreatedUsers.get(i);
- if (preCreatedUser.getIdentifier() == userId) {
- Slogf.d(TAG, "Updating role on pre-created user %d", userId);
- mServiceStub.mRoleObserver.onRoleHoldersChanged(RoleManager.ROLE_ASSISTANT,
- preCreatedUser);
- mIgnoredPreCreatedUsers.remove(i);
- return;
- }
- }
- Slogf.w(TAG, "onPreCreatedUserConversion(%d): not available on "
- + "mIgnoredPreCreatedUserIds (%s)", userId, mIgnoredPreCreatedUsers);
+ Slogf.d(TAG, "onPreCreatedUserConversion(%d): calling onRoleHoldersChanged() again",
+ userId);
+ mServiceStub.mRoleObserver.onRoleHoldersChanged(RoleManager.ROLE_ASSISTANT,
+ UserHandle.of(userId));
}
-
}
// implementation entry point and binder service
@@ -809,8 +796,10 @@
if (TextUtils.isEmpty(curInteractor)) {
return null;
}
- if (DEBUG) Slog.d(TAG, "getCurInteractor curInteractor=" + curInteractor
+ if (DEBUG) {
+ Slog.d(TAG, "getCurInteractor curInteractor=" + curInteractor
+ " user=" + userHandle);
+ }
return ComponentName.unflattenFromString(curInteractor);
}
@@ -818,8 +807,9 @@
Settings.Secure.putStringForUser(mContext.getContentResolver(),
Settings.Secure.VOICE_INTERACTION_SERVICE,
comp != null ? comp.flattenToShortString() : "", userHandle);
- if (DEBUG) Slog.d(TAG, "setCurInteractor comp=" + comp
- + " user=" + userHandle);
+ if (DEBUG) {
+ Slog.d(TAG, "setCurInteractor comp=" + comp + " user=" + userHandle);
+ }
}
ComponentName findAvailRecognizer(String prefPackage, int userHandle) {
@@ -1917,7 +1907,6 @@
pw.println(" mTemporarilyDisabled: " + mTemporarilyDisabled);
pw.println(" mCurUser: " + mCurUser);
pw.println(" mCurUserSupported: " + mCurUserSupported);
- pw.println(" mIgnoredPreCreatedUsers: " + mIgnoredPreCreatedUsers);
dumpSupportedUsers(pw, " ");
mDbHelper.dump(pw);
if (mImpl == null) {
@@ -2031,6 +2020,11 @@
List<String> roleHolders = mRm.getRoleHoldersAsUser(roleName, user);
+ if (DEBUG) {
+ Slogf.d(TAG, "onRoleHoldersChanged(%s, %s): roleHolders=%s", roleName, user,
+ roleHolders);
+ }
+
// TODO(b/226201975): this method is beling called when a pre-created user is added,
// at which point it doesn't have any role holders. But it's not called again when
// the actual user is added (i.e., when the pre-created user is converted), so we
@@ -2041,9 +2035,9 @@
if (roleHolders.isEmpty()) {
UserInfo userInfo = mUserManagerInternal.getUserInfo(user.getIdentifier());
if (userInfo != null && userInfo.preCreated) {
- Slogf.d(TAG, "onRoleHoldersChanged(): ignoring pre-created user %s for now",
- userInfo.toFullString());
- mIgnoredPreCreatedUsers.add(user);
+ Slogf.d(TAG, "onRoleHoldersChanged(): ignoring pre-created user %s for now,"
+ + " this method will be called again when it's converted to a real"
+ + " user", userInfo.toFullString());
return;
}
}
diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt b/tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt
index a415217..bba819c 100644
--- a/tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt
+++ b/tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt
@@ -82,8 +82,16 @@
if (attr1[i].name != attr2[i].name) {
return false
}
- val v1 = ConstantEvaluator.evaluate(context, attr1[i].value)
- val v2 = ConstantEvaluator.evaluate(context, attr2[i].value)
+ val value1 = attr1[i].value
+ val value2 = attr2[i].value
+ if (value1 == null && value2 == null) {
+ continue
+ }
+ if (value1 == null || value2 == null) {
+ return false
+ }
+ val v1 = ConstantEvaluator.evaluate(context, value1)
+ val v2 = ConstantEvaluator.evaluate(context, value2)
if (v1 != v2) {
return false
}