Merge "Remove some unused fields and methods in WM"
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
index 035c9d2..b2e95a5 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
@@ -16,8 +16,6 @@
package com.android.server.tare;
-import static android.text.format.DateUtils.HOUR_IN_MILLIS;
-
import static com.android.server.tare.EconomicPolicy.REGULATION_BASIC_INCOME;
import static com.android.server.tare.EconomicPolicy.REGULATION_BIRTHRIGHT;
import static com.android.server.tare.EconomicPolicy.REGULATION_WEALTH_RECLAMATION;
@@ -69,12 +67,6 @@
private static final boolean DEBUG = InternalResourceService.DEBUG
|| Log.isLoggable(TAG, Log.DEBUG);
- /**
- * The minimum amount of time an app must not have been used by the user before we start
- * regularly reclaiming ARCs from it.
- */
- private static final long MIN_UNUSED_TIME_MS = 3 * 24 * HOUR_IN_MILLIS;
-
private static final String ALARM_TAG_AFFORDABILITY_CHECK = "*tare.affordability_check*";
private final Object mLock;
@@ -550,41 +542,62 @@
/**
* Reclaim a percentage of unused ARCs from every app that hasn't been used recently. The
- * reclamation will not reduce an app's balance below its minimum balance as dictated by the
- * EconomicPolicy.
+ * reclamation will not reduce an app's balance below its minimum balance as dictated by
+ * {@code scaleMinBalance}.
*
- * @param percentage A value between 0 and 1 to indicate how much of the unused balance should
- * be reclaimed.
+ * @param percentage A value between 0 and 1 to indicate how much of the unused balance
+ * should be reclaimed.
+ * @param minUnusedTimeMs The minimum amount of time (in milliseconds) that must have
+ * transpired since the last user usage event before we will consider
+ * reclaiming ARCs from the app.
+ * @param scaleMinBalance Whether or not to used the scaled minimum app balance. If false,
+ * this will use the constant min balance floor given by
+ * {@link EconomicPolicy#getMinSatiatedBalance(int, String)}. If true,
+ * this will use the scaled balance given by
+ * {@link InternalResourceService#getMinBalanceLocked(int, String)}.
*/
@GuardedBy("mLock")
- void reclaimUnusedAssetsLocked(double percentage) {
+ void reclaimUnusedAssetsLocked(double percentage, long minUnusedTimeMs,
+ boolean scaleMinBalance) {
final CompleteEconomicPolicy economicPolicy = mIrs.getCompleteEconomicPolicyLocked();
- final List<PackageInfo> pkgs = mIrs.getInstalledPackages();
+ final SparseArrayMap<String, Ledger> ledgers = mScribe.getLedgersLocked();
final long now = getCurrentTimeMillis();
- for (int i = 0; i < pkgs.size(); ++i) {
- final int userId = UserHandle.getUserId(pkgs.get(i).applicationInfo.uid);
- final String pkgName = pkgs.get(i).packageName;
- final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName);
- // AppStandby only counts elapsed time for things like this
- // TODO: should we use clock time instead?
- final long timeSinceLastUsedMs =
- mAppStandbyInternal.getTimeSinceLastUsedByUser(pkgName, userId);
- if (timeSinceLastUsedMs >= MIN_UNUSED_TIME_MS) {
- // Use a constant floor instead of the scaled floor from the IRS.
- final long minBalance = economicPolicy.getMinSatiatedBalance(userId, pkgName);
+ for (int u = 0; u < ledgers.numMaps(); ++u) {
+ final int userId = ledgers.keyAt(u);
+ for (int p = 0; p < ledgers.numElementsForKey(userId); ++p) {
+ final Ledger ledger = ledgers.valueAt(u, p);
final long curBalance = ledger.getCurrentBalance();
- long toReclaim = (long) (curBalance * percentage);
- if (curBalance - toReclaim < minBalance) {
- toReclaim = curBalance - minBalance;
+ if (curBalance <= 0) {
+ continue;
}
- if (toReclaim > 0) {
- Slog.i(TAG, "Reclaiming unused wealth! Taking " + toReclaim
- + " from " + appToString(userId, pkgName));
+ final String pkgName = ledgers.keyAt(u, p);
+ // AppStandby only counts elapsed time for things like this
+ // TODO: should we use clock time instead?
+ final long timeSinceLastUsedMs =
+ mAppStandbyInternal.getTimeSinceLastUsedByUser(pkgName, userId);
+ if (timeSinceLastUsedMs >= minUnusedTimeMs) {
+ final long minBalance;
+ if (!scaleMinBalance) {
+ // Use a constant floor instead of the scaled floor from the IRS.
+ minBalance = economicPolicy.getMinSatiatedBalance(userId, pkgName);
+ } else {
+ minBalance = mIrs.getMinBalanceLocked(userId, pkgName);
+ }
+ long toReclaim = (long) (curBalance * percentage);
+ if (curBalance - toReclaim < minBalance) {
+ toReclaim = curBalance - minBalance;
+ }
+ if (toReclaim > 0) {
+ if (DEBUG) {
+ Slog.i(TAG, "Reclaiming unused wealth! Taking " + toReclaim
+ + " from " + appToString(userId, pkgName));
+ }
- recordTransactionLocked(userId, pkgName, ledger,
- new Ledger.Transaction(
- now, now, REGULATION_WEALTH_RECLAMATION, null, -toReclaim),
- true);
+ recordTransactionLocked(userId, pkgName, ledger,
+ new Ledger.Transaction(
+ now, now, REGULATION_WEALTH_RECLAMATION, null, -toReclaim),
+ true);
+ }
}
}
}
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 a39fd47..1ad7407 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -18,6 +18,7 @@
import static android.provider.Settings.Global.TARE_ALARM_MANAGER_CONSTANTS;
import static android.provider.Settings.Global.TARE_JOB_SCHEDULER_CONSTANTS;
+import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
@@ -85,6 +86,11 @@
static final long UNUSED_RECLAMATION_PERIOD_MS = 24 * HOUR_IN_MILLIS;
/** How much of an app's unused wealth should be reclaimed periodically. */
private static final float DEFAULT_UNUSED_RECLAMATION_PERCENTAGE = .1f;
+ /**
+ * The minimum amount of time an app must not have been used by the user before we start
+ * periodically reclaiming ARCs from it.
+ */
+ private static final long MIN_UNUSED_TIME_MS = 3 * DAY_IN_MILLIS;
/** The amount of time to delay reclamation by after boot. */
private static final long RECLAMATION_STARTUP_DELAY_MS = 30_000L;
private static final int PACKAGE_QUERY_FLAGS =
@@ -107,6 +113,44 @@
@GuardedBy("mLock")
private CompleteEconomicPolicy mCompleteEconomicPolicy;
+ private static final class ReclamationConfig {
+ /**
+ * ARC circulation threshold (% circulating vs scaled maximum) above which this config
+ * should come into play.
+ */
+ public final double circulationPercentageThreshold;
+ /** @see Agent#reclaimUnusedAssetsLocked(double, long, boolean) */
+ public final double reclamationPercentage;
+ /** @see Agent#reclaimUnusedAssetsLocked(double, long, boolean) */
+ public final long minUsedTimeMs;
+ /** @see Agent#reclaimUnusedAssetsLocked(double, long, boolean) */
+ public final boolean scaleMinBalance;
+
+ ReclamationConfig(double circulationPercentageThreshold, double reclamationPercentage,
+ long minUsedTimeMs, boolean scaleMinBalance) {
+ this.circulationPercentageThreshold = circulationPercentageThreshold;
+ this.reclamationPercentage = reclamationPercentage;
+ this.minUsedTimeMs = minUsedTimeMs;
+ this.scaleMinBalance = scaleMinBalance;
+ }
+ }
+
+ /**
+ * Sorted list of reclamation configs used to determine how many credits to force reclaim when
+ * the circulation percentage is too high. The list should *always* be sorted in descending
+ * order of {@link ReclamationConfig#circulationPercentageThreshold}.
+ */
+ @GuardedBy("mLock")
+ private final List<ReclamationConfig> mReclamationConfigs = List.of(
+ new ReclamationConfig(2, .75, 12 * HOUR_IN_MILLIS, true),
+ new ReclamationConfig(1.6, .5, DAY_IN_MILLIS, true),
+ new ReclamationConfig(1.4, .25, DAY_IN_MILLIS, true),
+ new ReclamationConfig(1.2, .25, 2 * DAY_IN_MILLIS, true),
+ new ReclamationConfig(1, .25, MIN_UNUSED_TIME_MS, false),
+ new ReclamationConfig(
+ .9, DEFAULT_UNUSED_RECLAMATION_PERCENTAGE, MIN_UNUSED_TIME_MS, false)
+ );
+
@NonNull
@GuardedBy("mLock")
private final List<PackageInfo> mPkgCache = new ArrayList<>();
@@ -190,7 +234,8 @@
@Override
public void onAlarm() {
synchronized (mLock) {
- mAgent.reclaimUnusedAssetsLocked(DEFAULT_UNUSED_RECLAMATION_PERCENTAGE);
+ mAgent.reclaimUnusedAssetsLocked(
+ DEFAULT_UNUSED_RECLAMATION_PERCENTAGE, MIN_UNUSED_TIME_MS, false);
mScribe.setLastReclamationTimeLocked(getCurrentTimeMillis());
scheduleUnusedWealthReclamationLocked();
}
@@ -200,6 +245,7 @@
private static final int MSG_NOTIFY_AFFORDABILITY_CHANGE_LISTENER = 0;
private static final int MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT = 1;
private static final int MSG_PROCESS_USAGE_EVENT = 2;
+ private static final int MSG_MAYBE_FOCE_RECLAIM = 3;
private static final String ALARM_TAG_WEALTH_RECLAMATION = "*tare.reclamation*";
private static final String KEY_PKG = "pkg";
@@ -317,6 +363,8 @@
final int newBatteryLevel = getCurrentBatteryLevel();
if (newBatteryLevel > mCurrentBatteryLevel) {
mAgent.distributeBasicIncomeLocked(newBatteryLevel);
+ } else if (newBatteryLevel < mCurrentBatteryLevel) {
+ mHandler.obtainMessage(MSG_MAYBE_FOCE_RECLAIM).sendToTarget();
}
mCurrentBatteryLevel = newBatteryLevel;
}
@@ -511,6 +559,48 @@
}
}
+ /**
+ * Reclaim unused ARCs above apps' minimum balances if there are too many credits currently
+ * in circulation.
+ */
+ @GuardedBy("mLock")
+ private void maybeForceReclaimLocked() {
+ final long maxCirculation = getMaxCirculationLocked();
+ if (maxCirculation == 0) {
+ Slog.wtf(TAG, "Max scaled circulation is 0...");
+ mAgent.reclaimUnusedAssetsLocked(1, HOUR_IN_MILLIS, true);
+ mScribe.setLastReclamationTimeLocked(getCurrentTimeMillis());
+ scheduleUnusedWealthReclamationLocked();
+ return;
+ }
+ final long curCirculation = mScribe.getNarcsInCirculationLocked();
+ final double circulationPerc = 1.0 * curCirculation / maxCirculation;
+ if (DEBUG) {
+ Slog.d(TAG, "Circulation %: " + circulationPerc);
+ }
+ final int numConfigs = mReclamationConfigs.size();
+ if (numConfigs == 0) {
+ return;
+ }
+ // The configs are sorted in descending order of circulationPercentageThreshold, so we can
+ // short-circuit if the current circulation is lower than the lowest threshold.
+ if (circulationPerc
+ < mReclamationConfigs.get(numConfigs - 1).circulationPercentageThreshold) {
+ return;
+ }
+ // TODO: maybe exclude apps we think will be launched in the next few hours
+ for (int i = 0; i < numConfigs; ++i) {
+ final ReclamationConfig config = mReclamationConfigs.get(i);
+ if (circulationPerc >= config.circulationPercentageThreshold) {
+ mAgent.reclaimUnusedAssetsLocked(
+ config.reclamationPercentage, config.minUsedTimeMs, config.scaleMinBalance);
+ mScribe.setLastReclamationTimeLocked(getCurrentTimeMillis());
+ scheduleUnusedWealthReclamationLocked();
+ break;
+ }
+ }
+ }
+
private void registerListeners() {
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_LEVEL_CHANGED);
@@ -594,6 +684,14 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
+ case MSG_MAYBE_FOCE_RECLAIM: {
+ removeMessages(MSG_MAYBE_FOCE_RECLAIM);
+ synchronized (mLock) {
+ maybeForceReclaimLocked();
+ }
+ }
+ break;
+
case MSG_NOTIFY_AFFORDABILITY_CHANGE_LISTENER: {
Bundle data = msg.getData();
final int userId = msg.arg1;
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
index d4c6c8c..42f3d9a 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
@@ -147,6 +147,12 @@
return ledger;
}
+ @GuardedBy("mIrs.getLock()")
+ @NonNull
+ SparseArrayMap<String, Ledger> getLedgersLocked() {
+ return mLedgers;
+ }
+
/** Returns the total amount of narcs currently allocated to apps. */
@GuardedBy("mIrs.getLock()")
long getNarcsInCirculationLocked() {
diff --git a/core/api/current.txt b/core/api/current.txt
index 52e0daff..066746d 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -30975,6 +30975,7 @@
field public static final int Q = 29; // 0x1d
field public static final int R = 30; // 0x1e
field public static final int S = 31; // 0x1f
+ field public static final int S_V2 = 32; // 0x20
field public static final int TIRAMISU = 10000; // 0x2710
}
@@ -31589,7 +31590,9 @@
method @NonNull public static android.os.Parcel obtain();
method @NonNull public static android.os.Parcel obtain(@NonNull android.os.IBinder);
method @Nullable public Object[] readArray(@Nullable ClassLoader);
+ method @Nullable public <T> T[] readArray(@Nullable ClassLoader, @NonNull Class<T>);
method @Nullable public java.util.ArrayList readArrayList(@Nullable ClassLoader);
+ method @Nullable public <T> java.util.ArrayList<T> readArrayList(@Nullable ClassLoader, @NonNull Class<? extends T>);
method public void readBinderArray(@NonNull android.os.IBinder[]);
method public void readBinderList(@NonNull java.util.List<android.os.IBinder>);
method public boolean readBoolean();
@@ -31617,6 +31620,7 @@
method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader);
method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader, @NonNull Class<T>);
method @Nullable public android.os.Parcelable[] readParcelableArray(@Nullable ClassLoader);
+ method @Nullable public <T> T[] readParcelableArray(@Nullable ClassLoader, @NonNull Class<T>);
method @Nullable public android.os.Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader);
method @Nullable public <T> android.os.Parcelable.Creator<T> readParcelableCreator(@Nullable ClassLoader, @NonNull Class<T>);
method @NonNull public <T extends android.os.Parcelable> java.util.List<T> readParcelableList(@NonNull java.util.List<T>, @Nullable ClassLoader);
@@ -31626,6 +31630,7 @@
method @NonNull public android.util.Size readSize();
method @NonNull public android.util.SizeF readSizeF();
method @Nullable public <T> android.util.SparseArray<T> readSparseArray(@Nullable ClassLoader);
+ method @Nullable public <T> android.util.SparseArray<T> readSparseArray(@Nullable ClassLoader, @NonNull Class<? extends T>);
method @Nullable public android.util.SparseBooleanArray readSparseBooleanArray();
method @Nullable public String readString();
method public void readStringArray(@NonNull String[]);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index a0f2d23..95d06b0 100755
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -95,6 +95,7 @@
field public static final String CONTROL_OEM_PAID_NETWORK_PREFERENCE = "android.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE";
field public static final String CONTROL_VPN = "android.permission.CONTROL_VPN";
field public static final String CREATE_USERS = "android.permission.CREATE_USERS";
+ field public static final String CREATE_VIRTUAL_DEVICE = "android.permission.CREATE_VIRTUAL_DEVICE";
field public static final String CRYPT_KEEPER = "android.permission.CRYPT_KEEPER";
field public static final String DEVICE_POWER = "android.permission.DEVICE_POWER";
field public static final String DISABLE_SYSTEM_SOUND_EFFECTS = "android.permission.DISABLE_SYSTEM_SOUND_EFFECTS";
@@ -2333,6 +2334,17 @@
}
+package android.companion.virtual {
+
+ public final class VirtualDeviceManager {
+ }
+
+ public static class VirtualDeviceManager.VirtualDevice implements java.lang.AutoCloseable {
+ method public void close();
+ }
+
+}
+
package android.content {
public class ApexEnvironment {
diff --git a/core/java/android/app/ApplicationLoaders.java b/core/java/android/app/ApplicationLoaders.java
index 08cd0b3..53b16d3 100644
--- a/core/java/android/app/ApplicationLoaders.java
+++ b/core/java/android/app/ApplicationLoaders.java
@@ -48,18 +48,19 @@
ClassLoader parent, String classLoaderName) {
return getClassLoaderWithSharedLibraries(zip, targetSdkVersion, isBundled,
librarySearchPath, libraryPermittedPath, parent, classLoaderName,
- null, null);
+ null, null, null);
}
ClassLoader getClassLoaderWithSharedLibraries(
String zip, int targetSdkVersion, boolean isBundled,
String librarySearchPath, String libraryPermittedPath,
ClassLoader parent, String classLoaderName,
- List<ClassLoader> sharedLibraries, List<String> nativeSharedLibraries) {
+ List<ClassLoader> sharedLibraries, List<String> nativeSharedLibraries,
+ List<ClassLoader> sharedLibrariesLoadedAfterApp) {
// For normal usage the cache key used is the same as the zip path.
return getClassLoader(zip, targetSdkVersion, isBundled, librarySearchPath,
libraryPermittedPath, parent, zip, classLoaderName, sharedLibraries,
- nativeSharedLibraries);
+ nativeSharedLibraries, sharedLibrariesLoadedAfterApp);
}
/**
@@ -71,7 +72,8 @@
*/
ClassLoader getSharedLibraryClassLoaderWithSharedLibraries(String zip, int targetSdkVersion,
boolean isBundled, String librarySearchPath, String libraryPermittedPath,
- ClassLoader parent, String classLoaderName, List<ClassLoader> sharedLibraries) {
+ ClassLoader parent, String classLoaderName, List<ClassLoader> sharedLibraries,
+ List<ClassLoader> sharedLibrariesAfter) {
ClassLoader loader = getCachedNonBootclasspathSystemLib(zip, parent, classLoaderName,
sharedLibraries);
if (loader != null) {
@@ -86,14 +88,15 @@
nativeSharedLibraries.add("ALL");
return getClassLoaderWithSharedLibraries(zip, targetSdkVersion, isBundled,
librarySearchPath, libraryPermittedPath, parent, classLoaderName, sharedLibraries,
- nativeSharedLibraries);
+ nativeSharedLibraries, sharedLibrariesAfter);
}
private ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
String librarySearchPath, String libraryPermittedPath,
ClassLoader parent, String cacheKey,
String classLoaderName, List<ClassLoader> sharedLibraries,
- List<String> nativeSharedLibraries) {
+ List<String> nativeSharedLibraries,
+ List<ClassLoader> sharedLibrariesLoadedAfterApp) {
/*
* This is the parent we use if they pass "null" in. In theory
* this should be the "system" class loader; in practice we
@@ -123,7 +126,7 @@
ClassLoader classloader = ClassLoaderFactory.createClassLoader(
zip, librarySearchPath, libraryPermittedPath, parent,
targetSdkVersion, isBundled, classLoaderName, sharedLibraries,
- nativeSharedLibraries);
+ nativeSharedLibraries, sharedLibrariesLoadedAfterApp);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
@@ -140,7 +143,8 @@
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip);
ClassLoader loader = ClassLoaderFactory.createClassLoader(
- zip, null, parent, classLoaderName, sharedLibraries);
+ zip, null, parent, classLoaderName, sharedLibraries,
+ null /*sharedLibrariesLoadedAfterApp*/);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
return loader;
}
@@ -196,7 +200,7 @@
ClassLoader classLoader = getClassLoader(path, Build.VERSION.SDK_INT, true /*isBundled*/,
null /*librarySearchPath*/, null /*libraryPermittedPath*/, null /*parent*/,
null /*cacheKey*/, null /*classLoaderName*/, sharedLibraries /*sharedLibraries*/,
- null /* nativeSharedLibraries */);
+ null /* nativeSharedLibraries */, null /*sharedLibrariesLoadedAfterApp*/);
if (classLoader == null) {
// bad configuration or break in classloading code
@@ -267,7 +271,8 @@
// stub's APK path, when the actual package path is the donor APK.
return getClassLoader(packagePath, Build.VERSION.SDK_INT, false, libsPath, null, null,
cacheKey, null /* classLoaderName */, null /* sharedLibraries */,
- null /* nativeSharedLibraries */);
+ null /* nativeSharedLibraries */,
+ null /*sharedLibrariesLoadedAfterApp*/);
}
/**
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 2be7803..b052bc5 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -68,6 +68,7 @@
import android.os.WorkSource;
import android.service.voice.IVoiceInteractionSession;
import android.view.IRecentsAnimationRunner;
+import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationDefinition;
import android.view.RemoteAnimationAdapter;
import android.window.IWindowOrganizerController;
@@ -344,4 +345,9 @@
* @param caller is the IApplicationThread representing the calling process.
*/
void setRunningRemoteTransitionDelegate(in IApplicationThread caller);
+
+ /**
+ * Prepare the back preview in the server
+ */
+ void startBackPreview(IRemoteAnimationRunner runner);
}
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 74208c3a..5750484 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -54,10 +54,12 @@
import android.util.AndroidRuntimeException;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.view.DisplayAdjustments;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
@@ -76,6 +78,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
+import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
@@ -700,7 +703,7 @@
ClassLoader createSharedLibraryLoader(SharedLibraryInfo sharedLibrary,
boolean isBundledApp, String librarySearchPath, String libraryPermittedPath) {
List<String> paths = sharedLibrary.getAllCodePaths();
- List<ClassLoader> sharedLibraries = createSharedLibrariesLoaders(
+ Pair<List<ClassLoader>, List<ClassLoader>> sharedLibraries = createSharedLibrariesLoaders(
sharedLibrary.getDependencies(), isBundledApp, librarySearchPath,
libraryPermittedPath);
final String jars = (paths.size() == 1) ? paths.get(0) :
@@ -711,15 +714,31 @@
return ApplicationLoaders.getDefault().getSharedLibraryClassLoaderWithSharedLibraries(jars,
mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath,
libraryPermittedPath, /* parent */ null,
- /* classLoaderName */ null, sharedLibraries);
+ /* classLoaderName */ null, sharedLibraries.first, sharedLibraries.second);
}
- private List<ClassLoader> createSharedLibrariesLoaders(List<SharedLibraryInfo> sharedLibraries,
+ /**
+ *
+ * @return a {@link Pair} of List<ClassLoader> where the first is for standard shared libraries
+ * and the second is list for shared libraries that code should be loaded after the dex
+ */
+ private Pair<List<ClassLoader>, List<ClassLoader>> createSharedLibrariesLoaders(
+ List<SharedLibraryInfo> sharedLibraries,
boolean isBundledApp, String librarySearchPath, String libraryPermittedPath) {
- if (sharedLibraries == null) {
- return null;
+ if (sharedLibraries == null || sharedLibraries.isEmpty()) {
+ return new Pair<>(null, null);
}
+
+ // if configured to do so, shared libs are split into 2 collections: those that are
+ // on the class path before the applications code, which is standard, and those
+ // specified to be loaded after the applications code.
+ HashSet<String> libsToLoadAfter = new HashSet<>();
+ Resources systemR = Resources.getSystem();
+ Collections.addAll(libsToLoadAfter, systemR.getStringArray(
+ R.array.config_sharedLibrariesLoadedAfterApp));
+
List<ClassLoader> loaders = new ArrayList<>();
+ List<ClassLoader> after = new ArrayList<>();
for (SharedLibraryInfo info : sharedLibraries) {
if (info.isNative()) {
// Native shared lib doesn't contribute to the native lib search path. Its name is
@@ -727,10 +746,19 @@
// default linker namespace.
continue;
}
- loaders.add(createSharedLibraryLoader(
- info, isBundledApp, librarySearchPath, libraryPermittedPath));
+ if (libsToLoadAfter.contains(info.getName())) {
+ if (DEBUG) {
+ Slog.v(ActivityThread.TAG,
+ info.getName() + " will be loaded after application code");
+ }
+ after.add(createSharedLibraryLoader(
+ info, isBundledApp, librarySearchPath, libraryPermittedPath));
+ } else {
+ loaders.add(createSharedLibraryLoader(
+ info, isBundledApp, librarySearchPath, libraryPermittedPath));
+ }
}
- return loaders;
+ return new Pair<>(loaders, after);
}
private StrictMode.ThreadPolicy allowThreadDiskReads() {
@@ -955,9 +983,9 @@
// as this is early and necessary.
StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads();
- List<ClassLoader> sharedLibraries = createSharedLibrariesLoaders(
- mApplicationInfo.sharedLibraryInfos, isBundledApp, librarySearchPath,
- libraryPermittedPath);
+ Pair<List<ClassLoader>, List<ClassLoader>> sharedLibraries =
+ createSharedLibrariesLoaders(mApplicationInfo.sharedLibraryInfos, isBundledApp,
+ librarySearchPath, libraryPermittedPath);
List<String> nativeSharedLibraries = new ArrayList<>();
if (mApplicationInfo.sharedLibraryInfos != null) {
@@ -971,7 +999,8 @@
mDefaultClassLoader = ApplicationLoaders.getDefault().getClassLoaderWithSharedLibraries(
zip, mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath,
libraryPermittedPath, mBaseClassLoader,
- mApplicationInfo.classLoaderName, sharedLibraries, nativeSharedLibraries);
+ mApplicationInfo.classLoaderName, sharedLibraries.first, nativeSharedLibraries,
+ sharedLibraries.second);
mAppComponentFactory = createAppFactory(mApplicationInfo, mDefaultClassLoader);
setThreadPolicy(oldPolicy);
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 40c1978..890a8d6 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -54,6 +54,8 @@
import android.bluetooth.BluetoothManager;
import android.companion.CompanionDeviceManager;
import android.companion.ICompanionDeviceManager;
+import android.companion.virtual.IVirtualDeviceManager;
+import android.companion.virtual.VirtualDeviceManager;
import android.content.ClipboardManager;
import android.content.ContentCaptureOptions;
import android.content.Context;
@@ -875,6 +877,16 @@
return new CompanionDeviceManager(service, ctx.getOuterContext());
}});
+ registerService(Context.VIRTUAL_DEVICE_SERVICE, VirtualDeviceManager.class,
+ new CachedServiceFetcher<VirtualDeviceManager>() {
+ @Override
+ public VirtualDeviceManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IVirtualDeviceManager service = IVirtualDeviceManager.Stub.asInterface(
+ ServiceManager.getServiceOrThrow(Context.VIRTUAL_DEVICE_SERVICE));
+ return new VirtualDeviceManager(service, ctx.getOuterContext());
+ }});
+
registerService(Context.CONSUMER_IR_SERVICE, ConsumerIrManager.class,
new CachedServiceFetcher<ConsumerIrManager>() {
@Override
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index 858819e..bb537dd 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -189,7 +189,7 @@
@NonNull
@SystemApi
public static final ParcelUuid CAP =
- ParcelUuid.fromString("EEEEEEEE-EEEE-EEEE-EEEE-EEEEEEEEEEEE");
+ ParcelUuid.fromString("00008FE0-0000-1000-8000-00805F9B34FB");
/** @hide */
@NonNull
@SystemApi
diff --git a/core/java/android/companion/Association.aidl b/core/java/android/companion/AssociationInfo.aidl
similarity index 95%
rename from core/java/android/companion/Association.aidl
rename to core/java/android/companion/AssociationInfo.aidl
index 2a28f1f..abab743 100644
--- a/core/java/android/companion/Association.aidl
+++ b/core/java/android/companion/AssociationInfo.aidl
@@ -15,4 +15,4 @@
*/
package android.companion;
-parcelable Association;
+parcelable AssociationInfo;
diff --git a/core/java/android/companion/Association.java b/core/java/android/companion/AssociationInfo.java
similarity index 91%
rename from core/java/android/companion/Association.java
rename to core/java/android/companion/AssociationInfo.java
index 7cea33d..ab1eb1f 100644
--- a/core/java/android/companion/Association.java
+++ b/core/java/android/companion/AssociationInfo.java
@@ -39,7 +39,7 @@
* TODO(b/1979395): un-hide and rename to AssociationInfo when implementing public APIs that use
* this class.
*/
-public final class Association implements Parcelable {
+public final class AssociationInfo implements Parcelable {
/**
* A unique ID of this Association record.
* Disclosed to the clients (ie. companion applications) for referring to this record (eg. in
@@ -63,7 +63,7 @@
*
* @hide
*/
- public Association(int associationId, @UserIdInt int userId, @NonNull String packageName,
+ public AssociationInfo(int associationId, @UserIdInt int userId, @NonNull String packageName,
@NonNull List<DeviceId> deviceIds, @Nullable String deviceProfile,
boolean managedByCompanionApp, boolean notifyOnDeviceNearby, long timeApprovedMs) {
if (associationId <= 0) {
@@ -178,8 +178,8 @@
@Override
public boolean equals(Object o) {
if (this == o) return true;
- if (!(o instanceof Association)) return false;
- final Association that = (Association) o;
+ if (!(o instanceof AssociationInfo)) return false;
+ final AssociationInfo that = (AssociationInfo) o;
return mAssociationId == that.mAssociationId
&& mUserId == that.mUserId
&& mManagedByCompanionApp == that.mManagedByCompanionApp
@@ -216,7 +216,7 @@
dest.writeLong(mTimeApprovedMs);
}
- private Association(@NonNull Parcel in) {
+ private AssociationInfo(@NonNull Parcel in) {
mAssociationId = in.readInt();
mUserId = in.readInt();
@@ -230,16 +230,16 @@
mTimeApprovedMs = in.readLong();
}
- public static final Parcelable.Creator<Association> CREATOR =
- new Parcelable.Creator<Association>() {
+ public static final Parcelable.Creator<AssociationInfo> CREATOR =
+ new Parcelable.Creator<AssociationInfo>() {
@Override
- public Association[] newArray(int size) {
- return new Association[size];
+ public AssociationInfo[] newArray(int size) {
+ return new AssociationInfo[size];
}
@Override
- public Association createFromParcel(@NonNull Parcel in) {
- return new Association(in);
+ public AssociationInfo createFromParcel(@NonNull Parcel in) {
+ return new AssociationInfo(in);
}
};
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index d42d6b4..a8fe602 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -319,13 +319,13 @@
}
/**
- * Gets all package-device {@link Association}s for the current user.
+ * Gets all package-device {@link AssociationInfo}s for the current user.
*
* @return the associations list
* @hide
*/
@RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES)
- public @NonNull List<Association> getAllAssociations() {
+ public @NonNull List<AssociationInfo> getAllAssociations() {
if (!checkFeaturePresent()) {
return Collections.emptyList();
}
diff --git a/core/java/android/companion/ICompanionDeviceManager.aidl b/core/java/android/companion/ICompanionDeviceManager.aidl
index 25c5cb3..101f948 100644
--- a/core/java/android/companion/ICompanionDeviceManager.aidl
+++ b/core/java/android/companion/ICompanionDeviceManager.aidl
@@ -18,7 +18,7 @@
import android.app.PendingIntent;
import android.companion.IFindDeviceCallback;
-import android.companion.Association;
+import android.companion.AssociationInfo;
import android.companion.AssociationRequest;
import android.content.ComponentName;
@@ -36,7 +36,7 @@
in String callingPackage);
List<String> getAssociations(String callingPackage, int userId);
- List<Association> getAssociationsForUser(int userId);
+ List<AssociationInfo> getAssociationsForUser(int userId);
void disassociate(String deviceMacAddress, String callingPackage);
diff --git a/core/java/android/companion/Association.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
similarity index 74%
copy from core/java/android/companion/Association.aidl
copy to core/java/android/companion/virtual/IVirtualDevice.aidl
index 2a28f1f..0aa442b 100644
--- a/core/java/android/companion/Association.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.companion;
-parcelable Association;
+package android.companion.virtual;
+
+/**
+ * Interface for a virtual device.
+ *
+ * @hide
+ */
+interface IVirtualDevice {
+ void close();
+}
diff --git a/core/java/android/companion/Association.aidl b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
similarity index 62%
copy from core/java/android/companion/Association.aidl
copy to core/java/android/companion/virtual/IVirtualDeviceManager.aidl
index 2a28f1f..91e717d 100644
--- a/core/java/android/companion/Association.aidl
+++ b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.companion;
-parcelable Association;
+package android.companion.virtual;
+
+import android.companion.virtual.IVirtualDevice;
+
+/**
+ * Interface for communication between VirtualDeviceManager and VirtualDeviceManagerService.
+ *
+ * @hide
+ */
+interface IVirtualDeviceManager {
+
+ IVirtualDevice createVirtualDevice();
+}
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
new file mode 100644
index 0000000..6187de5
--- /dev/null
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.companion.virtual;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.RemoteException;
+
+/**
+ * System level service for managing virtual devices.
+ *
+ * @hide
+ */
+@SystemApi
+@SystemService(Context.VIRTUAL_DEVICE_SERVICE)
+public final class VirtualDeviceManager {
+
+ private static final boolean DEBUG = false;
+ private static final String LOG_TAG = "VirtualDeviceManager";
+
+ private final IVirtualDeviceManager mService;
+ private final Context mContext;
+
+ /** @hide */
+ public VirtualDeviceManager(
+ @Nullable IVirtualDeviceManager service, @NonNull Context context) {
+ mService = service;
+ mContext = context;
+ }
+
+ /**
+ * Creates a virtual device.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ @Nullable
+ public VirtualDevice createVirtualDevice() {
+ // TODO(b/194949534): Add CDM association ID here and unhide this API
+ try {
+ IVirtualDevice virtualDevice = mService.createVirtualDevice();
+ return new VirtualDevice(mContext, virtualDevice);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * A virtual device has its own virtual display, audio output, microphone, and camera etc. The
+ * creator of a virtual device can take the output from the virtual display and stream it over
+ * to another device, and inject input events that are received from the remote device.
+ */
+ public static class VirtualDevice implements AutoCloseable {
+
+ private final Context mContext;
+ private final IVirtualDevice mVirtualDevice;
+
+ private VirtualDevice(Context context, IVirtualDevice virtualDevice) {
+ mContext = context.getApplicationContext();
+ mVirtualDevice = virtualDevice;
+ }
+
+ /**
+ * Closes the virtual device, stopping and tearing down any virtual displays,
+ * audio policies, and event injection that's currently in progress.
+ */
+ public void close() {
+ try {
+ mVirtualDevice.close();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index cfd3417..bfc4333 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3799,6 +3799,7 @@
//@hide: INCIDENT_COMPANION_SERVICE,
//@hide: STATS_COMPANION_SERVICE,
COMPANION_DEVICE_SERVICE,
+ //@hide: VIRTUAL_DEVICE_SERVICE,
CROSS_PROFILE_APPS_SERVICE,
//@hide: SYSTEM_UPDATE_SERVICE,
//@hide: TIME_DETECTOR_SERVICE,
@@ -5262,6 +5263,16 @@
/**
* Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.companion.virtual.VirtualDeviceManager} for managing virtual devices.
+ *
+ * @see #getSystemService(String)
+ * @see android.companion.virtual.VirtualDeviceManager
+ * @hide
+ */
+ public static final String VIRTUAL_DEVICE_SERVICE = "virtualdevice";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.hardware.ConsumerIrManager} for transmitting infrared
* signals from the device.
*
diff --git a/core/java/android/content/res/AssetFileDescriptor.java b/core/java/android/content/res/AssetFileDescriptor.java
index e93ec00..dd00c3a 100644
--- a/core/java/android/content/res/AssetFileDescriptor.java
+++ b/core/java/android/content/res/AssetFileDescriptor.java
@@ -21,6 +21,8 @@
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
+import android.system.ErrnoException;
+import android.system.Os;
import java.io.Closeable;
import java.io.FileDescriptor;
@@ -40,7 +42,7 @@
* the data extends to the end of the file.
*/
public static final long UNKNOWN_LENGTH = -1;
-
+
@UnsupportedAppUsage
private final ParcelFileDescriptor mFd;
@UnsupportedAppUsage
@@ -52,11 +54,11 @@
/**
* Create a new AssetFileDescriptor from the given values.
*
- * @param fd The underlying file descriptor.
+ * @param fd The underlying file descriptor.
* @param startOffset The location within the file that the asset starts.
- * This must be 0 if length is UNKNOWN_LENGTH.
- * @param length The number of bytes of the asset, or
- * {@link #UNKNOWN_LENGTH} if it extends to the end of the file.
+ * This must be 0 if length is UNKNOWN_LENGTH.
+ * @param length The number of bytes of the asset, or
+ * {@link #UNKNOWN_LENGTH} if it extends to the end of the file.
*/
public AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset,
long length) {
@@ -66,13 +68,13 @@
/**
* Create a new AssetFileDescriptor from the given values.
*
- * @param fd The underlying file descriptor.
+ * @param fd The underlying file descriptor.
* @param startOffset The location within the file that the asset starts.
- * This must be 0 if length is UNKNOWN_LENGTH.
- * @param length The number of bytes of the asset, or
- * {@link #UNKNOWN_LENGTH} if it extends to the end of the file.
- * @param extras additional details that can be used to interpret the
- * underlying file descriptor. May be null.
+ * This must be 0 if length is UNKNOWN_LENGTH.
+ * @param length The number of bytes of the asset, or
+ * {@link #UNKNOWN_LENGTH} if it extends to the end of the file.
+ * @param extras additional details that can be used to interpret the
+ * underlying file descriptor. May be null.
*/
public AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset,
long length, Bundle extras) {
@@ -97,7 +99,7 @@
public ParcelFileDescriptor getParcelFileDescriptor() {
return mFd;
}
-
+
/**
* Returns the FileDescriptor that can be used to read the data in the
* file.
@@ -105,7 +107,7 @@
public FileDescriptor getFileDescriptor() {
return mFd.getFileDescriptor();
}
-
+
/**
* Returns the byte offset where this asset entry's data starts.
*/
@@ -129,7 +131,7 @@
* ParcelFileDescriptor.getStatSize()} to find the total size of the file,
* returning that number if found or {@link #UNKNOWN_LENGTH} if it could
* not be determined.
- *
+ *
* @see #getDeclaredLength()
*/
public long getLength() {
@@ -139,19 +141,19 @@
long len = mFd.getStatSize();
return len >= 0 ? len : UNKNOWN_LENGTH;
}
-
+
/**
* Return the actual number of bytes that were declared when the
* AssetFileDescriptor was constructed. Will be
* {@link #UNKNOWN_LENGTH} if the length was not declared, meaning data
* should be read to the end of the file.
- *
+ *
* @see #getDeclaredLength()
*/
public long getDeclaredLength() {
return mLength;
}
-
+
/**
* Convenience for calling <code>getParcelFileDescriptor().close()</code>.
*/
@@ -174,7 +176,7 @@
}
return new AutoCloseInputStream(this);
}
-
+
/**
* Create and return a new auto-close output stream for this asset. This
* will either return a full asset {@link AutoCloseOutputStream}, or
@@ -189,13 +191,13 @@
}
return new AutoCloseOutputStream(this);
}
-
+
@Override
public String toString() {
return "{AssetFileDescriptor: " + mFd
+ " start=" + mStartOffset + " len=" + mLength + "}";
}
-
+
/**
* An InputStream you can create on a ParcelFileDescriptor, which will
* take care of calling {@link ParcelFileDescriptor#close
@@ -203,19 +205,24 @@
*/
public static class AutoCloseInputStream
extends ParcelFileDescriptor.AutoCloseInputStream {
- private long mRemaining;
-
+ private final long mSizeFromStartOffset;
+ private final long mStartOffset;
+ private long mPosFromStartOffset;
+
public AutoCloseInputStream(AssetFileDescriptor fd) throws IOException {
super(fd.getParcelFileDescriptor());
+ // this skip is necessary if getChannel() is called
super.skip(fd.getStartOffset());
- mRemaining = (int)fd.getLength();
+ mSizeFromStartOffset = fd.getLength();
+ mStartOffset = fd.getStartOffset();
}
@Override
public int available() throws IOException {
- return mRemaining >= 0
- ? (mRemaining < 0x7fffffff ? (int)mRemaining : 0x7fffffff)
- : super.available();
+ long available = mSizeFromStartOffset - mPosFromStartOffset;
+ return available >= 0
+ ? (available < 0x7fffffff ? (int) available : 0x7fffffff)
+ : 0;
}
@Override
@@ -227,15 +234,24 @@
@Override
public int read(byte[] buffer, int offset, int count) throws IOException {
- if (mRemaining >= 0) {
- if (mRemaining == 0) return -1;
- if (count > mRemaining) count = (int)mRemaining;
- int res = super.read(buffer, offset, count);
- if (res >= 0) mRemaining -= res;
- return res;
+ int available = available();
+
+ if (available <= 0) {
+ return -1;
+ } else {
+ if (count > available) count = available;
+ try {
+ int res = Os.pread(getFD(), buffer, offset, count,
+ mStartOffset + mPosFromStartOffset);
+ // pread returns 0 at end of file, while java's InputStream interface
+ // requires -1
+ if (res == 0) res = -1;
+ if (res >= 0) mPosFromStartOffset += res;
+ return res;
+ } catch (ErrnoException e) {
+ throw new IOException(e);
+ }
}
-
- return super.read(buffer, offset, count);
}
@Override
@@ -245,41 +261,31 @@
@Override
public long skip(long count) throws IOException {
- if (mRemaining >= 0) {
- if (mRemaining == 0) return -1;
- if (count > mRemaining) count = mRemaining;
- long res = super.skip(count);
- if (res >= 0) mRemaining -= res;
- return res;
+ int available = available();
+ if (available <= 0) {
+ return -1;
+ } else {
+ if (count > available) count = available;
+ mPosFromStartOffset += count;
+ return count;
}
-
- return super.skip(count);
}
@Override
public void mark(int readlimit) {
- if (mRemaining >= 0) {
- // Not supported.
- return;
- }
- super.mark(readlimit);
+ // Not supported.
+ return;
}
@Override
public boolean markSupported() {
- if (mRemaining >= 0) {
- return false;
- }
- return super.markSupported();
+ return false;
}
@Override
public synchronized void reset() throws IOException {
- if (mRemaining >= 0) {
- // Not supported.
- return;
- }
- super.reset();
+ // Not supported.
+ return;
}
}
@@ -291,25 +297,25 @@
public static class AutoCloseOutputStream
extends ParcelFileDescriptor.AutoCloseOutputStream {
private long mRemaining;
-
+
public AutoCloseOutputStream(AssetFileDescriptor fd) throws IOException {
super(fd.getParcelFileDescriptor());
if (fd.getParcelFileDescriptor().seekTo(fd.getStartOffset()) < 0) {
throw new IOException("Unable to seek");
}
- mRemaining = (int)fd.getLength();
+ mRemaining = (int) fd.getLength();
}
@Override
public void write(byte[] buffer, int offset, int count) throws IOException {
if (mRemaining >= 0) {
if (mRemaining == 0) return;
- if (count > mRemaining) count = (int)mRemaining;
+ if (count > mRemaining) count = (int) mRemaining;
super.write(buffer, offset, count);
mRemaining -= count;
return;
}
-
+
super.write(buffer, offset, count);
}
@@ -318,12 +324,12 @@
if (mRemaining >= 0) {
if (mRemaining == 0) return;
int count = buffer.length;
- if (count > mRemaining) count = (int)mRemaining;
+ if (count > mRemaining) count = (int) mRemaining;
super.write(buffer);
mRemaining -= count;
return;
}
-
+
super.write(buffer);
}
@@ -335,7 +341,7 @@
mRemaining--;
return;
}
-
+
super.write(oneByte);
}
}
@@ -375,6 +381,7 @@
public AssetFileDescriptor createFromParcel(Parcel in) {
return new AssetFileDescriptor(in);
}
+
public AssetFileDescriptor[] newArray(int size) {
return new AssetFileDescriptor[size];
}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 394d270..743468a 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1024,7 +1024,7 @@
* will also enable {@link StrictMode.ThreadPolicy.Builder#detectUnbufferedIo}.</li>
* <li>{@link android.provider.DocumentsContract}'s various methods will throw failure
* exceptions back to the caller instead of returning null.
- * <li>{@link View#hasFocusable View.hasFocusable} now includes auto-focusable views.</li>
+ * <li>{@link View#hasFocusable() View.hasFocusable} now includes auto-focusable views.</li>
* <li>{@link android.view.SurfaceView} will no longer always change the underlying
* Surface object when something about it changes; apps need to look at the current
* state of the object to determine which things they are interested in have changed.</li>
@@ -1132,6 +1132,13 @@
public static final int S = 31;
/**
+ * S V2.
+ *
+ * Once more unto the breach, dear friends, once more.
+ */
+ public static final int S_V2 = 32;
+
+ /**
* Tiramisu.
*/
public static final int TIRAMISU = CUR_DEVELOPMENT;
diff --git a/core/java/android/os/BytesMatcher.java b/core/java/android/os/BytesMatcher.java
deleted file mode 100644
index 8974c5e..0000000
--- a/core/java/android/os/BytesMatcher.java
+++ /dev/null
@@ -1,315 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.os;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.bluetooth.BluetoothUuid;
-import android.net.MacAddress;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.internal.util.HexDump;
-
-import java.util.ArrayList;
-import java.util.function.Predicate;
-
-/**
- * Predicate that tests if a given {@code byte[]} value matches a set of
- * configured rules.
- * <p>
- * Rules are tested in the order in which they were originally added, which
- * means a narrow rule can reject a specific value before a later broader rule
- * might accept that same value, or vice versa.
- * <p>
- * Matchers can contain rules of varying lengths, and tested values will only be
- * matched against rules of the exact same length. This is designed to support
- * {@link BluetoothUuid} style values which can be variable length.
- *
- * @hide
- */
-public class BytesMatcher implements Predicate<byte[]> {
- private static final String TAG = "BytesMatcher";
-
- private static final char TYPE_EXACT_ACCEPT = '+';
- private static final char TYPE_EXACT_REJECT = '-';
- private static final char TYPE_PREFIX_ACCEPT = '⊆';
- private static final char TYPE_PREFIX_REJECT = '⊈';
-
- private final ArrayList<Rule> mRules = new ArrayList<>();
-
- private static class Rule {
- public final char type;
- public final @NonNull byte[] value;
- public final @Nullable byte[] mask;
-
- public Rule(char type, @NonNull byte[] value, @Nullable byte[] mask) {
- if (mask != null && value.length != mask.length) {
- throw new IllegalArgumentException(
- "Expected length " + value.length + " but found " + mask.length);
- }
- this.type = type;
- this.value = value;
- this.mask = mask;
- }
-
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder();
- encode(builder);
- return builder.toString();
- }
-
- public void encode(@NonNull StringBuilder builder) {
- builder.append(type);
- builder.append(HexDump.toHexString(value));
- if (mask != null) {
- builder.append('/');
- builder.append(HexDump.toHexString(mask));
- }
- }
-
- public boolean test(@NonNull byte[] value) {
- switch (type) {
- case TYPE_EXACT_ACCEPT:
- case TYPE_EXACT_REJECT:
- if (value.length != this.value.length) {
- return false;
- }
- break;
- case TYPE_PREFIX_ACCEPT:
- case TYPE_PREFIX_REJECT:
- if (value.length < this.value.length) {
- return false;
- }
- break;
- }
- for (int i = 0; i < this.value.length; i++) {
- byte local = this.value[i];
- byte remote = value[i];
- if (this.mask != null) {
- local &= this.mask[i];
- remote &= this.mask[i];
- }
- if (local != remote) {
- return false;
- }
- }
- return true;
- }
- }
-
- /**
- * Add a rule that will result in {@link #test(byte[])} returning
- * {@code true} when a value being tested matches it. This rule will only
- * match values of the exact same length.
- * <p>
- * Rules are tested in the order in which they were originally added, which
- * means a narrow rule can reject a specific value before a later broader
- * rule might accept that same value, or vice versa.
- *
- * @param value to be matched
- * @param mask to be applied to both values before testing for equality; if
- * {@code null} then both values must match exactly
- */
- public void addExactAcceptRule(@NonNull byte[] value, @Nullable byte[] mask) {
- mRules.add(new Rule(TYPE_EXACT_ACCEPT, value, mask));
- }
-
- /**
- * Add a rule that will result in {@link #test(byte[])} returning
- * {@code false} when a value being tested matches it. This rule will only
- * match values of the exact same length.
- * <p>
- * Rules are tested in the order in which they were originally added, which
- * means a narrow rule can reject a specific value before a later broader
- * rule might accept that same value, or vice versa.
- *
- * @param value to be matched
- * @param mask to be applied to both values before testing for equality; if
- * {@code null} then both values must match exactly
- */
- public void addExactRejectRule(@NonNull byte[] value, @Nullable byte[] mask) {
- mRules.add(new Rule(TYPE_EXACT_REJECT, value, mask));
- }
-
- /**
- * Add a rule that will result in {@link #test(byte[])} returning
- * {@code true} when a value being tested matches it. This rule will match
- * values of the exact same length or longer.
- * <p>
- * Rules are tested in the order in which they were originally added, which
- * means a narrow rule can reject a specific value before a later broader
- * rule might accept that same value, or vice versa.
- *
- * @param value to be matched
- * @param mask to be applied to both values before testing for equality; if
- * {@code null} then both values must match exactly
- */
- public void addPrefixAcceptRule(@NonNull byte[] value, @Nullable byte[] mask) {
- mRules.add(new Rule(TYPE_PREFIX_ACCEPT, value, mask));
- }
-
- /**
- * Add a rule that will result in {@link #test(byte[])} returning
- * {@code false} when a value being tested matches it. This rule will match
- * values of the exact same length or longer.
- * <p>
- * Rules are tested in the order in which they were originally added, which
- * means a narrow rule can reject a specific value before a later broader
- * rule might accept that same value, or vice versa.
- *
- * @param value to be matched
- * @param mask to be applied to both values before testing for equality; if
- * {@code null} then both values must match exactly
- */
- public void addPrefixRejectRule(@NonNull byte[] value, @Nullable byte[] mask) {
- mRules.add(new Rule(TYPE_PREFIX_REJECT, value, mask));
- }
-
- /**
- * Test if the given {@code ParcelUuid} value matches the set of rules
- * configured in this matcher.
- */
- public boolean testBluetoothUuid(@NonNull ParcelUuid value) {
- return test(BluetoothUuid.uuidToBytes(value));
- }
-
- /**
- * Test if the given {@code MacAddress} value matches the set of rules
- * configured in this matcher.
- */
- public boolean testMacAddress(@NonNull MacAddress value) {
- return test(value.toByteArray());
- }
-
- /**
- * Test if the given {@code byte[]} value matches the set of rules
- * configured in this matcher.
- */
- @Override
- public boolean test(@NonNull byte[] value) {
- return test(value, false);
- }
-
- /**
- * Test if the given {@code byte[]} value matches the set of rules
- * configured in this matcher.
- */
- public boolean test(@NonNull byte[] value, boolean defaultValue) {
- final int size = mRules.size();
- for (int i = 0; i < size; i++) {
- final Rule rule = mRules.get(i);
- if (rule.test(value)) {
- switch (rule.type) {
- case TYPE_EXACT_ACCEPT:
- case TYPE_PREFIX_ACCEPT:
- return true;
- case TYPE_EXACT_REJECT:
- case TYPE_PREFIX_REJECT:
- return false;
- }
- }
- }
- return defaultValue;
- }
-
- /**
- * Encode the given matcher into a human-readable {@link String} which can
- * be used to transport matchers across device boundaries.
- * <p>
- * The human-readable format is an ordered list separated by commas, where
- * each rule is a {@code +} or {@code -} symbol indicating if the match
- * should be accepted or rejected, then followed by a hex value and an
- * optional hex mask. For example, {@code -caff,+cafe/ff00} is a valid
- * encoded matcher.
- *
- * @see #decode(String)
- */
- public static @NonNull String encode(@NonNull BytesMatcher matcher) {
- final StringBuilder builder = new StringBuilder();
- final int size = matcher.mRules.size();
- for (int i = 0; i < size; i++) {
- final Rule rule = matcher.mRules.get(i);
- rule.encode(builder);
- builder.append(',');
- }
- if (builder.length() > 0) {
- builder.deleteCharAt(builder.length() - 1);
- }
- return builder.toString();
- }
-
- /**
- * Decode the given human-readable {@link String} used to transport matchers
- * across device boundaries.
- * <p>
- * The human-readable format is an ordered list separated by commas, where
- * each rule is a {@code +} or {@code -} symbol indicating if the match
- * should be accepted or rejected, then followed by a hex value and an
- * optional hex mask. For example, {@code -caff,+cafe/ff00} is a valid
- * encoded matcher.
- *
- * @see #encode(BytesMatcher)
- */
- public static @NonNull BytesMatcher decode(@Nullable String value) {
- final BytesMatcher matcher = new BytesMatcher();
- if (TextUtils.isEmpty(value)) return matcher;
-
- final int length = value.length();
- for (int i = 0; i < length;) {
- final char type = value.charAt(i);
-
- int nextRule = value.indexOf(',', i);
- int nextMask = value.indexOf('/', i);
-
- if (nextRule == -1) nextRule = length;
- if (nextMask > nextRule) nextMask = -1;
-
- final byte[] ruleValue;
- final byte[] ruleMask;
- if (nextMask >= 0) {
- ruleValue = HexDump.hexStringToByteArray(value.substring(i + 1, nextMask));
- ruleMask = HexDump.hexStringToByteArray(value.substring(nextMask + 1, nextRule));
- } else {
- ruleValue = HexDump.hexStringToByteArray(value.substring(i + 1, nextRule));
- ruleMask = null;
- }
-
- switch (type) {
- case TYPE_EXACT_ACCEPT:
- matcher.addExactAcceptRule(ruleValue, ruleMask);
- break;
- case TYPE_EXACT_REJECT:
- matcher.addExactRejectRule(ruleValue, ruleMask);
- break;
- case TYPE_PREFIX_ACCEPT:
- matcher.addPrefixAcceptRule(ruleValue, ruleMask);
- break;
- case TYPE_PREFIX_REJECT:
- matcher.addPrefixRejectRule(ruleValue, ruleMask);
- break;
- default:
- Log.w(TAG, "Ignoring unknown type " + type);
- break;
- }
-
- i = nextRule + 1;
- }
- return matcher;
- }
-}
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index ab2c8c0..1468d9f 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.app.AppOpsManager;
import android.compat.annotation.UnsupportedAppUsage;
@@ -3094,14 +3095,24 @@
* Parcelables.
*/
@Nullable
- public final ArrayList readArrayList(@Nullable ClassLoader loader) {
- int N = readInt();
- if (N < 0) {
- return null;
- }
- ArrayList l = new ArrayList(N);
- readListInternal(l, N, loader, /* clazz */ null);
- return l;
+ public ArrayList readArrayList(@Nullable ClassLoader loader) {
+ return readArrayListInternal(loader, /* clazz */ null);
+ }
+
+ /**
+ * Same as {@link #readArrayList(ClassLoader)} but accepts {@code clazz} parameter as
+ * the type required for each item.
+ *
+ * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized
+ * is not an instance of that class or any of its children classes or there was an error
+ * trying to instantiate an element.
+ */
+ @SuppressLint({"ConcreteCollection", "NullableCollection"})
+ @Nullable
+ public <T> ArrayList<T> readArrayList(@Nullable ClassLoader loader,
+ @NonNull Class<? extends T> clazz) {
+ Objects.requireNonNull(clazz);
+ return readArrayListInternal(loader, clazz);
}
/**
@@ -3111,14 +3122,23 @@
* Parcelables.
*/
@Nullable
- public final Object[] readArray(@Nullable ClassLoader loader) {
- int N = readInt();
- if (N < 0) {
- return null;
- }
- Object[] l = new Object[N];
- readArrayInternal(l, N, loader);
- return l;
+ public Object[] readArray(@Nullable ClassLoader loader) {
+ return readArrayInternal(loader, /* clazz */ null);
+ }
+
+ /**
+ * Same as {@link #readArray(ClassLoader)} but accepts {@code clazz} parameter as
+ * the type required for each item.
+ *
+ * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized
+ * is not an instance of that class or any of its children classes or there was an error
+ * trying to instantiate an element.
+ */
+ @SuppressLint({"ArrayReturn", "NullableCollection"})
+ @Nullable
+ public <T> T[] readArray(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+ Objects.requireNonNull(clazz);
+ return readArrayInternal(loader, clazz);
}
/**
@@ -3128,14 +3148,23 @@
* Parcelables.
*/
@Nullable
- public final <T> SparseArray<T> readSparseArray(@Nullable ClassLoader loader) {
- int N = readInt();
- if (N < 0) {
- return null;
- }
- SparseArray sa = new SparseArray(N);
- readSparseArrayInternal(sa, N, loader);
- return sa;
+ public <T> SparseArray<T> readSparseArray(@Nullable ClassLoader loader) {
+ return readSparseArrayInternal(loader, /* clazz */ null);
+ }
+
+ /**
+ * Same as {@link #readSparseArray(ClassLoader)} but accepts {@code clazz} parameter as
+ * the type required for each item.
+ *
+ * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized
+ * is not an instance of that class or any of its children classes or there was an error
+ * trying to instantiate an element.
+ */
+ @Nullable
+ public <T> SparseArray<T> readSparseArray(@Nullable ClassLoader loader,
+ @NonNull Class<? extends T> clazz) {
+ Objects.requireNonNull(clazz);
+ return readSparseArrayInternal(loader, clazz);
}
/**
@@ -3851,7 +3880,7 @@
"Parcel " + this + ": Unmarshalling unknown type code " + type
+ " at offset " + off);
}
- if (clazz != null && !clazz.isInstance(object)) {
+ if (object != null && clazz != null && !clazz.isInstance(object)) {
throw new BadParcelableException("Unparcelled object " + object
+ " is not an instance of required class " + clazz.getName()
+ " provided in the parameter");
@@ -3910,7 +3939,6 @@
}
/**
- *
* @param clazz The type of the parcelable expected or {@code null} for performing no checks.
*/
@SuppressWarnings("unchecked")
@@ -3969,7 +3997,7 @@
* as the required type.
*
* @throws BadParcelableException Throws BadParcelableException if the item to be deserialized
- * is not an instance of that class or any of its children class or there there was an error
+ * is not an instance of that class or any of its children classes or there there was an error
* trying to read the {@link Parcelable.Creator}.
*/
@Nullable
@@ -4092,17 +4120,25 @@
return p;
}
- /** @hide */
+ /**
+ * Same as {@link #readParcelableArray(ClassLoader)} but accepts {@code clazz} parameter as
+ * the type required for each item.
+ *
+ * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized
+ * is not an instance of that class or any of its children classes or there was an error
+ * trying to instantiate an element.
+ */
+ @SuppressLint({"ArrayReturn", "NullableCollection"})
+ @SuppressWarnings("unchecked")
@Nullable
- public final <T extends Parcelable> T[] readParcelableArray(@Nullable ClassLoader loader,
- @NonNull Class<T> clazz) {
- int N = readInt();
- if (N < 0) {
+ public <T> T[] readParcelableArray(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+ int n = readInt();
+ if (n < 0) {
return null;
}
- T[] p = (T[]) Array.newInstance(clazz, N);
- for (int i = 0; i < N; i++) {
- p[i] = readParcelable(loader);
+ T[] p = (T[]) Array.newInstance(clazz, n);
+ for (int i = 0; i < n; i++) {
+ p[i] = readParcelableInternal(loader, clazz);
}
return p;
}
@@ -4320,9 +4356,12 @@
return result;
}
- private void readListInternal(@NonNull List outVal, int n,
- @Nullable ClassLoader loader) {
- readListInternal(outVal, n, loader, null);
+ /**
+ * The method is replaced by {@link #readListInternal(List, int, ClassLoader, Class)}, however
+ * we are keeping this unused method here to allow unsupported app usages.
+ */
+ private void readListInternal(@NonNull List outVal, int n, @Nullable ClassLoader loader) {
+ readListInternal(outVal, n, loader, /* clazz */ null);
}
/**
@@ -4338,26 +4377,88 @@
}
}
+ /**
+ * @param clazz The type of the object expected or {@code null} for performing no checks.
+ */
+ @SuppressLint({"ConcreteCollection", "NullableCollection"})
+ @Nullable
+ private <T> ArrayList<T> readArrayListInternal(@Nullable ClassLoader loader,
+ @Nullable Class<? extends T> clazz) {
+ int n = readInt();
+ if (n < 0) {
+ return null;
+ }
+ ArrayList<T> l = new ArrayList<>(n);
+ readListInternal(l, n, loader, clazz);
+ return l;
+ }
+
+ /**
+ * The method is replaced by {@link #readArrayInternal(ClassLoader, Class)}, however
+ * we are keeping this unused method here to allow unsupported app usages.
+ */
private void readArrayInternal(@NonNull Object[] outVal, int N,
@Nullable ClassLoader loader) {
for (int i = 0; i < N; i++) {
- Object value = readValue(loader);
- //Log.d(TAG, "Unmarshalling value=" + value);
+ Object value = readValue(loader, /* clazz */ null);
outVal[i] = value;
}
}
+ /**
+ * @param clazz The type of the object expected or {@code null} for performing no checks.
+ */
+ @SuppressWarnings("unchecked")
+ @Nullable
+ private <T> T[] readArrayInternal(@Nullable ClassLoader loader, @Nullable Class<T> clazz) {
+ int n = readInt();
+ if (n < 0) {
+ return null;
+ }
+ T[] outVal = (T[]) ((clazz == null) ? new Object[n] : Array.newInstance(clazz, n));
+
+ for (int i = 0; i < n; i++) {
+ T value = readValue(loader, clazz);
+ outVal[i] = value;
+ }
+ return outVal;
+ }
+
+ /**
+ * The method is replaced by {@link #readSparseArray(ClassLoader, Class)}, however
+ * we are keeping this unused method here to allow unsupported app usages.
+ */
private void readSparseArrayInternal(@NonNull SparseArray outVal, int N,
@Nullable ClassLoader loader) {
while (N > 0) {
int key = readInt();
Object value = readValue(loader);
- //Log.i(TAG, "Unmarshalling key=" + key + " value=" + value);
outVal.append(key, value);
N--;
}
}
+ /**
+ * @param clazz The type of the object expected or {@code null} for performing no checks.
+ */
+ @Nullable
+ private <T> SparseArray<T> readSparseArrayInternal(@Nullable ClassLoader loader,
+ @Nullable Class<? extends T> clazz) {
+ int n = readInt();
+ if (n < 0) {
+ return null;
+ }
+ SparseArray<T> outVal = new SparseArray<>(n);
+
+ while (n > 0) {
+ int key = readInt();
+ T value = readValue(loader, clazz);
+ outVal.append(key, value);
+ n--;
+ }
+ return outVal;
+ }
+
private void readSparseBooleanArrayInternal(@NonNull SparseBooleanArray outVal, int N) {
while (N > 0) {
diff --git a/core/java/android/security/attestationverification/OWNERS b/core/java/android/security/attestationverification/OWNERS
new file mode 100644
index 0000000..80a1f44
--- /dev/null
+++ b/core/java/android/security/attestationverification/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 1111194
+
+dlm@google.com
+dkrahn@google.com
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 9819648..096595f 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -26,7 +26,10 @@
import android.app.AlarmManager;
import android.app.Service;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
+import android.content.ServiceConnection;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
@@ -58,6 +61,8 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayDeque;
+import java.util.function.Consumer;
/**
* Extend this class to implement a custom dream (available to the user as a "Daydream").
@@ -170,6 +175,13 @@
"android.service.dreams.DreamService";
/**
+ * The name of the extra where the dream overlay component is stored.
+ * @hide
+ */
+ public static final String EXTRA_DREAM_OVERLAY_COMPONENT =
+ "android.service.dream.DreamService.dream_overlay_component";
+
+ /**
* Name under which a Dream publishes information about itself.
* This meta-data must reference an XML resource containing
* a <code><{@link android.R.styleable#Dream dream}></code>
@@ -191,6 +203,7 @@
private boolean mCanDoze;
private boolean mDozing;
private boolean mWindowless;
+ private boolean mOverlayServiceBound;
private int mDozeScreenState = Display.STATE_UNKNOWN;
private int mDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
@@ -199,8 +212,62 @@
private DreamServiceWrapper mDreamServiceWrapper;
private Runnable mDispatchAfterOnAttachedToWindow;
+ private OverlayConnection mOverlayConnection;
+
+ private static class OverlayConnection implements ServiceConnection {
+ // Overlay set during onBind.
+ private IDreamOverlay mOverlay;
+ // A Queue of pending requests to execute on the overlay.
+ private ArrayDeque<Consumer<IDreamOverlay>> mRequests;
+
+ OverlayConnection() {
+ mRequests = new ArrayDeque<>();
+ }
+
+ public void request(Consumer<IDreamOverlay> request) {
+ mRequests.push(request);
+ evaluate();
+ }
+
+ private void evaluate() {
+ if (mOverlay == null) {
+ return;
+ }
+
+ // Any new requests that arrive during this loop will be processed synchronously after
+ // the loop exits.
+ while (!mRequests.isEmpty()) {
+ final Consumer<IDreamOverlay> request = mRequests.pop();
+ request.accept(mOverlay);
+ }
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ // Store Overlay and execute pending requests.
+ mOverlay = IDreamOverlay.Stub.asInterface(service);
+ evaluate();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ // Clear Overlay binder to prevent further request processing.
+ mOverlay = null;
+ }
+ }
+
+ private IDreamOverlayCallback mOverlayCallback = new IDreamOverlayCallback.Stub() {
+ @Override
+ public void onExitRequested() {
+ // Simply finish dream when exit is requested.
+ finish();
+ }
+ };
+
+
public DreamService() {
mDreamManager = IDreamManager.Stub.asInterface(ServiceManager.getService(DREAM_SERVICE));
+ mOverlayConnection = new OverlayConnection();
}
/**
@@ -861,6 +928,18 @@
public final IBinder onBind(Intent intent) {
if (mDebug) Slog.v(TAG, "onBind() intent = " + intent);
mDreamServiceWrapper = new DreamServiceWrapper();
+
+ // Connect to the overlay service if present.
+ final ComponentName overlayComponent =
+ intent.getParcelableExtra(EXTRA_DREAM_OVERLAY_COMPONENT);
+ if (overlayComponent != null && !mWindowless) {
+ final Intent overlayIntent = new Intent();
+ overlayIntent.setComponent(overlayComponent);
+
+ mOverlayServiceBound = getApplicationContext().bindService(overlayIntent,
+ mOverlayConnection, Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE);
+ }
+
return mDreamServiceWrapper;
}
@@ -894,6 +973,11 @@
return;
}
+ if (!mWindowless && mOverlayServiceBound) {
+ unbindService(mOverlayConnection);
+ mOverlayServiceBound = false;
+ }
+
try {
// finishSelf will unbind the dream controller from the dream service. This will
// trigger DreamService.this.onDestroy and DreamService.this will die.
@@ -1101,6 +1185,16 @@
}
}
});
+
+ // Request the DreamOverlay be told to dream with dream's window parameters once the service
+ // has connected.
+ mOverlayConnection.request(overlay -> {
+ try {
+ overlay.startDream(mWindow.getAttributes(), mOverlayCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "could not send window attributes:" + e);
+ }
+ });
}
private boolean getWindowFlagValue(int flag, boolean defaultValue) {
diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl
index 0ce9cfa..3e0deeb 100644
--- a/core/java/android/service/dreams/IDreamManager.aidl
+++ b/core/java/android/service/dreams/IDreamManager.aidl
@@ -41,4 +41,5 @@
void forceAmbientDisplayEnabled(boolean enabled);
ComponentName[] getDreamComponentsForUser(int userId);
void setDreamComponentsForUser(int userId, in ComponentName[] componentNames);
+ void registerDreamOverlayService(in ComponentName componentName);
}
diff --git a/core/java/android/service/dreams/IDreamOverlay.aidl b/core/java/android/service/dreams/IDreamOverlay.aidl
new file mode 100644
index 0000000..2b6633d
--- /dev/null
+++ b/core/java/android/service/dreams/IDreamOverlay.aidl
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.dreams;
+
+import android.service.dreams.IDreamOverlayCallback;
+import android.view.WindowManager.LayoutParams;
+
+/**
+* {@link IDreamOverlay} provides a way for a component to annotate a dream with additional view
+* elements. Registered through the DreamManager, a IDreamOverlay is bound to by the dream and
+* passed the necessary window details to participate in the user interface.
+
+* @hide
+*/
+interface IDreamOverlay {
+ /**
+ * @param params The {@link LayoutParams} for the associated DreamWindow, including the window
+ token of the Dream Activity.
+ * @param callback The {@link IDreamOverlayCallback} for requesting actions such as exiting the
+ * dream.
+ */
+ void startDream(in LayoutParams params, in IDreamOverlayCallback callback);
+}
diff --git a/core/java/android/service/dreams/IDreamOverlayCallback.aidl b/core/java/android/service/dreams/IDreamOverlayCallback.aidl
new file mode 100644
index 0000000..ec76a33
--- /dev/null
+++ b/core/java/android/service/dreams/IDreamOverlayCallback.aidl
@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.dreams;
+
+/**
+* {@link IDreamOverlayCallback} defines the interactions a dream overlay can have with its
+* associated dream. It is the discretion of the {@link DreamService}) to honor any inbound requests
+* from this callback.
+*
+* @hide
+*/
+interface IDreamOverlayCallback {
+ /**
+ * Invoked to request the dream exit.
+ */
+ void onExitRequested();
+}
\ No newline at end of file
diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
index e1c4305..2b79bbf 100644
--- a/core/java/android/view/VelocityTracker.java
+++ b/core/java/android/view/VelocityTracker.java
@@ -19,7 +19,7 @@
import android.annotation.IntDef;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
-import android.os.SystemProperties;
+import android.sysprop.InputProperties;
import android.util.ArrayMap;
import android.util.Pools.SynchronizedPool;
@@ -279,8 +279,7 @@
// If user has not selected a specific strategy
if (strategy == VELOCITY_TRACKER_STRATEGY_DEFAULT) {
// Check if user specified strategy by overriding system property.
- String strategyProperty =
- SystemProperties.get("persist.input.velocitytracker.strategy");
+ String strategyProperty = InputProperties.velocitytracker_strategy().orElse(null);
if (strategyProperty == null || strategyProperty.isEmpty()) {
mStrategy = strategy;
} else {
diff --git a/core/java/com/android/internal/os/ClassLoaderFactory.java b/core/java/com/android/internal/os/ClassLoaderFactory.java
index d347f2e..8b0411d 100644
--- a/core/java/com/android/internal/os/ClassLoaderFactory.java
+++ b/core/java/com/android/internal/os/ClassLoaderFactory.java
@@ -80,15 +80,20 @@
*/
public static ClassLoader createClassLoader(String dexPath,
String librarySearchPath, ClassLoader parent, String classloaderName,
- List<ClassLoader> sharedLibraries) {
+ List<ClassLoader> sharedLibraries, List<ClassLoader> sharedLibrariesLoadedAfter) {
ClassLoader[] arrayOfSharedLibraries = (sharedLibraries == null)
? null
: sharedLibraries.toArray(new ClassLoader[sharedLibraries.size()]);
+ ClassLoader[] arrayOfSharedLibrariesLoadedAfterApp = (sharedLibrariesLoadedAfter == null)
+ ? null
+ : sharedLibrariesLoadedAfter.toArray(
+ new ClassLoader[sharedLibrariesLoadedAfter.size()]);
if (isPathClassLoaderName(classloaderName)) {
- return new PathClassLoader(dexPath, librarySearchPath, parent, arrayOfSharedLibraries);
+ return new PathClassLoader(dexPath, librarySearchPath, parent, arrayOfSharedLibraries,
+ arrayOfSharedLibrariesLoadedAfterApp);
} else if (isDelegateLastClassLoaderName(classloaderName)) {
return new DelegateLastClassLoader(dexPath, librarySearchPath, parent,
- arrayOfSharedLibraries);
+ arrayOfSharedLibraries, arrayOfSharedLibrariesLoadedAfterApp);
}
throw new AssertionError("Invalid classLoaderName: " + classloaderName);
@@ -102,20 +107,20 @@
String librarySearchPath, String libraryPermittedPath, ClassLoader parent,
int targetSdkVersion, boolean isNamespaceShared, String classLoaderName) {
return createClassLoader(dexPath, librarySearchPath, libraryPermittedPath,
- parent, targetSdkVersion, isNamespaceShared, classLoaderName, null, null);
+ parent, targetSdkVersion, isNamespaceShared, classLoaderName, null, null, null);
}
-
/**
* Create a ClassLoader and initialize a linker-namespace for it.
*/
public static ClassLoader createClassLoader(String dexPath,
String librarySearchPath, String libraryPermittedPath, ClassLoader parent,
int targetSdkVersion, boolean isNamespaceShared, String classLoaderName,
- List<ClassLoader> sharedLibraries, List<String> nativeSharedLibraries) {
+ List<ClassLoader> sharedLibraries, List<String> nativeSharedLibraries,
+ List<ClassLoader> sharedLibrariesAfter) {
final ClassLoader classLoader = createClassLoader(dexPath, librarySearchPath, parent,
- classLoaderName, sharedLibraries);
+ classLoaderName, sharedLibraries, sharedLibrariesAfter);
String sonameList = "";
if (nativeSharedLibraries != null) {
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 59089a9..3248cf5 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -202,7 +202,6 @@
optional .com.android.server.wm.IdentifierProto resumed_activity = 24;
repeated TaskProto tasks = 25 [deprecated=true];
optional bool display_ready = 26;
-
optional WindowStateProto input_method_target = 27;
optional WindowStateProto input_method_input_target = 28;
optional WindowStateProto input_method_control_target = 29;
@@ -214,6 +213,9 @@
optional int32 ime_policy = 34;
repeated InsetsSourceProviderProto insets_source_providers = 35;
+ optional bool is_sleeping = 36;
+ repeated string sleep_tokens = 37;
+
}
/* represents DisplayArea object */
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 329f02a..b3a16d9 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5911,6 +5911,11 @@
<permission android:name="android.permission.READ_GLOBAL_APP_SEARCH_DATA"
android:protectionLevel="internal|role" />
+ <!-- @SystemApi Allows an application to create virtual devices in VirtualDeviceManager.
+ @hide -->
+ <permission android:name="android.permission.CREATE_VIRTUAL_DEVICE"
+ android:protectionLevel="internal|role" />
+
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 74ce8f2..e4b3991 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -5280,6 +5280,11 @@
to 0, the seconds hand will be disabled. -->
<integer name="config_defaultAnalogClockSecondsHandFps">1</integer>
+ <!-- List of shared library packages that should be loaded by the classloader after the
+ code and resources provided by applications. This value will be set by the manufacturer -->
+ <string-array name="config_sharedLibrariesLoadedAfterApp" translatable="false">
+ </string-array>
+
<!-- the number of the max cached processes in the system. -->
<integer name="config_customizedMaxCachedProcesses">32</integer>
diff --git a/core/res/res/values/dimens_car.xml b/core/res/res/values/dimens_car.xml
index 36f1edb..c5dddb8 100644
--- a/core/res/res/values/dimens_car.xml
+++ b/core/res/res/values/dimens_car.xml
@@ -17,7 +17,7 @@
-->
<resources>
<dimen name="car_large_avatar_size">96dp</dimen>
- <dimen name="car_large_avatar_badge_size">32dp</dimen>
+ <dimen name="car_large_avatar_badge_size">24dp</dimen>
<!-- Application Bar -->
<dimen name="car_app_bar_height">80dp</dimen>
<!-- Margin -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 846ec60..f166b73 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4559,6 +4559,10 @@
<java-symbol type="bool" name="config_volumeShowRemoteSessions" />
+ <!-- List of shared library packages that should be loaded by the classloader after the
+ code and resources provided by applications. -->
+ <java-symbol type="array" name="config_sharedLibrariesLoadedAfterApp" />
+
<java-symbol type="integer" name="config_customizedMaxCachedProcesses" />
<java-symbol type="color" name="overview_background"/>
diff --git a/core/tests/bluetoothtests/Android.bp b/core/tests/bluetoothtests/Android.bp
index a2e4dff..68416dd 100644
--- a/core/tests/bluetoothtests/Android.bp
+++ b/core/tests/bluetoothtests/Android.bp
@@ -15,7 +15,10 @@
"android.test.runner",
"android.test.base",
],
- static_libs: ["junit"],
+ static_libs: [
+ "junit",
+ "modules-utils-bytesmatcher",
+ ],
platform_apis: true,
certificate: "platform",
}
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java
index c287ea9..4e817d4 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/le/ScanRecordTest.java
@@ -16,11 +16,11 @@
package android.bluetooth.le;
-import android.os.BytesMatcher;
import android.os.ParcelUuid;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.internal.util.HexDump;
+import com.android.modules.utils.BytesMatcher;
import junit.framework.TestCase;
diff --git a/core/tests/coretests/src/android/os/BytesMatcherTest.java b/core/tests/coretests/src/android/os/BytesMatcherTest.java
deleted file mode 100644
index b28e309..0000000
--- a/core/tests/coretests/src/android/os/BytesMatcherTest.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.os;
-
-import static com.android.internal.util.HexDump.hexStringToByteArray;
-
-import android.bluetooth.BluetoothUuid;
-import android.net.MacAddress;
-
-import androidx.test.filters.SmallTest;
-
-import junit.framework.TestCase;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-@SmallTest
-public class BytesMatcherTest extends TestCase {
- @Test
- public void testEmpty() throws Exception {
- BytesMatcher matcher = BytesMatcher.decode("");
- assertFalse(matcher.test(hexStringToByteArray("cafe")));
- assertFalse(matcher.test(hexStringToByteArray("")));
- }
-
- @Test
- public void testExact() throws Exception {
- BytesMatcher matcher = BytesMatcher.decode("+cafe");
- assertTrue(matcher.test(hexStringToByteArray("cafe")));
- assertFalse(matcher.test(hexStringToByteArray("beef")));
- assertFalse(matcher.test(hexStringToByteArray("ca")));
- assertFalse(matcher.test(hexStringToByteArray("cafe00")));
- }
-
- @Test
- public void testMask() throws Exception {
- BytesMatcher matcher = BytesMatcher.decode("+cafe/ff00");
- assertTrue(matcher.test(hexStringToByteArray("cafe")));
- assertTrue(matcher.test(hexStringToByteArray("ca88")));
- assertFalse(matcher.test(hexStringToByteArray("beef")));
- assertFalse(matcher.test(hexStringToByteArray("ca")));
- assertFalse(matcher.test(hexStringToByteArray("cafe00")));
- }
-
- @Test
- public void testPrefix() throws Exception {
- BytesMatcher matcher = BytesMatcher.decode("⊆cafe,⊆beef/ff00");
- assertTrue(matcher.test(hexStringToByteArray("cafe")));
- assertFalse(matcher.test(hexStringToByteArray("caff")));
- assertTrue(matcher.test(hexStringToByteArray("cafecafe")));
- assertFalse(matcher.test(hexStringToByteArray("ca")));
- assertTrue(matcher.test(hexStringToByteArray("beef")));
- assertTrue(matcher.test(hexStringToByteArray("beff")));
- assertTrue(matcher.test(hexStringToByteArray("beffbeff")));
- assertFalse(matcher.test(hexStringToByteArray("be")));
- }
-
- @Test
- public void testMacAddress() throws Exception {
- BytesMatcher matcher = BytesMatcher.decode("+cafe00112233/ffffff000000");
- assertTrue(matcher.testMacAddress(
- MacAddress.fromString("ca:fe:00:00:00:00")));
- assertFalse(matcher.testMacAddress(
- MacAddress.fromString("f0:0d:00:00:00:00")));
- }
-
- @Test
- public void testBluetoothUuid() throws Exception {
- BytesMatcher matcher = BytesMatcher.decode("+cafe/ff00");
- assertTrue(matcher.testBluetoothUuid(
- BluetoothUuid.parseUuidFrom(hexStringToByteArray("cafe"))));
- assertFalse(matcher.testBluetoothUuid(
- BluetoothUuid.parseUuidFrom(hexStringToByteArray("beef"))));
- }
-
- /**
- * Verify that single matcher can be configured to match Bluetooth UUIDs of
- * varying lengths.
- */
- @Test
- public void testBluetoothUuid_Mixed() throws Exception {
- BytesMatcher matcher = BytesMatcher.decode("+aaaa/ff00,+bbbbbbbb/ffff0000");
- assertTrue(matcher.testBluetoothUuid(
- BluetoothUuid.parseUuidFrom(hexStringToByteArray("aaaa"))));
- assertFalse(matcher.testBluetoothUuid(
- BluetoothUuid.parseUuidFrom(hexStringToByteArray("bbbb"))));
- assertTrue(matcher.testBluetoothUuid(
- BluetoothUuid.parseUuidFrom(hexStringToByteArray("bbbbbbbb"))));
- assertFalse(matcher.testBluetoothUuid(
- BluetoothUuid.parseUuidFrom(hexStringToByteArray("aaaaaaaa"))));
- }
-
- @Test
- public void testSerialize_Empty() throws Exception {
- BytesMatcher matcher = new BytesMatcher();
- matcher = BytesMatcher.decode(BytesMatcher.encode(matcher));
-
- // Also very empty and null values
- BytesMatcher.decode("");
- BytesMatcher.decode(null);
- }
-
- @Test
- public void testSerialize_Exact() throws Exception {
- BytesMatcher matcher = new BytesMatcher();
- matcher.addExactRejectRule(hexStringToByteArray("cafe00112233"),
- hexStringToByteArray("ffffff000000"));
- matcher.addExactRejectRule(hexStringToByteArray("beef00112233"),
- null);
- matcher.addExactAcceptRule(hexStringToByteArray("000000000000"),
- hexStringToByteArray("000000000000"));
-
- assertFalse(matcher.test(hexStringToByteArray("cafe00ffffff")));
- assertFalse(matcher.test(hexStringToByteArray("beef00112233")));
- assertTrue(matcher.test(hexStringToByteArray("beef00ffffff")));
-
- // Bounce through serialization pass and confirm it still works
- matcher = BytesMatcher.decode(BytesMatcher.encode(matcher));
-
- assertFalse(matcher.test(hexStringToByteArray("cafe00ffffff")));
- assertFalse(matcher.test(hexStringToByteArray("beef00112233")));
- assertTrue(matcher.test(hexStringToByteArray("beef00ffffff")));
- }
-
- @Test
- public void testSerialize_Prefix() throws Exception {
- BytesMatcher matcher = new BytesMatcher();
- matcher.addExactRejectRule(hexStringToByteArray("aa"), null);
- matcher.addExactAcceptRule(hexStringToByteArray("bb"), null);
- matcher.addPrefixAcceptRule(hexStringToByteArray("aa"), null);
- matcher.addPrefixRejectRule(hexStringToByteArray("bb"), null);
-
- assertFalse(matcher.test(hexStringToByteArray("aa")));
- assertTrue(matcher.test(hexStringToByteArray("bb")));
- assertTrue(matcher.test(hexStringToByteArray("aaaa")));
- assertFalse(matcher.test(hexStringToByteArray("bbbb")));
-
- // Bounce through serialization pass and confirm it still works
- matcher = BytesMatcher.decode(BytesMatcher.encode(matcher));
-
- assertFalse(matcher.test(hexStringToByteArray("aa")));
- assertTrue(matcher.test(hexStringToByteArray("bb")));
- assertTrue(matcher.test(hexStringToByteArray("aaaa")));
- assertFalse(matcher.test(hexStringToByteArray("bbbb")));
- }
-
- @Test
- public void testOrdering_RejectFirst() throws Exception {
- BytesMatcher matcher = BytesMatcher.decode("-ff/0f,+ff/f0");
- assertFalse(matcher.test(hexStringToByteArray("ff")));
- assertTrue(matcher.test(hexStringToByteArray("f0")));
- assertFalse(matcher.test(hexStringToByteArray("0f")));
- }
-
- @Test
- public void testOrdering_AcceptFirst() throws Exception {
- BytesMatcher matcher = BytesMatcher.decode("+ff/f0,-ff/0f");
- assertTrue(matcher.test(hexStringToByteArray("ff")));
- assertTrue(matcher.test(hexStringToByteArray("f0")));
- assertFalse(matcher.test(hexStringToByteArray("0f")));
- }
-}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 6e92755..8b37805 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1561,6 +1561,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/DisplayRotation.java"
},
+ "-436553282": {
+ "message": "Remove sleep token: tag=%s, displayId=%d",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/RootWindowContainer.java"
+ },
"-415865166": {
"message": "findFocusedWindow: Found new focus @ %s",
"level": "VERBOSE",
@@ -1669,6 +1675,12 @@
"group": "WM_DEBUG_ADD_REMOVE",
"at": "com\/android\/server\/wm\/WindowState.java"
},
+ "-317761482": {
+ "message": "Create sleep token: tag=%s, displayId=%d",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/RootWindowContainer.java"
+ },
"-317194205": {
"message": "clearLockedTasks: %s",
"level": "INFO",
diff --git a/libs/WindowManager/Shell/res/layout/split_decor.xml b/libs/WindowManager/Shell/res/layout/split_decor.xml
new file mode 100644
index 0000000..9ffa5e8
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/split_decor.xml
@@ -0,0 +1,30 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent">
+
+ <ImageView android:id="@+id/split_resizing_icon"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center"
+ android:padding="0dp"
+ android:visibility="gone"
+ android:background="@null"/>
+
+</FrameLayout>
diff --git a/libs/WindowManager/Shell/res/layout/split_outline.xml b/libs/WindowManager/Shell/res/layout/split_outline.xml
index 13a30f5..6cb9ebb 100644
--- a/libs/WindowManager/Shell/res/layout/split_outline.xml
+++ b/libs/WindowManager/Shell/res/layout/split_outline.xml
@@ -1,18 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2021 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
index 9113c79..e87b150 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
@@ -103,8 +103,6 @@
return runMoveToSideStage(args, pw);
case "removeFromSideStage":
return runRemoveFromSideStage(args, pw);
- case "setSideStageOutline":
- return runSetSideStageOutline(args, pw);
case "setSideStagePosition":
return runSetSideStagePosition(args, pw);
case "setSideStageVisibility":
@@ -163,18 +161,6 @@
return true;
}
- private boolean runSetSideStageOutline(String[] args, PrintWriter pw) {
- if (args.length < 3) {
- // First arguments are "WMShell" and command name.
- pw.println("Error: whether to enable or disable side stage outline border should be"
- + " provided as arguments");
- return false;
- }
- final boolean enable = new Boolean(args[2]);
- mSplitScreenOptional.ifPresent(split -> split.setSideStageOutline(enable));
- return true;
- }
-
private boolean runSetSideStagePosition(String[] args, PrintWriter pw) {
if (args.length < 3) {
// First arguments are "WMShell" and command name.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
index 6a252e0..c8449a3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
@@ -131,7 +131,8 @@
mSplitLayout = new SplitLayout(TAG + "SplitDivider",
mDisplayController.getDisplayContext(mRootTaskInfo.displayId),
mRootTaskInfo.configuration, this /* layoutChangeListener */,
- mParentContainerCallbacks, mDisplayImeController, mController.getTaskOrganizer());
+ mParentContainerCallbacks, mDisplayImeController, mController.getTaskOrganizer(),
+ true /* applyDismissingParallax */);
mDisplayInsetsController.addInsetsChangedListener(mRootTaskInfo.displayId, mSplitLayout);
final WindowContainerToken token1 = task1.token;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackPreviewHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackPreviewHandler.java
new file mode 100644
index 0000000..dc20f7b
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackPreviewHandler.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.back;
+
+import android.os.SystemProperties;
+import android.view.IWindowManager;
+
+import javax.inject.Inject;
+
+/**
+ * Handle the preview of what a back gesture will lead to.
+ */
+public class BackPreviewHandler {
+
+ private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability";
+
+ public static boolean isEnabled() {
+ return SystemProperties.getInt(BACK_PREDICTABILITY_PROP, 0) > 0;
+ }
+
+ private final IWindowManager mWmService;
+
+ @Inject
+ public BackPreviewHandler(IWindowManager windowManagerService) {
+ mWmService = windowManagerService;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SurfaceUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SurfaceUtils.java
index 55c5125..4b138e4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SurfaceUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SurfaceUtils.java
@@ -23,16 +23,22 @@
* Helpers for handling surface.
*/
public class SurfaceUtils {
- /** Creates a dim layer above indicated host surface. */
+ /** Creates a dim layer above host surface. */
public static SurfaceControl makeDimLayer(SurfaceControl.Transaction t, SurfaceControl host,
String name, SurfaceSession surfaceSession) {
- SurfaceControl dimLayer = new SurfaceControl.Builder(surfaceSession)
+ final SurfaceControl dimLayer = makeColorLayer(host, name, surfaceSession);
+ t.setLayer(dimLayer, Integer.MAX_VALUE).setColor(dimLayer, new float[]{0f, 0f, 0f});
+ return dimLayer;
+ }
+
+ /** Creates a color layer for host surface. */
+ public static SurfaceControl makeColorLayer(SurfaceControl host, String name,
+ SurfaceSession surfaceSession) {
+ return new SurfaceControl.Builder(surfaceSession)
.setParent(host)
.setColorLayer()
.setName(name)
- .setCallsite("SurfaceUtils.makeDimLayer")
+ .setCallsite("SurfaceUtils.makeColorLayer")
.build();
- t.setLayer(dimLayer, Integer.MAX_VALUE).setColor(dimLayer, new float[]{0f, 0f, 0f});
- return dimLayer;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
new file mode 100644
index 0000000..ad9ebb2
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.common.split;
+
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import static android.view.WindowManagerPolicyConstants.SPLIT_DIVIDER_LAYER;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Binder;
+import android.view.IWindow;
+import android.view.LayoutInflater;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.SurfaceSession;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowlessWindowManager;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import com.android.launcher3.icons.IconProvider;
+import com.android.wm.shell.R;
+import com.android.wm.shell.common.SurfaceUtils;
+
+/**
+ * Handles split decor like showing resizing hint for a specific split.
+ */
+public class SplitDecorManager extends WindowlessWindowManager {
+ private static final String TAG = SplitDecorManager.class.getSimpleName();
+ private static final String RESIZING_BACKGROUND_SURFACE_NAME = "ResizingBackground";
+
+ private final IconProvider mIconProvider;
+ private final SurfaceSession mSurfaceSession;
+
+ private Drawable mIcon;
+ private ImageView mResizingIconView;
+ private SurfaceControlViewHost mViewHost;
+ private SurfaceControl mHostLeash;
+ private SurfaceControl mIconLeash;
+ private SurfaceControl mBackgroundLeash;
+
+ public SplitDecorManager(Configuration configuration, IconProvider iconProvider,
+ SurfaceSession surfaceSession) {
+ super(configuration, null /* rootSurface */, null /* hostInputToken */);
+ mIconProvider = iconProvider;
+ mSurfaceSession = surfaceSession;
+ }
+
+ @Override
+ protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
+ // Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later.
+ final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
+ .setContainerLayer()
+ .setName(TAG)
+ .setHidden(true)
+ .setParent(mHostLeash)
+ .setCallsite("SplitDecorManager#attachToParentSurface");
+ mIconLeash = builder.build();
+ b.setParent(mIconLeash);
+ }
+
+ /** Inflates split decor surface on the root surface. */
+ public void inflate(Context context, SurfaceControl rootLeash, Rect rootBounds) {
+ if (mIconLeash != null && mViewHost != null) {
+ return;
+ }
+
+ context = context.createWindowContext(context.getDisplay(), TYPE_APPLICATION_OVERLAY,
+ null /* options */);
+ mHostLeash = rootLeash;
+ mViewHost = new SurfaceControlViewHost(context, context.getDisplay(), this);
+
+ final FrameLayout rootLayout = (FrameLayout) LayoutInflater.from(context)
+ .inflate(R.layout.split_decor, null);
+ mResizingIconView = rootLayout.findViewById(R.id.split_resizing_icon);
+
+ final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ 0 /* width */, 0 /* height */, TYPE_APPLICATION_OVERLAY,
+ FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT);
+ lp.width = rootBounds.width();
+ lp.height = rootBounds.height();
+ lp.token = new Binder();
+ lp.setTitle(TAG);
+ lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
+ // TODO(b/189839391): Set INPUT_FEATURE_NO_INPUT_CHANNEL after WM supports
+ // TRUSTED_OVERLAY for windowless window without input channel.
+ mViewHost.setView(rootLayout, lp);
+ }
+
+ /** Releases the surfaces for split decor. */
+ public void release(SurfaceControl.Transaction t) {
+ if (mViewHost != null) {
+ mViewHost.release();
+ mViewHost = null;
+ }
+ if (mIconLeash != null) {
+ t.remove(mIconLeash);
+ mIconLeash = null;
+ }
+ if (mBackgroundLeash != null) {
+ t.remove(mBackgroundLeash);
+ mBackgroundLeash = null;
+ }
+ mHostLeash = null;
+ mIcon = null;
+ mResizingIconView = null;
+ }
+
+ /** Showing resizing hint. */
+ public void onResizing(ActivityManager.RunningTaskInfo resizingTask, Rect newBounds,
+ SurfaceControl.Transaction t) {
+ if (mResizingIconView == null) {
+ return;
+ }
+
+ if (mIcon == null) {
+ // TODO: add fade-in animation.
+ mBackgroundLeash = SurfaceUtils.makeColorLayer(mHostLeash,
+ RESIZING_BACKGROUND_SURFACE_NAME, mSurfaceSession);
+ t.setColor(mBackgroundLeash, getResizingBackgroundColor(resizingTask))
+ .setLayer(mBackgroundLeash, SPLIT_DIVIDER_LAYER - 1)
+ .show(mBackgroundLeash);
+
+ mIcon = mIconProvider.getIcon(resizingTask.topActivityInfo);
+ mResizingIconView.setImageDrawable(mIcon);
+ mResizingIconView.setVisibility(View.VISIBLE);
+
+ WindowManager.LayoutParams lp =
+ (WindowManager.LayoutParams) mViewHost.getView().getLayoutParams();
+ lp.width = mIcon.getIntrinsicWidth();
+ lp.height = mIcon.getIntrinsicHeight();
+ mViewHost.relayout(lp);
+ t.show(mIconLeash).setLayer(mIconLeash, SPLIT_DIVIDER_LAYER);
+ }
+
+ t.setPosition(mIconLeash,
+ newBounds.width() / 2 - mIcon.getIntrinsicWidth() / 2,
+ newBounds.height() / 2 - mIcon.getIntrinsicWidth() / 2);
+ }
+
+ /** Stops showing resizing hint. */
+ public void onResized(Rect newBounds, SurfaceControl.Transaction t) {
+ if (mResizingIconView == null) {
+ return;
+ }
+
+ if (mIcon != null) {
+ mResizingIconView.setVisibility(View.GONE);
+ mResizingIconView.setImageDrawable(null);
+ t.remove(mBackgroundLeash).hide(mIconLeash);
+ mIcon = null;
+ mBackgroundLeash = null;
+ }
+ }
+
+ private static float[] getResizingBackgroundColor(ActivityManager.RunningTaskInfo taskInfo) {
+ final int taskBgColor = taskInfo.taskDescription.getBackgroundColor();
+ return Color.valueOf(taskBgColor == -1 ? Color.WHITE : taskBgColor).getComponents();
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index b1b0382..a9ed64c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -103,7 +103,7 @@
private final SplitWindowManager mSplitWindowManager;
private final DisplayImeController mDisplayImeController;
private final ImePositionProcessor mImePositionProcessor;
- private final DismissingParallaxPolicy mDismissingParallaxPolicy;
+ private final DismissingEffectPolicy mDismissingEffectPolicy;
private final ShellTaskOrganizer mTaskOrganizer;
private final InsetsState mInsetsState = new InsetsState();
@@ -119,7 +119,8 @@
public SplitLayout(String windowName, Context context, Configuration configuration,
SplitLayoutHandler splitLayoutHandler,
SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks,
- DisplayImeController displayImeController, ShellTaskOrganizer taskOrganizer) {
+ DisplayImeController displayImeController, ShellTaskOrganizer taskOrganizer,
+ boolean applyDismissingParallax) {
mContext = context.createConfigurationContext(configuration);
mOrientation = configuration.orientation;
mRotation = configuration.windowConfiguration.getRotation();
@@ -129,7 +130,7 @@
parentContainerCallbacks);
mTaskOrganizer = taskOrganizer;
mImePositionProcessor = new ImePositionProcessor(mContext.getDisplayId());
- mDismissingParallaxPolicy = new DismissingParallaxPolicy();
+ mDismissingEffectPolicy = new DismissingEffectPolicy(applyDismissingParallax);
final Resources resources = context.getResources();
mDividerWindowWidth = resources.getDimensionPixelSize(
@@ -247,7 +248,7 @@
}
DockedDividerUtils.sanitizeStackBounds(mBounds1, true /** topLeft */);
DockedDividerUtils.sanitizeStackBounds(mBounds2, false /** topLeft */);
- mDismissingParallaxPolicy.applyDividerPosition(position, isLandscape);
+ mDismissingEffectPolicy.applyDividerPosition(position, isLandscape);
}
/** Inflates {@link DividerView} on the root surface. */
@@ -290,7 +291,6 @@
*/
void updateDivideBounds(int position) {
updateBounds(position);
- mSplitWindowManager.setResizingSplits(true);
mSplitLayoutHandler.onLayoutSizeChanging(this);
}
@@ -298,13 +298,11 @@
mDividePosition = position;
updateBounds(mDividePosition);
mSplitLayoutHandler.onLayoutSizeChanged(this);
- mSplitWindowManager.setResizingSplits(false);
}
/** Resets divider position. */
public void resetDividerPosition() {
mDividePosition = mDividerSnapAlgorithm.getMiddleTarget().position;
- mSplitWindowManager.setResizingSplits(false);
updateBounds(mDividePosition);
mWinToken1 = null;
mWinToken2 = null;
@@ -360,8 +358,8 @@
@VisibleForTesting
void flingDividePosition(int from, int to, @Nullable Runnable flingFinishedCallback) {
if (from == to) {
- // No animation run, it should stop resizing here.
- mSplitWindowManager.setResizingSplits(false);
+ // No animation run, still callback to stop resizing.
+ mSplitLayoutHandler.onLayoutSizeChanged(this);
return;
}
ValueAnimator animator = ValueAnimator
@@ -425,7 +423,7 @@
return;
}
- mDismissingParallaxPolicy.adjustDismissingSurface(t, leash1, leash2, dimLayer1, dimLayer2);
+ mDismissingEffectPolicy.adjustDismissingSurface(t, leash1, leash2, dimLayer1, dimLayer2);
}
/** Apply recorded task layout to the {@link WindowContainerTransaction}. */
@@ -543,7 +541,10 @@
* Calculates and applies proper dismissing parallax offset and dimming value to hint users
* dismissing gesture.
*/
- private class DismissingParallaxPolicy {
+ private class DismissingEffectPolicy {
+ /** Indicates whether to offset splitting bounds to hint dismissing progress or not. */
+ private final boolean mApplyParallax;
+
// The current dismissing side.
int mDismissingSide = DOCKED_INVALID;
@@ -553,6 +554,10 @@
// The dimming value to hint the dismissing side and progress.
float mDismissingDimValue = 0.0f;
+ DismissingEffectPolicy(boolean applyDismissingParallax) {
+ mApplyParallax = applyDismissingParallax;
+ }
+
/**
* Applies a parallax to the task to hint dismissing progress.
*
@@ -627,12 +632,14 @@
return false;
}
- t.setPosition(targetLeash,
- mTempRect.left + mDismissingParallaxOffset.x,
- mTempRect.top + mDismissingParallaxOffset.y);
- // Transform the screen-based split bounds to surface-based crop bounds.
- mTempRect.offsetTo(-mDismissingParallaxOffset.x, -mDismissingParallaxOffset.y);
- t.setWindowCrop(targetLeash, mTempRect);
+ if (mApplyParallax) {
+ t.setPosition(targetLeash,
+ mTempRect.left + mDismissingParallaxOffset.x,
+ mTempRect.top + mDismissingParallaxOffset.y);
+ // Transform the screen-based split bounds to surface-based crop bounds.
+ mTempRect.offsetTo(-mDismissingParallaxOffset.x, -mDismissingParallaxOffset.y);
+ t.setWindowCrop(targetLeash, mTempRect);
+ }
t.setAlpha(targetDimLayer, mDismissingDimValue)
.setVisibility(targetDimLayer, mDismissingDimValue > 0.001f);
return true;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
index 47dceb3..08754d3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
@@ -25,7 +25,6 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
-import android.app.ActivityTaskManager;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
@@ -33,8 +32,6 @@
import android.graphics.Region;
import android.os.Binder;
import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Slog;
import android.view.IWindow;
import android.view.InsetsState;
import android.view.LayoutInflater;
@@ -59,7 +56,6 @@
private Context mContext;
private SurfaceControlViewHost mViewHost;
private SurfaceControl mLeash;
- private boolean mResizingSplits;
private DividerView mDividerView;
public interface ParentContainerCallbacks {
@@ -154,16 +150,6 @@
mDividerView.setInteractive(interactive);
}
- void setResizingSplits(boolean resizing) {
- if (resizing == mResizingSplits) return;
- try {
- ActivityTaskManager.getService().setSplitScreenResizing(resizing);
- mResizingSplits = resizing;
- } catch (RemoteException e) {
- Slog.w(TAG, "Error calling setSplitScreenResizing", e);
- }
- }
-
/**
* Gets {@link SurfaceControl} of the surface holding divider view. @return {@code null} if not
* feasible.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
index a47a152..6440ef0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
@@ -19,11 +19,13 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import android.annotation.Nullable;
+import android.content.Context;
import android.graphics.Rect;
import android.view.SurfaceSession;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
+import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -37,11 +39,11 @@
private boolean mIsActive = false;
- MainStage(ShellTaskOrganizer taskOrganizer, int displayId,
+ MainStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
- SurfaceSession surfaceSession,
+ SurfaceSession surfaceSession, IconProvider iconProvider,
@Nullable StageTaskUnfoldController stageTaskUnfoldController) {
- super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession,
+ super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession, iconProvider,
stageTaskUnfoldController);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java
deleted file mode 100644
index a459c8d..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.splitscreen;
-
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.os.Binder;
-import android.view.IWindow;
-import android.view.InsetsSource;
-import android.view.InsetsState;
-import android.view.LayoutInflater;
-import android.view.SurfaceControl;
-import android.view.SurfaceControlViewHost;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.view.WindowlessWindowManager;
-import android.widget.FrameLayout;
-
-import com.android.wm.shell.R;
-
-/**
- * Handles drawing outline of the bounds of provided root surface. The outline will be drown with
- * the consideration of display insets like status bar, navigation bar and display cutout.
- */
-class OutlineManager extends WindowlessWindowManager {
- private static final String WINDOW_NAME = "SplitOutlineLayer";
- private final Context mContext;
- private final Rect mRootBounds = new Rect();
- private final Rect mTempRect = new Rect();
- private final Rect mLastOutlineBounds = new Rect();
- private final InsetsState mInsetsState = new InsetsState();
- private final int mExpandedTaskBarHeight;
- private OutlineView mOutlineView;
- private SurfaceControlViewHost mViewHost;
- private SurfaceControl mHostLeash;
- private SurfaceControl mLeash;
-
- OutlineManager(Context context, Configuration configuration) {
- super(configuration, null /* rootSurface */, null /* hostInputToken */);
- mContext = context.createWindowContext(context.getDisplay(), TYPE_APPLICATION_OVERLAY,
- null /* options */);
- mExpandedTaskBarHeight = mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.taskbar_frame_height);
- }
-
- @Override
- protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
- b.setParent(mHostLeash);
- }
-
- void inflate(SurfaceControl rootLeash, Rect rootBounds) {
- if (mLeash != null || mViewHost != null) return;
-
- mHostLeash = rootLeash;
- mRootBounds.set(rootBounds);
- mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
-
- final FrameLayout rootLayout = (FrameLayout) LayoutInflater.from(mContext)
- .inflate(R.layout.split_outline, null);
- mOutlineView = rootLayout.findViewById(R.id.split_outline);
-
- final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- 0 /* width */, 0 /* height */, TYPE_APPLICATION_OVERLAY,
- FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT);
- lp.width = mRootBounds.width();
- lp.height = mRootBounds.height();
- lp.token = new Binder();
- lp.setTitle(WINDOW_NAME);
- lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
- // TODO(b/189839391): Set INPUT_FEATURE_NO_INPUT_CHANNEL after WM supports
- // TRUSTED_OVERLAY for windowless window without input channel.
- mViewHost.setView(rootLayout, lp);
- mLeash = getSurfaceControl(mViewHost.getWindowToken());
-
- drawOutline();
- }
-
- void release() {
- if (mViewHost != null) {
- mViewHost.release();
- mViewHost = null;
- }
- mRootBounds.setEmpty();
- mLastOutlineBounds.setEmpty();
- mOutlineView = null;
- mHostLeash = null;
- mLeash = null;
- }
-
- @Nullable
- SurfaceControl getOutlineLeash() {
- return mLeash;
- }
-
- void setVisibility(boolean visible) {
- if (mOutlineView != null) {
- mOutlineView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
- }
- }
-
- void setRootBounds(Rect rootBounds) {
- if (mViewHost == null || mViewHost.getView() == null) {
- return;
- }
-
- if (!mRootBounds.equals(rootBounds)) {
- WindowManager.LayoutParams lp =
- (WindowManager.LayoutParams) mViewHost.getView().getLayoutParams();
- lp.width = rootBounds.width();
- lp.height = rootBounds.height();
- mViewHost.relayout(lp);
- mRootBounds.set(rootBounds);
- drawOutline();
- }
- }
-
- void onInsetsChanged(InsetsState insetsState) {
- if (!mInsetsState.equals(insetsState)) {
- mInsetsState.set(insetsState);
- drawOutline();
- }
- }
-
- private void computeOutlineBounds(Rect rootBounds, InsetsState insetsState, Rect outBounds) {
- outBounds.set(rootBounds);
- final InsetsSource taskBarInsetsSource =
- insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
- // Only insets the divider bar with task bar when it's expanded so that the rounded corners
- // will be drawn against task bar.
- if (taskBarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) {
- outBounds.inset(taskBarInsetsSource.calculateVisibleInsets(outBounds));
- }
-
- // Offset the coordinate from screen based to surface based.
- outBounds.offset(-rootBounds.left, -rootBounds.top);
- }
-
- void drawOutline() {
- if (mOutlineView == null) {
- return;
- }
-
- computeOutlineBounds(mRootBounds, mInsetsState, mTempRect);
- if (mTempRect.equals(mLastOutlineBounds)) {
- return;
- }
-
- ViewGroup.MarginLayoutParams lp =
- (ViewGroup.MarginLayoutParams) mOutlineView.getLayoutParams();
- lp.leftMargin = mTempRect.left;
- lp.topMargin = mTempRect.top;
- lp.width = mTempRect.width();
- lp.height = mTempRect.height();
- mOutlineView.setLayoutParams(lp);
- mLastOutlineBounds.set(mTempRect);
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineView.java
deleted file mode 100644
index 94dd9b2..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineView.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.splitscreen;
-
-import static android.view.RoundedCorner.POSITION_BOTTOM_LEFT;
-import static android.view.RoundedCorner.POSITION_BOTTOM_RIGHT;
-import static android.view.RoundedCorner.POSITION_TOP_LEFT;
-import static android.view.RoundedCorner.POSITION_TOP_RIGHT;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.util.AttributeSet;
-import android.view.RoundedCorner;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.internal.R;
-
-/** View for drawing split outline. */
-public class OutlineView extends View {
- private final Paint mPaint = new Paint();
- private final Path mPath = new Path();
- private final float[] mRadii = new float[8];
-
- public OutlineView(@NonNull Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
- mPaint.setStyle(Paint.Style.STROKE);
- mPaint.setStrokeWidth(
- getResources().getDimension(R.dimen.accessibility_focus_highlight_stroke_width));
- mPaint.setColor(getResources().getColor(R.color.system_accent1_100, null));
- }
-
- @Override
- protected void onAttachedToWindow() {
- // TODO(b/200850654): match the screen corners with the actual display decor.
- mRadii[0] = mRadii[1] = getCornerRadius(POSITION_TOP_LEFT);
- mRadii[2] = mRadii[3] = getCornerRadius(POSITION_TOP_RIGHT);
- mRadii[4] = mRadii[5] = getCornerRadius(POSITION_BOTTOM_RIGHT);
- mRadii[6] = mRadii[7] = getCornerRadius(POSITION_BOTTOM_LEFT);
- }
-
- private int getCornerRadius(@RoundedCorner.Position int position) {
- final RoundedCorner roundedCorner = getDisplay().getRoundedCorner(position);
- return roundedCorner == null ? 0 : roundedCorner.getRadius();
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- if (changed) {
- mPath.reset();
- mPath.addRoundRect(0, 0, getWidth(), getHeight(), mRadii, Path.Direction.CW);
- }
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- canvas.drawPath(mPath, mPaint);
- }
-
- @Override
- public boolean hasOverlappingRendering() {
- return false;
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
index dc8fb9f..51104e4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
@@ -16,20 +16,16 @@
package com.android.wm.shell.splitscreen;
-import android.annotation.CallSuper;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Rect;
-import android.view.InsetsSourceControl;
-import android.view.InsetsState;
-import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
+import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.SyncTransactionQueue;
/**
@@ -38,19 +34,15 @@
*
* @see StageCoordinator
*/
-class SideStage extends StageTaskListener implements
- DisplayInsetsController.OnInsetsChangedListener {
+class SideStage extends StageTaskListener {
private static final String TAG = SideStage.class.getSimpleName();
- private final Context mContext;
- private OutlineManager mOutlineManager;
SideStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
- SurfaceSession surfaceSession,
+ SurfaceSession surfaceSession, IconProvider iconProvider,
@Nullable StageTaskUnfoldController stageTaskUnfoldController) {
- super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession,
+ super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession, iconProvider,
stageTaskUnfoldController);
- mContext = context;
}
void addTask(ActivityManager.RunningTaskInfo task, Rect rootBounds,
@@ -83,62 +75,4 @@
wct.reparent(task.token, newParent, false /* onTop */);
return true;
}
-
- @Nullable
- public SurfaceControl getOutlineLeash() {
- return mOutlineManager.getOutlineLeash();
- }
-
- @Override
- @CallSuper
- public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
- super.onTaskAppeared(taskInfo, leash);
- if (isRootTask(taskInfo)) {
- mOutlineManager = new OutlineManager(mContext, taskInfo.configuration);
- enableOutline(true);
- }
- }
-
- @Override
- @CallSuper
- public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
- super.onTaskInfoChanged(taskInfo);
- if (isRootTask(taskInfo)) {
- mOutlineManager.setRootBounds(taskInfo.configuration.windowConfiguration.getBounds());
- }
- }
-
- private boolean isRootTask(ActivityManager.RunningTaskInfo taskInfo) {
- return mRootTaskInfo != null && mRootTaskInfo.taskId == taskInfo.taskId;
- }
-
- void enableOutline(boolean enable) {
- if (mOutlineManager == null) {
- return;
- }
-
- if (enable) {
- if (mRootTaskInfo != null) {
- mOutlineManager.inflate(mRootLeash,
- mRootTaskInfo.configuration.windowConfiguration.getBounds());
- }
- } else {
- mOutlineManager.release();
- }
- }
-
- void setOutlineVisibility(boolean visible) {
- mOutlineManager.setVisibility(visible);
- }
-
- @Override
- public void insetsChanged(InsetsState insetsState) {
- mOutlineManager.onInsetsChanged(insetsState);
- }
-
- @Override
- public void insetsControlChanged(InsetsState insetsState,
- InsetsSourceControl[] activeControls) {
- insetsChanged(insetsState);
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitDecorManager.java
new file mode 100644
index 0000000..8e5cc6d
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitDecorManager.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.splitscreen;
+
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import static android.view.WindowManagerPolicyConstants.SPLIT_DIVIDER_LAYER;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Binder;
+import android.view.IWindow;
+import android.view.LayoutInflater;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.SurfaceSession;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowlessWindowManager;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import com.android.launcher3.icons.IconProvider;
+import com.android.wm.shell.R;
+import com.android.wm.shell.common.SurfaceUtils;
+
+/**
+ * Handles split decor like showing resizing hint for a specific split.
+ */
+class SplitDecorManager extends WindowlessWindowManager {
+ private static final String TAG = SplitDecorManager.class.getSimpleName();
+ private static final String RESIZING_BACKGROUND_SURFACE_NAME = "ResizingBackground";
+
+ private final IconProvider mIconProvider;
+ private final SurfaceSession mSurfaceSession;
+
+ private Drawable mIcon;
+ private ImageView mResizingIconView;
+ private SurfaceControlViewHost mViewHost;
+ private SurfaceControl mHostLeash;
+ private SurfaceControl mIconLeash;
+ private SurfaceControl mBackgroundLeash;
+
+ SplitDecorManager(Configuration configuration, IconProvider iconProvider,
+ SurfaceSession surfaceSession) {
+ super(configuration, null /* rootSurface */, null /* hostInputToken */);
+ mIconProvider = iconProvider;
+ mSurfaceSession = surfaceSession;
+ }
+
+ @Override
+ protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
+ // Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later.
+ final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
+ .setContainerLayer()
+ .setName(TAG)
+ .setHidden(true)
+ .setParent(mHostLeash)
+ .setCallsite("SplitDecorManager#attachToParentSurface");
+ mIconLeash = builder.build();
+ b.setParent(mIconLeash);
+ }
+
+ void inflate(Context context, SurfaceControl rootLeash, Rect rootBounds) {
+ if (mIconLeash != null && mViewHost != null) {
+ return;
+ }
+
+ context = context.createWindowContext(context.getDisplay(), TYPE_APPLICATION_OVERLAY,
+ null /* options */);
+ mHostLeash = rootLeash;
+ mViewHost = new SurfaceControlViewHost(context, context.getDisplay(), this);
+
+ final FrameLayout rootLayout = (FrameLayout) LayoutInflater.from(context)
+ .inflate(R.layout.split_decor, null);
+ mResizingIconView = rootLayout.findViewById(R.id.split_resizing_icon);
+
+ final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ 0 /* width */, 0 /* height */, TYPE_APPLICATION_OVERLAY,
+ FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT);
+ lp.width = rootBounds.width();
+ lp.height = rootBounds.height();
+ lp.token = new Binder();
+ lp.setTitle(TAG);
+ lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
+ // TODO(b/189839391): Set INPUT_FEATURE_NO_INPUT_CHANNEL after WM supports
+ // TRUSTED_OVERLAY for windowless window without input channel.
+ mViewHost.setView(rootLayout, lp);
+ }
+
+ void release(SurfaceControl.Transaction t) {
+ if (mViewHost != null) {
+ mViewHost.release();
+ mViewHost = null;
+ }
+ if (mIconLeash != null) {
+ t.remove(mIconLeash);
+ mIconLeash = null;
+ }
+ if (mBackgroundLeash != null) {
+ t.remove(mBackgroundLeash);
+ mBackgroundLeash = null;
+ }
+ mHostLeash = null;
+ mIcon = null;
+ mResizingIconView = null;
+ }
+
+ /** Showing resizing hint. */
+ void onResizing(ActivityManager.RunningTaskInfo resizingTask, Rect newBounds,
+ SurfaceControl.Transaction t) {
+ if (mResizingIconView == null) {
+ return;
+ }
+
+ if (mIcon == null) {
+ // TODO: add fade-in animation.
+ mBackgroundLeash = SurfaceUtils.makeColorLayer(mHostLeash,
+ RESIZING_BACKGROUND_SURFACE_NAME, mSurfaceSession);
+ t.setColor(mBackgroundLeash, getResizingBackgroundColor(resizingTask))
+ .setLayer(mBackgroundLeash, SPLIT_DIVIDER_LAYER - 1)
+ .show(mBackgroundLeash);
+
+ mIcon = mIconProvider.getIcon(resizingTask.topActivityInfo);
+ mResizingIconView.setImageDrawable(mIcon);
+ mResizingIconView.setVisibility(View.VISIBLE);
+
+ WindowManager.LayoutParams lp =
+ (WindowManager.LayoutParams) mViewHost.getView().getLayoutParams();
+ lp.width = mIcon.getIntrinsicWidth();
+ lp.height = mIcon.getIntrinsicHeight();
+ mViewHost.relayout(lp);
+ t.show(mIconLeash).setLayer(mIconLeash, SPLIT_DIVIDER_LAYER);
+ }
+
+ t.setPosition(mIconLeash,
+ newBounds.width() / 2 - mIcon.getIntrinsicWidth() / 2,
+ newBounds.height() / 2 - mIcon.getIntrinsicWidth() / 2);
+ }
+
+ /** Stops showing resizing hint. */
+ void onResized(Rect newBounds, SurfaceControl.Transaction t) {
+ if (mResizingIconView == null) {
+ return;
+ }
+
+ if (mIcon != null) {
+ mResizingIconView.setVisibility(View.GONE);
+ mResizingIconView.setImageDrawable(null);
+ t.remove(mBackgroundLeash).hide(mIconLeash);
+ mIcon = null;
+ mBackgroundLeash = null;
+ }
+ }
+
+ private static float[] getResizingBackgroundColor(ActivityManager.RunningTaskInfo taskInfo) {
+ final int taskBgColor = taskInfo.taskDescription.getBackgroundColor();
+ return Color.valueOf(taskBgColor == -1 ? Color.WHITE : taskBgColor).getComponents();
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 3b75bfb..36f1406 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -54,6 +54,7 @@
import com.android.internal.logging.InstanceId;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayImeController;
@@ -78,6 +79,7 @@
/**
* Class manages split-screen multitasking mode and implements the main interface
* {@link SplitScreen}.
+ *
* @see StageCoordinator
*/
// TODO(b/198577848): Implement split screen flicker test to consolidate CUJ of split screen.
@@ -96,6 +98,7 @@
private final Transitions mTransitions;
private final TransactionPool mTransactionPool;
private final SplitscreenEventLogger mLogger;
+ private final IconProvider mIconProvider;
private final Provider<Optional<StageTaskUnfoldController>> mUnfoldControllerProvider;
private StageCoordinator mStageCoordinator;
@@ -105,7 +108,7 @@
RootTaskDisplayAreaOrganizer rootTDAOrganizer,
ShellExecutor mainExecutor, DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController,
- Transitions transitions, TransactionPool transactionPool,
+ Transitions transitions, TransactionPool transactionPool, IconProvider iconProvider,
Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
mTaskOrganizer = shellTaskOrganizer;
mSyncQueue = syncQueue;
@@ -118,6 +121,7 @@
mTransactionPool = transactionPool;
mUnfoldControllerProvider = unfoldControllerProvider;
mLogger = new SplitscreenEventLogger();
+ mIconProvider = iconProvider;
}
public SplitScreen asSplitScreen() {
@@ -140,7 +144,7 @@
mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
mRootTDAOrganizer, mTaskOrganizer, mDisplayImeController,
mDisplayInsetsController, mTransitions, mTransactionPool, mLogger,
- mUnfoldControllerProvider);
+ mIconProvider, mUnfoldControllerProvider);
}
}
@@ -165,10 +169,6 @@
return mStageCoordinator.removeFromSideStage(taskId);
}
- public void setSideStageOutline(boolean enable) {
- mStageCoordinator.setSideStageOutline(enable);
- }
-
public void setSideStagePosition(@SplitPosition int sideStagePosition) {
mStageCoordinator.setSideStagePosition(sideStagePosition, null /* wct */);
}
@@ -319,9 +319,7 @@
}
transaction.apply();
transaction.close();
- return new RemoteAnimationTarget[]{
- mStageCoordinator.getDividerBarLegacyTarget(),
- mStageCoordinator.getOutlineLegacyTarget()};
+ return new RemoteAnimationTarget[]{mStageCoordinator.getDividerBarLegacyTarget()};
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 72d9880..3b62afc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -80,6 +80,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.InstanceId;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayImeController;
@@ -151,10 +152,12 @@
/** Whether the device is supporting legacy split or not. */
private boolean mUseLegacySplit;
- @SplitScreen.StageType private int mDismissTop = NO_DISMISS;
+ @SplitScreen.StageType
+ private int mDismissTop = NO_DISMISS;
/** The target stage to dismiss to when unlock after folded. */
- @SplitScreen.StageType private int mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
+ @SplitScreen.StageType
+ private int mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
private final Runnable mOnTransitionAnimationComplete = () -> {
// If still playing, let it finish.
@@ -169,22 +172,23 @@
private final SplitWindowManager.ParentContainerCallbacks mParentContainerCallbacks =
new SplitWindowManager.ParentContainerCallbacks() {
- @Override
- public void attachToParentSurface(SurfaceControl.Builder b) {
- mRootTDAOrganizer.attachToDisplayArea(mDisplayId, b);
- }
+ @Override
+ public void attachToParentSurface(SurfaceControl.Builder b) {
+ mRootTDAOrganizer.attachToDisplayArea(mDisplayId, b);
+ }
- @Override
- public void onLeashReady(SurfaceControl leash) {
- mSyncQueue.runInSync(t -> applyDividerVisibility(t));
- }
- };
+ @Override
+ public void onLeashReady(SurfaceControl leash) {
+ mSyncQueue.runInSync(t -> applyDividerVisibility(t));
+ }
+ };
StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, Transitions transitions,
TransactionPool transactionPool, SplitscreenEventLogger logger,
+ IconProvider iconProvider,
Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
mContext = context;
mDisplayId = displayId;
@@ -196,11 +200,13 @@
mSideUnfoldController = unfoldControllerProvider.get().orElse(null);
mMainStage = new MainStage(
+ mContext,
mTaskOrganizer,
mDisplayId,
mMainStageListener,
mSyncQueue,
mSurfaceSession,
+ iconProvider,
mMainUnfoldController);
mSideStage = new SideStage(
mContext,
@@ -209,10 +215,10 @@
mSideStageListener,
mSyncQueue,
mSurfaceSession,
+ iconProvider,
mSideUnfoldController);
mDisplayImeController = displayImeController;
mDisplayInsetsController = displayInsetsController;
- mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSideStage);
mRootTDAOrganizer.registerListener(displayId, this);
final DeviceStateManager deviceStateManager =
mContext.getSystemService(DeviceStateManager.class);
@@ -284,10 +290,6 @@
return result;
}
- void setSideStageOutline(boolean enable) {
- mSideStage.enableOutline(enable);
- }
-
/** Starts 2 tasks in one transition. */
void startTasks(int mainTaskId, @Nullable Bundle mainOptions, int sideTaskId,
@Nullable Bundle sideOptions, @SplitPosition int sidePosition,
@@ -697,9 +699,10 @@
if (bothStageInvisible) {
if (mExitSplitScreenOnHide
- // Don't dismiss staged split when both stages are not visible due to sleeping display,
- // like the cases keyguard showing or screen off.
- || (!mMainStage.mRootTaskInfo.isSleeping && !mSideStage.mRootTaskInfo.isSleeping)) {
+ // Don't dismiss staged split when both stages are not visible due to sleeping
+ // display, like the cases keyguard showing or screen off.
+ || (!mMainStage.mRootTaskInfo.isSleeping
+ && !mSideStage.mRootTaskInfo.isSleeping)) {
exitSplitScreen(null /* childrenToTop */,
SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME);
}
@@ -719,7 +722,6 @@
t.setVisibility(mSideStage.mRootLeash, bothStageVisible)
.setVisibility(mMainStage.mRootLeash, bothStageVisible);
applyDividerVisibility(t);
- applyOutlineVisibility(t);
}
});
}
@@ -741,19 +743,6 @@
}
}
- private void applyOutlineVisibility(SurfaceControl.Transaction t) {
- final SurfaceControl outlineLeash = mSideStage.getOutlineLeash();
- if (outlineLeash == null) {
- return;
- }
-
- if (mDividerVisible) {
- t.show(outlineLeash).setLayer(outlineLeash, SPLIT_DIVIDER_LAYER);
- } else {
- t.hide(outlineLeash);
- }
- }
-
private void onStageHasChildrenChanged(StageListenerImpl stageListener) {
final boolean hasChildren = stageListener.mHasChildren;
final boolean isSideStage = stageListener == mSideStageListener;
@@ -817,8 +806,11 @@
@Override
public void onLayoutSizeChanging(SplitLayout layout) {
- mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t));
- mSideStage.setOutlineVisibility(false);
+ mSyncQueue.runInSync(t -> {
+ updateSurfaceBounds(layout, t);
+ mMainStage.onResizing(getMainStageBounds(), t);
+ mSideStage.onResizing(getSideStageBounds(), t);
+ });
}
@Override
@@ -827,8 +819,11 @@
updateWindowBounds(layout, wct);
updateUnfoldBounds();
mSyncQueue.queue(wct);
- mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t));
- mSideStage.setOutlineVisibility(true);
+ mSyncQueue.runInSync(t -> {
+ updateSurfaceBounds(layout, t);
+ mMainStage.onResized(getMainStageBounds(), t);
+ mSideStage.onResized(getSideStageBounds(), t);
+ });
mLogger.logResize(mSplitLayout.getDividerPositionAsFraction());
}
@@ -893,7 +888,7 @@
if (mSplitLayout == null) {
mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext,
mDisplayAreaInfo.configuration, this, mParentContainerCallbacks,
- mDisplayImeController, mTaskOrganizer);
+ mDisplayImeController, mTaskOrganizer, false /* applyDismissingParallax */);
mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout);
if (mMainUnfoldController != null && mSideUnfoldController != null) {
@@ -1216,18 +1211,6 @@
null /* taskInfo */, false /* allowEnterPip */, TYPE_DOCK_DIVIDER);
}
- RemoteAnimationTarget getOutlineLegacyTarget() {
- final Rect bounds = mSideStage.mRootTaskInfo.configuration.windowConfiguration.getBounds();
- // Leverage TYPE_DOCK_DIVIDER type when wrapping outline remote animation target in order to
- // distinguish as a split auxiliary target in Launcher.
- return new RemoteAnimationTarget(-1 /* taskId */, -1 /* mode */,
- mSideStage.getOutlineLeash(), false /* isTranslucent */, null /* clipRect */,
- null /* contentInsets */, Integer.MAX_VALUE /* prefixOrderIndex */,
- new android.graphics.Point(0, 0) /* position */, bounds, bounds,
- new WindowConfiguration(), true, null /* startLeash */, null /* startBounds */,
- null /* taskInfo */, false /* allowEnterPip */, TYPE_DOCK_DIVIDER);
- }
-
@Override
public void dump(@NonNull PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 6f1a09d..5100c56 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -26,6 +26,7 @@
import android.annotation.CallSuper;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.SparseArray;
@@ -35,9 +36,11 @@
import androidx.annotation.NonNull;
+import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.SurfaceUtils;
import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.split.SplitDecorManager;
import java.io.PrintWriter;
@@ -72,25 +75,31 @@
void onNoLongerSupportMultiWindow();
}
+ private final Context mContext;
private final StageListenerCallbacks mCallbacks;
private final SurfaceSession mSurfaceSession;
- protected final SyncTransactionQueue mSyncQueue;
+ private final SyncTransactionQueue mSyncQueue;
+ private final IconProvider mIconProvider;
protected ActivityManager.RunningTaskInfo mRootTaskInfo;
protected SurfaceControl mRootLeash;
protected SurfaceControl mDimLayer;
protected SparseArray<ActivityManager.RunningTaskInfo> mChildrenTaskInfo = new SparseArray<>();
private final SparseArray<SurfaceControl> mChildrenLeashes = new SparseArray<>();
+ // TODO(b/204308910): Extracts SplitDecorManager related code to common package.
+ private SplitDecorManager mSplitDecorManager;
private final StageTaskUnfoldController mStageTaskUnfoldController;
- StageTaskListener(ShellTaskOrganizer taskOrganizer, int displayId,
+ StageTaskListener(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
- SurfaceSession surfaceSession,
+ SurfaceSession surfaceSession, IconProvider iconProvider,
@Nullable StageTaskUnfoldController stageTaskUnfoldController) {
+ mContext = context;
mCallbacks = callbacks;
mSyncQueue = syncQueue;
mSurfaceSession = surfaceSession;
+ mIconProvider = iconProvider;
mStageTaskUnfoldController = stageTaskUnfoldController;
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this);
}
@@ -142,13 +151,14 @@
if (mRootTaskInfo == null && !taskInfo.hasParentTask()) {
mRootLeash = leash;
mRootTaskInfo = taskInfo;
+ mSplitDecorManager = new SplitDecorManager(
+ mRootTaskInfo.configuration,
+ mIconProvider,
+ mSurfaceSession);
mCallbacks.onRootTaskAppeared();
sendStatusChanged();
- mSyncQueue.runInSync(t -> {
- t.hide(mRootLeash);
- mDimLayer =
- SurfaceUtils.makeDimLayer(t, mRootLeash, "Dim layer", mSurfaceSession);
- });
+ mSyncQueue.runInSync(t -> mDimLayer =
+ SurfaceUtils.makeDimLayer(t, mRootLeash, "Dim layer", mSurfaceSession));
} else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) {
final int taskId = taskInfo.taskId;
mChildrenLeashes.put(taskId, leash);
@@ -179,6 +189,17 @@
return;
}
if (mRootTaskInfo.taskId == taskInfo.taskId) {
+ // Inflates split decor view only when the root task is visible.
+ if (mRootTaskInfo.isVisible != taskInfo.isVisible) {
+ mSyncQueue.runInSync(t -> {
+ if (taskInfo.isVisible) {
+ mSplitDecorManager.inflate(mContext, mRootLeash,
+ taskInfo.configuration.windowConfiguration.getBounds());
+ } else {
+ mSplitDecorManager.release(t);
+ }
+ });
+ }
mRootTaskInfo = taskInfo;
} else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) {
mChildrenTaskInfo.put(taskInfo.taskId, taskInfo);
@@ -205,8 +226,11 @@
final int taskId = taskInfo.taskId;
if (mRootTaskInfo.taskId == taskId) {
mCallbacks.onRootTaskVanished();
- mSyncQueue.runInSync(t -> t.remove(mDimLayer));
mRootTaskInfo = null;
+ mSyncQueue.runInSync(t -> {
+ t.remove(mDimLayer);
+ mSplitDecorManager.release(t);
+ });
} else if (mChildrenTaskInfo.contains(taskId)) {
mChildrenTaskInfo.remove(taskId);
mChildrenLeashes.remove(taskId);
@@ -237,6 +261,18 @@
}
}
+ void onResizing(Rect newBounds, SurfaceControl.Transaction t) {
+ if (mSplitDecorManager != null && mRootTaskInfo != null) {
+ mSplitDecorManager.onResizing(mRootTaskInfo, newBounds, t);
+ }
+ }
+
+ void onResized(Rect newBounds, SurfaceControl.Transaction t) {
+ if (mSplitDecorManager != null) {
+ mSplitDecorManager.onResized(newBounds, t);
+ }
+ }
+
void setBounds(Rect bounds, WindowContainerTransaction wct) {
wct.setBounds(mRootTaskInfo.token, bounds);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java
index 574e379..60a6cd7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java
@@ -882,7 +882,7 @@
if (mSplitLayout == null) {
mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext,
mDisplayAreaInfo.configuration, this, mParentContainerCallbacks,
- mDisplayImeController, mTaskOrganizer);
+ mDisplayImeController, mTaskOrganizer, true /* applyDismissingParallax */);
mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout);
if (mMainUnfoldController != null && mSideUnfoldController != null) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index b4caeb5..73eebad 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -68,7 +68,8 @@
mSplitLayoutHandler,
mCallbacks,
mDisplayImeController,
- mTaskOrganizer));
+ mTaskOrganizer,
+ false /* applyDismissingParallax */));
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java
index 2bcc45e..c972067 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java
@@ -25,10 +25,13 @@
import android.view.SurfaceSession;
import android.window.WindowContainerTransaction;
+import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -41,22 +44,24 @@
/** Tests for {@link MainStage} */
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class MainStageTests {
+public class MainStageTests extends ShellTestCase {
@Mock private ShellTaskOrganizer mTaskOrganizer;
@Mock private StageTaskListener.StageListenerCallbacks mCallbacks;
@Mock private SyncTransactionQueue mSyncQueue;
@Mock private ActivityManager.RunningTaskInfo mRootTaskInfo;
@Mock private SurfaceControl mRootLeash;
+ @Mock private IconProvider mIconProvider;
private WindowContainerTransaction mWct = new WindowContainerTransaction();
private SurfaceSession mSurfaceSession = new SurfaceSession();
private MainStage mMainStage;
@Before
+ @UiThreadTest
public void setup() {
MockitoAnnotations.initMocks(this);
mRootTaskInfo = new TestRunningTaskInfoBuilder().build();
- mMainStage = new MainStage(mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, mSyncQueue,
- mSurfaceSession, null);
+ mMainStage = new MainStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks,
+ mSyncQueue, mSurfaceSession, mIconProvider, null);
mMainStage.onTaskAppeared(mRootTaskInfo, mRootLeash);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
index 838aa81..1857faa 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
@@ -33,6 +33,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
@@ -54,6 +55,7 @@
@Mock private SyncTransactionQueue mSyncQueue;
@Mock private ActivityManager.RunningTaskInfo mRootTask;
@Mock private SurfaceControl mRootLeash;
+ @Mock private IconProvider mIconProvider;
@Spy private WindowContainerTransaction mWct;
private SurfaceSession mSurfaceSession = new SurfaceSession();
private SideStage mSideStage;
@@ -64,7 +66,7 @@
MockitoAnnotations.initMocks(this);
mRootTask = new TestRunningTaskInfoBuilder().build();
mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks,
- mSyncQueue, mSurfaceSession, null);
+ mSyncQueue, mSurfaceSession, mIconProvider, null);
mSideStage.onTaskAppeared(mRootTask, mRootLeash);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 05496b0..d5dee82 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -55,6 +55,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
@@ -89,6 +90,7 @@
@Mock private Transitions mTransitions;
@Mock private SurfaceSession mSurfaceSession;
@Mock private SplitscreenEventLogger mLogger;
+ @Mock private IconProvider mIconProvider;
private SplitLayout mSplitLayout;
private MainStage mMainStage;
private SideStage mSideStage;
@@ -107,11 +109,13 @@
doReturn(mockExecutor).when(mTransitions).getAnimExecutor();
doReturn(mock(SurfaceControl.Transaction.class)).when(mTransactionPool).acquire();
mSplitLayout = SplitTestUtils.createMockSplitLayout();
- mMainStage = new MainStage(mTaskOrganizer, DEFAULT_DISPLAY, mock(
- StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession, null);
+ mMainStage = new MainStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
+ StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession,
+ mIconProvider, null);
mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
- StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession, null);
+ StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession,
+ mIconProvider, null);
mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
index 3ed72e2..53d5076 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
@@ -35,10 +35,13 @@
import android.view.SurfaceSession;
import android.window.WindowContainerTransaction;
+import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -57,7 +60,7 @@
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
-public final class StageTaskListenerTests {
+public final class StageTaskListenerTests extends ShellTestCase {
private static final boolean ENABLE_SHELL_TRANSITIONS =
SystemProperties.getBoolean("persist.debug.shell_transit", false);
@@ -68,6 +71,8 @@
@Mock
private SyncTransactionQueue mSyncQueue;
@Mock
+ private IconProvider mIconProvider;
+ @Mock
private StageTaskUnfoldController mStageTaskUnfoldController;
@Captor
private ArgumentCaptor<SyncTransactionQueue.TransactionRunnable> mRunnableCaptor;
@@ -77,14 +82,17 @@
private StageTaskListener mStageTaskListener;
@Before
+ @UiThreadTest
public void setup() {
MockitoAnnotations.initMocks(this);
mStageTaskListener = new StageTaskListener(
+ mContext,
mTaskOrganizer,
DEFAULT_DISPLAY,
mCallbacks,
mSyncQueue,
mSurfaceSession,
+ mIconProvider,
mStageTaskUnfoldController);
mRootTask = new TestRunningTaskInfoBuilder().build();
mRootTask.parentTaskId = INVALID_TASK_ID;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
index 1d29966..ed12e83 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
@@ -68,7 +68,7 @@
}
for (Map.Entry<Integer, ParcelUuid> entry: groupIdMap.entrySet()) {
- if (entry.getValue().equals(BluetoothUuid.BASE_UUID)) {
+ if (entry.getValue().equals(BluetoothUuid.CAP)) {
return entry.getKey();
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 3347920..5e2f310 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -338,7 +338,7 @@
.getGroupUuidMapByDevice(cachedDevice.getDevice());
if (groupIdMap != null) {
for (Map.Entry<Integer, ParcelUuid> entry: groupIdMap.entrySet()) {
- if (entry.getValue().equals(BluetoothUuid.BASE_UUID)) {
+ if (entry.getValue().equals(BluetoothUuid.CAP)) {
cachedDevice.setGroupId(entry.getKey());
break;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
index 274696b..468aa05 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
@@ -183,6 +183,20 @@
return setBadge(badge);
}
+ /**
+ * Sets the managed badge to this user icon if the device has a device owner.
+ */
+ public UserIconDrawable setBadgeIfManagedDevice(Context context) {
+ Drawable badge = null;
+ boolean deviceOwnerExists = context.getSystemService(DevicePolicyManager.class)
+ .getDeviceOwnerComponentOnAnyUser() != null;
+ if (deviceOwnerExists) {
+ badge = getDrawableForDisplayDensity(
+ context, com.android.internal.R.drawable.ic_corp_badge_case);
+ }
+ return setBadge(badge);
+ }
+
public void setBadgeRadius(float radius) {
mBadgeRadius = radius;
onBoundsChange(getBounds());
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
index 989010e..a16f5cd 100644
--- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -123,18 +123,18 @@
/** Interface for launching Intents, which can differ on the lockscreen */
interface IntentStarter {
- default void startFromAction(SmartspaceAction action, View v) {
+ default void startFromAction(SmartspaceAction action, View v, boolean showOnLockscreen) {
if (action.getIntent() != null) {
- startIntent(v, action.getIntent());
+ startIntent(v, action.getIntent(), showOnLockscreen);
} else if (action.getPendingIntent() != null) {
- startPendingIntent(action.getPendingIntent());
+ startPendingIntent(action.getPendingIntent(), showOnLockscreen);
}
}
/** Start the intent */
- void startIntent(View v, Intent i);
+ void startIntent(View v, Intent i, boolean showOnLockscreen);
/** Start the PendingIntent */
- void startPendingIntent(PendingIntent pi);
+ void startPendingIntent(PendingIntent pi, boolean showOnLockscreen);
}
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
index 757dc2e..b83ea4a 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
@@ -55,13 +55,14 @@
/**
* Asks QS to update its presentation, according to {@code NotificationPanelViewController}.
- *
* @param qsExpansionFraction How much each UI element in QS should be expanded (QQS to QS.)
* @param panelExpansionFraction Whats the expansion of the whole shade.
* @param headerTranslation How much we should vertically translate QS.
+ * @param squishinessFraction Fraction that affects tile height. 0 when collapsed,
+ * 1 when expanded.
*/
void setQsExpansion(float qsExpansionFraction, float panelExpansionFraction,
- float headerTranslation);
+ float headerTranslation, float squishinessFraction);
void setHeaderListening(boolean listening);
void notifyCustomizeChanged();
void setContainerController(QSContainerController controller);
diff --git a/packages/SystemUI/res-keyguard/values-sw600dp-land/dimens.xml b/packages/SystemUI/res-keyguard/values-sw600dp-land/dimens.xml
index d816b3a..17765b5 100644
--- a/packages/SystemUI/res-keyguard/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values-sw600dp-land/dimens.xml
@@ -23,6 +23,6 @@
<dimen name="widget_big_font_size">88dp</dimen>
<dimen name="qs_header_system_icons_area_height">0dp</dimen>
- <dimen name="qs_panel_padding_top">0dp</dimen>
+ <dimen name="qs_panel_padding_top">@dimen/qqs_layout_margin_top</dimen>
</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index 6b14c96..10a2f4c 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -63,7 +63,7 @@
android:id="@+id/qqs_footer_actions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="16dp"
+ android:layout_marginTop="@dimen/qqs_layout_margin_top"
android:layout_marginStart="@dimen/qs_footer_margin"
android:layout_marginEnd="@dimen/qs_footer_margin"
/>
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
index b2a5409..11addf0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
@@ -40,7 +40,7 @@
@Nullable private UdfpsEnrollHelper mEnrollHelper;
@NonNull private List<UdfpsEnrollProgressBarSegment> mSegments = new ArrayList<>();
- private int mTotalSteps = 1;
+ private int mTotalSteps = 0;
private int mProgressSteps = 0;
private boolean mIsShowingHelp = false;
@@ -67,22 +67,19 @@
void onEnrollmentProgress(int remaining, int totalSteps) {
mTotalSteps = totalSteps;
- updateState(getProgressSteps(remaining, totalSteps), false /* isShowingHelp */);
+
+ // Show some progress for the initial touch.
+ updateState(Math.max(1, totalSteps - remaining), false /* isShowingHelp */);
}
void onEnrollmentHelp(int remaining, int totalSteps) {
- updateState(getProgressSteps(remaining, totalSteps), true /* isShowingHelp */);
+ updateState(Math.max(0, totalSteps - remaining), true /* isShowingHelp */);
}
void onLastStepAcquired() {
updateState(mTotalSteps, false /* isShowingHelp */);
}
- private static int getProgressSteps(int remaining, int totalSteps) {
- // Show some progress for the initial touch.
- return Math.max(1, totalSteps - remaining);
- }
-
private void updateState(int progressSteps, boolean isShowingHelp) {
updateProgress(progressSteps);
updateFillColor(isShowingHelp);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 9d0591e..5c3e07f 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -659,7 +659,7 @@
@Override
@AnyThread
public void onTrigger(TriggerEvent event) {
- final Sensor sensor = mSensors[mDevicePosture];
+ final Sensor sensor = mSensors[mPosture];
mDozeLog.traceSensor(mPulseReason);
mHandler.post(mWakeLock.wrap(() -> {
if (DEBUG) Log.d(TAG, "onTrigger: " + triggerEventToString(event));
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 1a7a306..7e5ff8a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -166,6 +166,11 @@
updateListening();
}
+ @Override
+ public void setSquishinessFraction(float squishinessFraction) {
+ // No-op, paged layouts are not squishy.
+ }
+
private void updateListening() {
for (TileLayout tilePage : mPages) {
tilePage.setListening(tilePage.getParent() != null && mListening);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 90d3448..44d5e21 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -316,8 +316,8 @@
if (mQQSTileHeightAnimator == null) {
mQQSTileHeightAnimator = new HeightExpansionAnimator(this,
- quickTileView.getHeight(), tileView.getHeight());
- qqsTileHeight = quickTileView.getHeight();
+ quickTileView.getMeasuredHeight(), tileView.getMeasuredHeight());
+ qqsTileHeight = quickTileView.getMeasuredHeight();
}
mQQSTileHeightAnimator.addView(quickTileView);
@@ -380,7 +380,7 @@
if (mOtherTilesExpandAnimator == null) {
mOtherTilesExpandAnimator =
new HeightExpansionAnimator(
- this, qqsTileHeight, tileView.getHeight());
+ this, qqsTileHeight, tileView.getMeasuredHeight());
}
mOtherTilesExpandAnimator.addView(tileView);
tileView.setClipChildren(true);
@@ -658,7 +658,7 @@
mTranslateWhileExpanding = shouldTranslate;
}
- static class HeightExpansionAnimator {
+ private static class HeightExpansionAnimator {
private final List<View> mViews = new ArrayList<>();
private final ValueAnimator mAnimator;
private final TouchAnimator.Listener mListener;
@@ -673,9 +673,10 @@
int height = (Integer) valueAnimator.getAnimatedValue();
for (int i = 0; i < viewCount; i++) {
View v = mViews.get(i);
- v.setBottom(v.getTop() + height);
if (v instanceof HeightOverrideable) {
((HeightOverrideable) v).setHeightOverride(height);
+ } else {
+ v.setBottom(v.getTop() + height);
}
}
if (t == 0f) {
@@ -713,9 +714,10 @@
final int viewsCount = mViews.size();
for (int i = 0; i < viewsCount; i++) {
View v = mViews.get(i);
- v.setBottom(v.getTop() + v.getMeasuredHeight());
if (v instanceof HeightOverrideable) {
((HeightOverrideable) v).resetOverride();
+ } else {
+ v.setBottom(v.getTop() + v.getMeasuredHeight());
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 89bbcf5..eeca239 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -81,6 +81,7 @@
private QSAnimator mQSAnimator;
private HeightListener mPanelView;
+ private QSSquishinessController mQSSquishinessController;
protected QuickStatusBarHeader mHeader;
protected NonInterceptingScrollView mQSPanelScrollView;
private QSDetail mQSDetail;
@@ -90,6 +91,7 @@
private QSFooter mFooter;
private float mLastQSExpansion = -1;
private float mLastPanelFraction;
+ private float mSquishinessFraction = 1;
private boolean mQsDisabled;
private ImageView mQsDragHandler;
@@ -210,6 +212,7 @@
mQSDetail.setQsPanel(mQSPanelController, mHeader, mFooter, mFalsingManager);
mQSAnimator = qsFragmentComponent.getQSAnimator();
+ mQSSquishinessController = qsFragmentComponent.getQSSquishinessController();
mQSCustomizerController = qsFragmentComponent.getQSCustomizerController();
mQSCustomizerController.init();
@@ -231,7 +234,7 @@
boolean sizeChanged = (oldTop - oldBottom) != (top - bottom);
if (sizeChanged) {
setQsExpansion(mLastQSExpansion, mLastPanelFraction,
- mLastHeaderTranslation);
+ mLastHeaderTranslation, mSquishinessFraction);
}
});
mQSPanelController.setUsingHorizontalLayoutChangeListener(
@@ -413,7 +416,8 @@
mQSAnimator.setShowCollapsedOnKeyguard(showCollapsed);
}
if (!showCollapsed && isKeyguardState()) {
- setQsExpansion(mLastQSExpansion, mLastPanelFraction, 0);
+ setQsExpansion(mLastQSExpansion, mLastPanelFraction, 0,
+ mSquishinessFraction);
}
}
}
@@ -494,12 +498,13 @@
updateShowCollapsedOnKeyguard();
}
mFullShadeProgress = progress;
- setQsExpansion(mLastQSExpansion, mLastPanelFraction, mLastHeaderTranslation);
+ setQsExpansion(mLastQSExpansion, mLastPanelFraction, mLastHeaderTranslation,
+ isTransitioningToFullShade ? progress : mSquishinessFraction);
}
@Override
public void setQsExpansion(float expansion, float panelExpansionFraction,
- float proposedTranslation) {
+ float proposedTranslation, float squishinessFraction) {
float headerTranslation = mTransitioningToFullShade ? 0 : proposedTranslation;
float progress = mTransitioningToFullShade ? mFullShadeProgress : panelExpansionFraction;
setAlphaAnimationProgress(mInSplitShade ? progress : 1);
@@ -517,11 +522,13 @@
if (expansion == mLastQSExpansion
&& mLastKeyguardAndExpanded == onKeyguardAndExpanded
&& mLastViewHeight == currentHeight
- && mLastHeaderTranslation == headerTranslation) {
+ && mLastHeaderTranslation == headerTranslation
+ && mSquishinessFraction == squishinessFraction) {
return;
}
mLastHeaderTranslation = headerTranslation;
mLastPanelFraction = panelExpansionFraction;
+ mSquishinessFraction = squishinessFraction;
mLastQSExpansion = expansion;
mLastKeyguardAndExpanded = onKeyguardAndExpanded;
mLastViewHeight = currentHeight;
@@ -557,6 +564,9 @@
}
updateQsBounds();
+ if (mQSSquishinessController != null) {
+ mQSSquishinessController.setSquishiness(mSquishinessFraction);
+ }
if (mQSAnimator != null) {
mQSAnimator.setPosition(expansion);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 28aa884..2665f3a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -736,6 +736,11 @@
void setListening(boolean listening, UiEventLogger uiEventLogger);
/**
+ * Sets a size modifier for the tile. Where 0 means collapsed, and 1 expanded.
+ */
+ void setSquishinessFraction(float squishinessFraction);
+
+ /**
* Sets the minimum number of rows to show
*
* @param minRows the minimum.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSquishinessController.kt b/packages/SystemUI/src/com/android/systemui/qs/QSSquishinessController.kt
new file mode 100644
index 0000000..6de8370
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSquishinessController.kt
@@ -0,0 +1,64 @@
+package com.android.systemui.qs
+
+import android.view.ViewGroup
+import com.android.systemui.qs.dagger.QSFragmentModule.QQS_FOOTER
+import com.android.systemui.qs.dagger.QSScope
+import com.android.systemui.qs.tileimpl.HeightOverrideable
+import javax.inject.Inject
+import javax.inject.Named
+
+@QSScope
+class QSSquishinessController @Inject constructor(
+ private val qsTileHost: QSTileHost,
+ @Named(QQS_FOOTER) private val qqsFooterActionsView: FooterActionsView,
+ private val qsAnimator: QSAnimator,
+ private val quickQSPanelController: QuickQSPanelController
+) {
+
+ /**
+ * Fraction from 0 to 1, where 0 is collapsed and 1 expanded.
+ */
+ var squishiness: Float = 1f
+ set(value) {
+ if (field == value) {
+ return
+ }
+ if ((field != 1f && value == 1f) || (field != 0f && value == 0f)) {
+ qsAnimator.requestAnimatorUpdate()
+ }
+ field = value
+ updateSquishiness()
+ }
+
+ /**
+ * Change the height of all tiles and repositions their siblings.
+ */
+ private fun updateSquishiness() {
+ // Start by updating the height of all tiles
+ for (tile in qsTileHost.tiles) {
+ val tileView = quickQSPanelController.getTileView(tile)
+ (tileView as? HeightOverrideable)?.let {
+ it.squishinessFraction = squishiness
+ }
+ }
+
+ // Update tile positions in the layout
+ val tileLayout = quickQSPanelController.tileLayout as TileLayout
+ tileLayout.setSquishinessFraction(squishiness)
+
+ // Calculate how much we should move the footer
+ val tileHeightOffset = tileLayout.height - tileLayout.tilesHeight
+ val footerTopMargin = (qqsFooterActionsView.layoutParams as ViewGroup.MarginLayoutParams)
+ .topMargin
+ val nextTop = tileLayout.bottom - tileHeightOffset + footerTopMargin
+ val amountMoved = nextTop - qqsFooterActionsView.top
+
+ // Move the footer and other siblings (MediaPlayer)
+ (qqsFooterActionsView.parent as ViewGroup?)?.let { parent ->
+ val index = parent.indexOfChild(qqsFooterActionsView)
+ for (i in index until parent.childCount) {
+ parent.getChildAt(i).top += amountMoved
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 1a890a7..ee5d5ff 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -41,6 +41,8 @@
private int mMinRows = 1;
private int mMaxColumns = NO_MAX_COLUMNS;
protected int mResourceColumns;
+ private float mSquishinessFraction = 1f;
+ private int mLastTileBottom;
public TileLayout(Context context) {
this(context, null);
@@ -210,10 +212,11 @@
return mMaxCellHeight;
}
- protected void layoutTileRecords(int numRecords) {
+ private void layoutTileRecords(int numRecords) {
final boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
int row = 0;
int column = 0;
+ mLastTileBottom = 0;
// Layout each QS tile.
final int tilesToLayout = Math.min(numRecords, mRows * mColumns);
@@ -228,7 +231,9 @@
final int top = getRowTop(row);
final int left = getColumnStart(isRtl ? mColumns - column - 1 : column);
final int right = left + mCellWidth;
- record.tileView.layout(left, top, right, top + record.tileView.getMeasuredHeight());
+ final int bottom = top + record.tileView.getMeasuredHeight();
+ record.tileView.layout(left, top, right, bottom);
+ mLastTileBottom = bottom;
}
}
@@ -238,7 +243,7 @@
}
protected int getRowTop(int row) {
- return row * (mCellHeight + mCellMarginVertical);
+ return (int) (row * (mCellHeight * mSquishinessFraction + mCellMarginVertical));
}
protected int getColumnStart(int column) {
@@ -264,4 +269,17 @@
// up.
return Math.max(mColumns * mRows, 1);
}
+
+ public int getTilesHeight() {
+ return mLastTileBottom + getPaddingBottom();
+ }
+
+ @Override
+ public void setSquishinessFraction(float squishinessFraction) {
+ if (Float.compare(mSquishinessFraction, squishinessFraction) == 0) {
+ return;
+ }
+ mSquishinessFraction = squishinessFraction;
+ layoutTileRecords(mRecords.size());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
index 8cc0502..63cbc21 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
@@ -21,6 +21,7 @@
import com.android.systemui.qs.QSFooter;
import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSPanelController;
+import com.android.systemui.qs.QSSquishinessController;
import com.android.systemui.qs.QuickQSPanelController;
import com.android.systemui.qs.customize.QSCustomizerController;
@@ -57,4 +58,7 @@
/** Construct a {@link QSCustomizerController}. */
QSCustomizerController getQSCustomizerController();
+
+ /** Construct a {@link QSSquishinessController}. */
+ QSSquishinessController getQSSquishinessController();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/HeightOverrideable.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/HeightOverrideable.kt
index 866fa09..61d68ec 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/HeightOverrideable.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/HeightOverrideable.kt
@@ -21,9 +21,8 @@
const val NO_OVERRIDE = -1
}
- var heightOverride: Int
+ abstract var heightOverride: Int
+ abstract fun resetOverride()
- fun resetOverride() {
- heightOverride = NO_OVERRIDE
- }
+ abstract var squishinessFraction: Float
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index ee5e4df..67c311e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -68,6 +68,18 @@
}
override var heightOverride: Int = HeightOverrideable.NO_OVERRIDE
+ set(value) {
+ if (field == value) return
+ field = value
+ updateHeight()
+ }
+
+ override var squishinessFraction: Float = 1f
+ set(value) {
+ if (field == value) return
+ field = value
+ updateHeight()
+ }
private val colorActive = Utils.getColorAttrDefaultColor(context,
com.android.internal.R.attr.colorAccentPrimary)
@@ -148,6 +160,11 @@
updateResources()
}
+ override fun resetOverride() {
+ heightOverride = HeightOverrideable.NO_OVERRIDE
+ updateHeight()
+ }
+
fun updateResources() {
FontSizeUtils.updateFontSize(label, R.dimen.qs_tile_text_size)
FontSizeUtils.updateFontSize(secondaryLabel, R.dimen.qs_tile_text_size)
@@ -218,9 +235,17 @@
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
super.onLayout(changed, l, t, r, b)
- if (heightOverride != HeightOverrideable.NO_OVERRIDE) {
- bottom = top + heightOverride
- }
+ updateHeight()
+ }
+
+ private fun updateHeight() {
+ val actualHeight = (if (heightOverride != HeightOverrideable.NO_OVERRIDE) {
+ heightOverride
+ } else {
+ measuredHeight
+ } * squishinessFraction).toInt()
+ bottom = top + actualHeight
+ scrollY = (actualHeight - height) / 2
}
override fun updateAccessibilityOrder(previousView: View?): View {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index bacb85a..cf9daf6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -34,6 +34,7 @@
import android.view.ViewGroup
import com.android.settingslib.Utils
import com.android.systemui.R
+import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
@@ -188,13 +189,28 @@
val ssView = plugin.getView(parent)
ssView.registerDataProvider(plugin)
+
+ val animationController = ActivityLaunchAnimator.Controller.fromView(
+ ssView as View,
+ null /* cujType */
+ )
+
ssView.setIntentStarter(object : BcSmartspaceDataPlugin.IntentStarter {
- override fun startIntent(v: View?, i: Intent?) {
- activityStarter.startActivity(i, true /* dismissShade */)
+ override fun startIntent(v: View?, i: Intent?, showOnLockscreen: Boolean) {
+ activityStarter.startActivity(
+ i,
+ true, /* dismissShade */
+ animationController,
+ showOnLockscreen
+ )
}
- override fun startPendingIntent(pi: PendingIntent?) {
- activityStarter.startPendingIntentDismissingKeyguard(pi)
+ override fun startPendingIntent(pi: PendingIntent?, showOnLockscreen: Boolean) {
+ if (showOnLockscreen) {
+ pi?.send()
+ } else {
+ activityStarter.startPendingIntentDismissingKeyguard(pi)
+ }
}
})
ssView.setFalsingManager(falsingManager)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorLogger.kt
index bac5223..e8f352f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorLogger.kt
@@ -15,7 +15,7 @@
fun logGutsOpened(key: String, guts: NotificationGuts) {
buffer.log(TAG, LogLevel.DEBUG, {
str1 = key
- str2 = guts::class.simpleName
+ str2 = guts.gutsContent::class.simpleName
bool1 = guts.isLeavebehind
}, {
"Guts of type $str2 (leave behind: $bool1) opened for class $str1"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
index 18f3b45..cd6f35b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
@@ -61,7 +61,9 @@
ConversationCoordinator conversationCoordinator,
PreparationCoordinator preparationCoordinator,
MediaCoordinator mediaCoordinator,
+ ShadeEventCoordinator shadeEventCoordinator,
SmartspaceDedupingCoordinator smartspaceDedupingCoordinator,
+ ViewConfigCoordinator viewConfigCoordinator,
VisualStabilityCoordinator visualStabilityCoordinator,
CommunalCoordinator communalCoordinator) {
dumpManager.registerDumpable(TAG, this);
@@ -75,6 +77,8 @@
mCoordinators.add(bubbleCoordinator);
mCoordinators.add(conversationCoordinator);
mCoordinators.add(mediaCoordinator);
+ mCoordinators.add(shadeEventCoordinator);
+ mCoordinators.add(viewConfigCoordinator);
mCoordinators.add(visualStabilityCoordinator);
mCoordinators.add(communalCoordinator);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinator.kt
new file mode 100644
index 0000000..f9648a0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinator.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator
+
+import android.service.notification.NotificationListenerService
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource
+import javax.inject.Inject
+
+/**
+ * A coordinator which provides callbacks to a view surfaces for various events relevant to the
+ * shade, such as when the user removes a notification, or when the shade is emptied.
+ */
+@SysUISingleton
+class ShadeEventCoordinator @Inject internal constructor(
+ private val mLogger: ShadeEventCoordinatorLogger
+) : Coordinator, NotifShadeEventSource {
+ private var mNotifRemovedByUserCallback: Runnable? = null
+ private var mShadeEmptiedCallback: Runnable? = null
+ private var mEntryRemoved = false
+ private var mEntryRemovedByUser = false
+
+ override fun attach(pipeline: NotifPipeline) {
+ pipeline.addCollectionListener(mNotifCollectionListener)
+ pipeline.addOnBeforeRenderListListener(this::onBeforeRenderList)
+ }
+
+ private val mNotifCollectionListener = object : NotifCollectionListener {
+ override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
+ mEntryRemoved = true
+ mEntryRemovedByUser =
+ reason == NotificationListenerService.REASON_CLICK ||
+ reason == NotificationListenerService.REASON_CANCEL_ALL ||
+ reason == NotificationListenerService.REASON_CANCEL
+ }
+ }
+
+ override fun setNotifRemovedByUserCallback(callback: Runnable) {
+ check(mNotifRemovedByUserCallback == null) { "mNotifRemovedByUserCallback already set" }
+ mNotifRemovedByUserCallback = callback
+ }
+
+ override fun setShadeEmptiedCallback(callback: Runnable) {
+ check(mShadeEmptiedCallback == null) { "mShadeEmptiedCallback already set" }
+ mShadeEmptiedCallback = callback
+ }
+
+ private fun onBeforeRenderList(entries: List<ListEntry>) {
+ if (mEntryRemoved && entries.isEmpty()) {
+ mLogger.logShadeEmptied()
+ mShadeEmptiedCallback?.run()
+ }
+ if (mEntryRemoved && mEntryRemovedByUser) {
+ mLogger.logNotifRemovedByUser()
+ mNotifRemovedByUserCallback?.run()
+ }
+ mEntryRemoved = false
+ mEntryRemovedByUser = false
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorLogger.kt
new file mode 100644
index 0000000..c687e1b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorLogger.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.dagger.NotificationLog
+import javax.inject.Inject
+
+private const val TAG = "ShadeEventCoordinator"
+
+/** Logger for the [ShadeEventCoordinator] */
+class ShadeEventCoordinatorLogger @Inject constructor(
+ @NotificationLog private val buffer: LogBuffer
+) {
+
+ fun logShadeEmptied() {
+ buffer.log(TAG, LogLevel.DEBUG, { }, { "Shade emptied" })
+ }
+
+ fun logNotifRemovedByUser() {
+ buffer.log(TAG, LogLevel.DEBUG, { }, { "Notification removed by user" })
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
new file mode 100644
index 0000000..df1132b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator
+
+import com.android.internal.widget.MessagingGroup
+import com.android.internal.widget.MessagingMessage
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener
+import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager
+import com.android.systemui.statusbar.policy.ConfigurationController
+import javax.inject.Inject
+
+/**
+ * A coordinator which ensures that notifications within the new pipeline are correctly inflated
+ * for the current uiMode and screen properties; additionally deferring those changes when a user
+ * change is in progress until that process has completed.
+ */
+@SysUISingleton
+class ViewConfigCoordinator @Inject internal constructor(
+ configurationController: ConfigurationController,
+ lockscreenUserManager: NotificationLockscreenUserManagerImpl,
+ featureFlags: FeatureFlags,
+ private val mGutsManager: NotificationGutsManager,
+ private val mKeyguardUpdateMonitor: KeyguardUpdateMonitor
+) : Coordinator, UserChangedListener, ConfigurationController.ConfigurationListener {
+
+ private var mReinflateNotificationsOnUserSwitched = false
+ private var mDispatchUiModeChangeOnUserSwitched = false
+ private var mPipeline: NotifPipeline? = null
+
+ init {
+ if (featureFlags.isNewNotifPipelineRenderingEnabled) {
+ lockscreenUserManager.addUserChangedListener(this)
+ configurationController.addCallback(this)
+ }
+ }
+
+ override fun attach(pipeline: NotifPipeline) {
+ mPipeline = pipeline
+ }
+
+ override fun onDensityOrFontScaleChanged() {
+ MessagingMessage.dropCache()
+ MessagingGroup.dropCache()
+ if (!mKeyguardUpdateMonitor.isSwitchingUser) {
+ updateNotificationsOnDensityOrFontScaleChanged()
+ } else {
+ mReinflateNotificationsOnUserSwitched = true
+ }
+ }
+
+ override fun onUiModeChanged() {
+ if (!mKeyguardUpdateMonitor.isSwitchingUser) {
+ updateNotificationsOnUiModeChanged()
+ } else {
+ mDispatchUiModeChangeOnUserSwitched = true
+ }
+ }
+
+ override fun onThemeChanged() {
+ onDensityOrFontScaleChanged()
+ }
+
+ override fun onUserChanged(userId: Int) {
+ if (mReinflateNotificationsOnUserSwitched) {
+ updateNotificationsOnDensityOrFontScaleChanged()
+ mReinflateNotificationsOnUserSwitched = false
+ }
+ if (mDispatchUiModeChangeOnUserSwitched) {
+ updateNotificationsOnUiModeChanged()
+ mDispatchUiModeChangeOnUserSwitched = false
+ }
+ }
+
+ private fun updateNotificationsOnUiModeChanged() {
+ mPipeline?.allNotifs?.forEach { entry ->
+ val row = entry.row
+ row?.onUiModeChanged()
+ }
+ }
+
+ private fun updateNotificationsOnDensityOrFontScaleChanged() {
+ mPipeline?.allNotifs?.forEach { entry ->
+ entry.onDensityOrFontScaleChanged()
+ val exposedGuts = entry.areGutsExposed()
+ if (exposedGuts) {
+ mGutsManager.onDensityOrFontScaleChanged(entry)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationPresenterExtensions.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationPresenterExtensions.java
new file mode 100644
index 0000000..4ee08ed
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationPresenterExtensions.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.legacy;
+
+import static com.android.systemui.statusbar.phone.StatusBar.SPEW;
+
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
+
+import org.jetbrains.annotations.NotNull;
+
+import javax.inject.Inject;
+
+/**
+ * This is some logic extracted from the
+ * {@link com.android.systemui.statusbar.phone.StatusBarNotificationPresenter}
+ * into a class that implements a new-pipeline interface so that the new pipeline can implement it
+ * correctly.
+ *
+ * Specifically, this is the logic which updates notifications when uiMode and screen properties
+ * change, and which closes the shade when the last notification disappears.
+ */
+public class LegacyNotificationPresenterExtensions implements NotifShadeEventSource {
+ private static final String TAG = "LegacyNotifPresenter";
+ private final NotificationEntryManager mEntryManager;
+ private boolean mEntryListenerAdded;
+ private Runnable mShadeEmptiedCallback;
+ private Runnable mNotifRemovedByUserCallback;
+
+ @Inject
+ public LegacyNotificationPresenterExtensions(NotificationEntryManager entryManager) {
+ mEntryManager = entryManager;
+ }
+
+ private void ensureEntryListenerAdded() {
+ if (mEntryListenerAdded) return;
+ mEntryListenerAdded = true;
+ mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
+ @Override
+ public void onEntryRemoved(
+ @NotNull NotificationEntry entry,
+ NotificationVisibility visibility,
+ boolean removedByUser,
+ int reason) {
+ StatusBarNotification old = entry.getSbn();
+ if (SPEW) {
+ Log.d(TAG, "removeNotification key=" + entry.getKey()
+ + " old=" + old + " reason=" + reason);
+ }
+
+ if (old != null && !mEntryManager.hasActiveNotifications()) {
+ if (mShadeEmptiedCallback != null) mShadeEmptiedCallback.run();
+ }
+ if (removedByUser) {
+ if (mNotifRemovedByUserCallback != null) mNotifRemovedByUserCallback.run();
+ }
+ }
+ });
+ }
+
+ @Override
+ public void setNotifRemovedByUserCallback(@NonNull Runnable callback) {
+ if (mNotifRemovedByUserCallback != null) {
+ throw new IllegalStateException("mNotifRemovedByUserCallback already set");
+ }
+ mNotifRemovedByUserCallback = callback;
+ ensureEntryListenerAdded();
+ }
+
+ @Override
+ public void setShadeEmptiedCallback(@NonNull Runnable callback) {
+ if (mShadeEmptiedCallback != null) {
+ throw new IllegalStateException("mShadeEmptiedCallback already set");
+ }
+ mShadeEmptiedCallback = callback;
+ ensureEntryListenerAdded();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifShadeEventSource.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifShadeEventSource.kt
new file mode 100644
index 0000000..e24f6a0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifShadeEventSource.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.render
+
+/**
+ * This is an object which provides callbacks for certain important events related to the
+ * notification shade, such as notifications being removed by the user, or the shade becoming empty.
+ */
+interface NotifShadeEventSource {
+ /**
+ * Registers a callback to be invoked when the last notification has been removed from
+ * the shade for any reason
+ */
+ fun setShadeEmptiedCallback(callback: Runnable)
+
+ /**
+ * Registers a callback to be invoked when a notification has been removed from
+ * the shade by a user action
+ */
+ fun setNotifRemovedByUserCallback(callback: Runnable)
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index dfa1f5f..540216c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -45,10 +45,12 @@
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.coordinator.ShadeEventCoordinator;
import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator;
import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.inflation.OnUserInteractionCallbackImpl;
+import com.android.systemui.statusbar.notification.collection.legacy.LegacyNotificationPresenterExtensions;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.OnUserInteractionCallbackImplLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
@@ -59,6 +61,7 @@
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManagerImpl;
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager;
+import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.init.NotificationsControllerImpl;
import com.android.systemui.statusbar.notification.init.NotificationsControllerStub;
@@ -90,7 +93,7 @@
/**
* Dagger Module for classes found within the com.android.systemui.statusbar.notification package.
*/
-@Module(includes = { NotificationSectionHeadersModule.class })
+@Module(includes = {NotificationSectionHeadersModule.class})
public interface NotificationsModule {
@Binds
StackScrollAlgorithm.SectionProvider bindSectionProvider(
@@ -271,6 +274,20 @@
}
/**
+ * Provide the active implementation for presenting notifications.
+ */
+ @Provides
+ @SysUISingleton
+ static NotifShadeEventSource provideNotifShadeEventSource(
+ FeatureFlags featureFlags,
+ Lazy<ShadeEventCoordinator> shadeEventCoordinatorLazy,
+ Lazy<LegacyNotificationPresenterExtensions> legacyNotificationPresenterExtensionsLazy) {
+ return featureFlags.isNewNotifPipelineRenderingEnabled()
+ ? shadeEventCoordinatorLazy.get()
+ : legacyNotificationPresenterExtensionsLazy.get();
+ }
+
+ /**
* Provide a dismissal callback that's triggered when a user manually dismissed a notification
* from the notification shade or it gets auto-cancelled by click.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index e956046..02b1210 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -1249,6 +1249,7 @@
@Override
public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
if (mMenuRow != null && mMenuRow.getMenuView() != null) {
mMenuRow.onConfigurationChanged();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index c5a1f48..4795f08 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -653,6 +653,10 @@
return 0f;
}
+ public float getNotificationSquishinessFraction() {
+ return mStackScrollAlgorithm.getNotificationSquishinessFraction(mAmbientState);
+ }
+
void reinflateViews() {
inflateFooterView();
inflateEmptyShadeView();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 09ab90e..94720e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -1078,6 +1078,10 @@
mView.setOnStackYChanged(onStackYChanged);
}
+ public float getNotificationSquishinessFraction() {
+ return mView.getNotificationSquishinessFraction();
+ }
+
public float calculateAppearFractionBypass() {
return mView.calculateAppearFractionBypass();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index afe0bba..015edb8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -110,6 +110,15 @@
getNotificationChildrenStates(algorithmState, ambientState);
}
+ /**
+ * How expanded or collapsed notifications are when pulling down the shade.
+ * @param ambientState Current ambient state.
+ * @return 0 when fully collapsed, 1 when expanded.
+ */
+ public float getNotificationSquishinessFraction(AmbientState ambientState) {
+ return getExpansionFractionWithoutShelf(mTempAlgorithmState, ambientState);
+ }
+
private void resetChildViewStates() {
int numChildren = mHostView.getChildCount();
for (int i = 0; i < numChildren; i++) {
@@ -364,7 +373,6 @@
final float stackHeight = ambientState.getStackHeight() - shelfHeight - scrimPadding;
final float stackEndHeight = ambientState.getStackEndHeight() - shelfHeight - scrimPadding;
-
return stackHeight / stackEndHeight;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 917f132..4ce33b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -468,7 +468,6 @@
private boolean mPulsing;
private boolean mUserSetupComplete;
- private int mQsNotificationTopPadding;
private boolean mHideIconsDuringLaunchAnimation = true;
private int mStackScrollerMeasuringPass;
private ArrayList<Consumer<ExpandableNotificationRow>>
@@ -2360,7 +2359,8 @@
private void updateQsExpansion() {
if (mQs == null) return;
float qsExpansionFraction = computeQsExpansionFraction();
- mQs.setQsExpansion(qsExpansionFraction, getExpandedFraction(), getHeaderTranslation());
+ mQs.setQsExpansion(qsExpansionFraction, getExpandedFraction(), getHeaderTranslation(),
+ mNotificationStackScrollLayoutController.getNotificationSquishinessFraction());
mMediaHierarchyManager.setQsExpansion(qsExpansionFraction);
int qsPanelBottomY = calculateQsBottomPosition(qsExpansionFraction);
mScrimController.setQsPosition(qsExpansionFraction, qsPanelBottomY);
@@ -2522,7 +2522,7 @@
// qsTranslation should only be positive during pulse expansion because it's
// already translating in from the top
qsTranslation = Math.max(0, (top - mQs.getHeader().getHeight()) / 2.0f);
- } else {
+ } else if (!mShouldUseSplitNotificationShade) {
qsTranslation = (top - mQs.getHeader().getHeight()) * QS_PARALLAX_AMOUNT;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
index a7ecd06..8732891 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
@@ -41,15 +41,28 @@
private val iconManager: StatusBarIconController.IconManager
private val qsCarrierGroupController: QSCarrierGroupController
private var visible = false
+ set(value) {
+ if (field == value) {
+ return
+ }
+ field = value
+ updateListeners()
+ }
var shadeExpanded = false
set(value) {
+ if (field == value) {
+ return
+ }
field = value
updateVisibility()
}
var splitShadeMode = false
set(value) {
+ if (field == value) {
+ return
+ }
field = value
updateVisibility()
}
@@ -78,15 +91,20 @@
}
private fun updateVisibility() {
- val shouldBeVisible = shadeExpanded && splitShadeMode
- if (visible != shouldBeVisible) {
- visible = shouldBeVisible
- statusBar.visibility = if (shouldBeVisible) View.VISIBLE else View.GONE
- updateListeners(shouldBeVisible)
+ val visibility = if (!splitShadeMode) {
+ View.GONE
+ } else if (shadeExpanded) {
+ View.VISIBLE
+ } else {
+ View.INVISIBLE
+ }
+ if (statusBar.visibility != visibility) {
+ statusBar.visibility = visibility
+ visible = visibility == View.VISIBLE
}
}
- private fun updateListeners(visible: Boolean) {
+ private fun updateListeners() {
qsCarrierGroupController.setListening(visible)
if (visible) {
statusBarIconController.addIconGroup(iconManager)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 0e75a45..bce6ccc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -207,6 +207,7 @@
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
@@ -535,6 +536,7 @@
private final int[] mAbsPos = new int[2];
+ private final NotifShadeEventSource mNotifShadeEventSource;
protected final NotificationEntryManager mEntryManager;
private final NotificationGutsManager mGutsManager;
private final NotificationLogger mNotificationLogger;
@@ -718,6 +720,7 @@
FalsingManager falsingManager,
FalsingCollector falsingCollector,
BroadcastDispatcher broadcastDispatcher,
+ NotifShadeEventSource notifShadeEventSource,
NotificationEntryManager notificationEntryManager,
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
@@ -823,6 +826,7 @@
mFalsingCollector = falsingCollector;
mFalsingManager = falsingManager;
mBroadcastDispatcher = broadcastDispatcher;
+ mNotifShadeEventSource = notifShadeEventSource;
mEntryManager = notificationEntryManager;
mGutsManager = notificationGutsManager;
mNotificationLogger = notificationLogger;
@@ -1506,6 +1510,7 @@
mDynamicPrivacyController,
mKeyguardStateController,
mKeyguardIndicationController,
+ mFeatureFlags,
this /* statusBar */,
mShadeController,
mLockscreenShadeTransitionController,
@@ -1513,6 +1518,7 @@
mViewHierarchyManager,
mLockscreenUserManager,
mStatusBarStateController,
+ mNotifShadeEventSource,
mEntryManager,
mMediaManager,
mGutsManager,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index c655964..cf9b2c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -17,9 +17,7 @@
import static com.android.systemui.statusbar.phone.StatusBar.CLOSE_PANEL_WHEN_EMPTIED;
import static com.android.systemui.statusbar.phone.StatusBar.DEBUG;
import static com.android.systemui.statusbar.phone.StatusBar.MULTIUSER_DEBUG;
-import static com.android.systemui.statusbar.phone.StatusBar.SPEW;
-import android.annotation.Nullable;
import android.app.KeyguardManager;
import android.content.Context;
import android.os.RemoteException;
@@ -36,7 +34,6 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.widget.MessagingGroup;
import com.android.internal.widget.MessagingMessage;
import com.android.keyguard.KeyguardUpdateMonitor;
@@ -44,6 +41,7 @@
import com.android.systemui.ForegroundServiceNotificationListener;
import com.android.systemui.InitController;
import com.android.systemui.R;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.statusbar.CommandQueue;
@@ -59,10 +57,10 @@
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.AboveShelfObserver;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
+import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
@@ -88,6 +86,7 @@
private final NotificationViewHierarchyManager mViewHierarchyManager;
private final NotificationLockscreenUserManager mLockscreenUserManager;
private final SysuiStatusBarStateController mStatusBarStateController;
+ private final NotifShadeEventSource mNotifShadeEventSource;
private final NotificationEntryManager mEntryManager;
private final NotificationMediaManager mMediaManager;
private final NotificationGutsManager mGutsManager;
@@ -100,6 +99,7 @@
private final DozeScrimController mDozeScrimController;
private final ScrimController mScrimController;
private final KeyguardIndicationController mKeyguardIndicationController;
+ private final FeatureFlags mFeatureFlags;
private final StatusBar mStatusBar;
private final ShadeController mShadeController;
private final LockscreenShadeTransitionController mShadeTransitionController;
@@ -127,6 +127,7 @@
DynamicPrivacyController dynamicPrivacyController,
KeyguardStateController keyguardStateController,
KeyguardIndicationController keyguardIndicationController,
+ FeatureFlags featureFlags,
StatusBar statusBar,
ShadeController shadeController,
LockscreenShadeTransitionController shadeTransitionController,
@@ -134,6 +135,7 @@
NotificationViewHierarchyManager notificationViewHierarchyManager,
NotificationLockscreenUserManager lockscreenUserManager,
SysuiStatusBarStateController sysuiStatusBarStateController,
+ NotifShadeEventSource notifShadeEventSource,
NotificationEntryManager notificationEntryManager,
NotificationMediaManager notificationMediaManager,
NotificationGutsManager notificationGutsManager,
@@ -148,6 +150,7 @@
mHeadsUpManager = headsUp;
mDynamicPrivacyController = dynamicPrivacyController;
mKeyguardIndicationController = keyguardIndicationController;
+ mFeatureFlags = featureFlags;
// TODO: use KeyguardStateController#isOccluded to remove this dependency
mStatusBar = statusBar;
mShadeController = shadeController;
@@ -156,6 +159,7 @@
mViewHierarchyManager = notificationViewHierarchyManager;
mLockscreenUserManager = lockscreenUserManager;
mStatusBarStateController = sysuiStatusBarStateController;
+ mNotifShadeEventSource = notifShadeEventSource;
mEntryManager = notificationEntryManager;
mMediaManager = notificationMediaManager;
mGutsManager = notificationGutsManager;
@@ -186,30 +190,18 @@
mNotificationPanel.createRemoteInputDelegate());
initController.addPostInitTask(() -> {
- NotificationEntryListener notificationEntryListener = new NotificationEntryListener() {
- @Override
- public void onEntryRemoved(
- @Nullable NotificationEntry entry,
- NotificationVisibility visibility,
- boolean removedByUser,
- int reason) {
- StatusBarNotificationPresenter.this.onNotificationRemoved(
- entry.getKey(), entry.getSbn(), reason);
- if (removedByUser) {
- maybeEndAmbientPulse();
- }
- }
- };
-
mKeyguardIndicationController.init();
mViewHierarchyManager.setUpWithPresenter(this,
stackScrollerController.getNotificationListContainer());
- mEntryManager.setUpWithPresenter(this);
- mEntryManager.addNotificationEntryListener(notificationEntryListener);
- mEntryManager.addNotificationLifetimeExtender(mHeadsUpManager);
- mEntryManager.addNotificationLifetimeExtender(mGutsManager);
- mEntryManager.addNotificationLifetimeExtenders(
- remoteInputManager.getLifetimeExtenders());
+ mNotifShadeEventSource.setShadeEmptiedCallback(this::maybeClosePanelForShadeEmptied);
+ mNotifShadeEventSource.setNotifRemovedByUserCallback(this::maybeEndAmbientPulse);
+ if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
+ mEntryManager.setUpWithPresenter(this);
+ mEntryManager.addNotificationLifetimeExtender(mHeadsUpManager);
+ mEntryManager.addNotificationLifetimeExtender(mGutsManager);
+ mEntryManager.addNotificationLifetimeExtenders(
+ remoteInputManager.getLifetimeExtenders());
+ }
notificationInterruptStateProvider.addSuppressor(mInterruptSuppressor);
mLockscreenUserManager.setUpWithPresenter(this);
mMediaManager.setUpWithPresenter(this);
@@ -226,8 +218,21 @@
configurationController.addCallback(this);
}
+ /** Called when the shade has been emptied to attempt to close the shade */
+ private void maybeClosePanelForShadeEmptied() {
+ if (CLOSE_PANEL_WHEN_EMPTIED
+ && !mNotificationPanel.isTracking()
+ && !mNotificationPanel.isQsExpanded()
+ && mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED
+ && !isCollapsing()) {
+ mStatusBarStateController.setState(StatusBarState.KEYGUARD);
+ }
+ }
+
@Override
public void onDensityOrFontScaleChanged() {
+ // TODO(b/145659174): Remove legacy pipeline code
+ if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) return;
MessagingMessage.dropCache();
MessagingGroup.dropCache();
if (!mKeyguardUpdateMonitor.isSwitchingUser()) {
@@ -239,8 +244,10 @@
@Override
public void onUiModeChanged() {
+ // TODO(b/145659174): Remove legacy pipeline code
+ if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) return;
if (!mKeyguardUpdateMonitor.isSwitchingUser()) {
- updateNotificationOnUiModeChanged();
+ updateNotificationsOnUiModeChanged();
} else {
mDispatchUiModeChangeOnUserSwitched = true;
}
@@ -251,7 +258,9 @@
onDensityOrFontScaleChanged();
}
- private void updateNotificationOnUiModeChanged() {
+ private void updateNotificationsOnUiModeChanged() {
+ // TODO(b/145659174): Remove legacy pipeline code
+ if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) return;
List<NotificationEntry> userNotifications =
mEntryManager.getActiveNotificationsForCurrentUser();
for (int i = 0; i < userNotifications.size(); i++) {
@@ -264,6 +273,8 @@
}
private void updateNotificationsOnDensityOrFontScaleChanged() {
+ // TODO(b/145659174): Remove legacy pipeline code
+ if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) return;
List<NotificationEntry> userNotifications =
mEntryManager.getActiveNotificationsForCurrentUser();
for (int i = 0; i < userNotifications.size(); i++) {
@@ -276,6 +287,7 @@
}
}
+
@Override
public boolean isCollapsing() {
return mNotificationPanel.isCollapsing()
@@ -308,21 +320,6 @@
mNotificationPanel.updateNotificationViews(reason);
}
- private void onNotificationRemoved(String key, StatusBarNotification old, int reason) {
- if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
-
- if (old != null && CLOSE_PANEL_WHEN_EMPTIED && !hasActiveNotifications()
- && !mNotificationPanel.isTracking() && !mNotificationPanel.isQsExpanded()
- && mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED
- && !isCollapsing()) {
- mStatusBarStateController.setState(StatusBarState.KEYGUARD);
- }
- }
-
- public boolean hasActiveNotifications() {
- return mEntryManager.hasActiveNotifications();
- }
-
@Override
public void onUserSwitched(int newUserId) {
// Begin old BaseStatusBar.userSwitched
@@ -335,7 +332,7 @@
mReinflateNotificationsOnUserSwitched = false;
}
if (mDispatchUiModeChangeOnUserSwitched) {
- updateNotificationOnUiModeChanged();
+ updateNotificationsOnUiModeChanged();
mDispatchUiModeChangeOnUserSwitched = false;
}
updateNotificationViews("user switched");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index c452a48..2681d5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -68,6 +68,7 @@
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
@@ -157,6 +158,7 @@
FalsingManager falsingManager,
FalsingCollector falsingCollector,
BroadcastDispatcher broadcastDispatcher,
+ NotifShadeEventSource notifShadeEventSource,
NotificationEntryManager notificationEntryManager,
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
@@ -260,6 +262,7 @@
falsingManager,
falsingCollector,
broadcastDispatcher,
+ notifShadeEventSource,
notificationEntryManager,
notificationGutsManager,
notificationLogger,
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index fd9783a..0a33930 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -463,13 +463,13 @@
@ShellMainThread ShellExecutor mainExecutor,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, Transitions transitions,
- TransactionPool transactionPool,
+ TransactionPool transactionPool, IconProvider iconProvider,
Provider<Optional<StageTaskUnfoldController>> stageTaskUnfoldControllerProvider) {
if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) {
return Optional.of(new SplitScreenController(shellTaskOrganizer, syncQueue, context,
rootTaskDisplayAreaOrganizer, mainExecutor, displayImeController,
- displayInsetsController, transitions,
- transactionPool, stageTaskUnfoldControllerProvider));
+ displayInsetsController, transitions, transactionPool, iconProvider,
+ stageTaskUnfoldControllerProvider));
} else {
return Optional.empty();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt
new file mode 100644
index 0000000..3059aa1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt
@@ -0,0 +1,62 @@
+package com.android.systemui.qs
+
+import android.testing.AndroidTestingRunner
+import android.view.ViewGroup
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.tileimpl.QSTileViewImpl
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class QSSquishinessControllerTest : SysuiTestCase() {
+
+ @Mock private lateinit var qsTileHost: QSTileHost
+ @Mock private lateinit var qqsFooterActionsView: FooterActionsView
+ @Mock private lateinit var qqsFooterActionsViewLP: ViewGroup.MarginLayoutParams
+ @Mock private lateinit var qsAnimator: QSAnimator
+ @Mock private lateinit var quickQsPanelController: QuickQSPanelController
+ @Mock private lateinit var qstileView: QSTileViewImpl
+ @Mock private lateinit var qstile: QSTile
+ @Mock private lateinit var tileLayout: TileLayout
+
+ @JvmField @Rule val mockitoRule = MockitoJUnit.rule()
+
+ private lateinit var qsSquishinessController: QSSquishinessController
+
+ @Before
+ fun setup() {
+ qsSquishinessController = QSSquishinessController(qsTileHost, qqsFooterActionsView,
+ qsAnimator, quickQsPanelController)
+ `when`(qsTileHost.tiles).thenReturn(mutableListOf(qstile))
+ `when`(quickQsPanelController.getTileView(any())).thenReturn(qstileView)
+ `when`(quickQsPanelController.tileLayout).thenReturn(tileLayout)
+ `when`(qqsFooterActionsView.layoutParams).thenReturn(qqsFooterActionsViewLP)
+ }
+
+ @Test
+ fun setSquishiness_requestsAnimatorUpdate() {
+ qsSquishinessController.squishiness = 0.5f
+ verify(qsAnimator, never()).requestAnimatorUpdate()
+
+ qsSquishinessController.squishiness = 0f
+ verify(qsAnimator).requestAnimatorUpdate()
+ }
+
+ @Test
+ fun setSquishiness_updatesTiles() {
+ qsSquishinessController.squishiness = 0.5f
+ verify(qstileView).squishinessFraction = 0.5f
+ verify(tileLayout).setSquishinessFraction(0.5f)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt
new file mode 100644
index 0000000..5915cd7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.notification.collection.coordinator
+
+import android.service.notification.NotificationListenerService.REASON_APP_CANCEL
+import android.service.notification.NotificationListenerService.REASON_CANCEL
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import com.android.systemui.util.mockito.argumentCaptor
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations.initMocks
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class ShadeEventCoordinatorTest : SysuiTestCase() {
+ private lateinit var coordinator: ShadeEventCoordinator
+ private lateinit var notifCollectionListener: NotifCollectionListener
+ private lateinit var onBeforeRenderListListener: OnBeforeRenderListListener
+
+ private lateinit var entry1: NotificationEntry
+ private lateinit var entry2: NotificationEntry
+
+ @Mock private lateinit var pipeline: NotifPipeline
+ @Mock private lateinit var logger: ShadeEventCoordinatorLogger
+ @Mock private lateinit var notifRemovedByUserCallback: Runnable
+ @Mock private lateinit var shadeEmptiedCallback: Runnable
+
+ @Before
+ fun setUp() {
+ initMocks(this)
+ coordinator = ShadeEventCoordinator(logger)
+ coordinator.attach(pipeline)
+ notifCollectionListener = argumentCaptor<NotifCollectionListener>().let {
+ verify(pipeline).addCollectionListener(it.capture())
+ it.value!!
+ }
+ onBeforeRenderListListener = argumentCaptor<OnBeforeRenderListListener>().let {
+ verify(pipeline).addOnBeforeRenderListListener(it.capture())
+ it.value!!
+ }
+ coordinator.setNotifRemovedByUserCallback(notifRemovedByUserCallback)
+ coordinator.setShadeEmptiedCallback(shadeEmptiedCallback)
+ entry1 = NotificationEntryBuilder().setId(1).build()
+ entry2 = NotificationEntryBuilder().setId(2).build()
+ }
+
+ @Test
+ fun testUserCancelLastNotification() {
+ notifCollectionListener.onEntryRemoved(entry1, REASON_CANCEL)
+ verify(shadeEmptiedCallback, never()).run()
+ verify(notifRemovedByUserCallback, never()).run()
+ onBeforeRenderListListener.onBeforeRenderList(listOf())
+ verify(shadeEmptiedCallback).run()
+ verify(notifRemovedByUserCallback).run()
+ }
+
+ @Test
+ fun testAppCancelLastNotification() {
+ notifCollectionListener.onEntryRemoved(entry1, REASON_APP_CANCEL)
+ onBeforeRenderListListener.onBeforeRenderList(listOf())
+ verify(shadeEmptiedCallback).run()
+ verify(notifRemovedByUserCallback, never()).run()
+ }
+
+ @Test
+ fun testUserCancelOneOfTwoNotifications() {
+ notifCollectionListener.onEntryRemoved(entry1, REASON_CANCEL)
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry2))
+ verify(shadeEmptiedCallback, never()).run()
+ verify(notifRemovedByUserCallback).run()
+ }
+
+ @Test
+ fun testAppCancelOneOfTwoNotifications() {
+ notifCollectionListener.onEntryRemoved(entry1, REASON_APP_CANCEL)
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry2))
+ verify(shadeEmptiedCallback, never()).run()
+ verify(notifRemovedByUserCallback, never()).run()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt
new file mode 100644
index 0000000..a9e8164
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt
@@ -0,0 +1,87 @@
+package com.android.systemui.statusbar.phone
+
+import android.content.Context
+import android.content.res.Resources
+import android.test.suitebuilder.annotation.SmallTest
+import android.view.View
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.ShadeInterpolation
+import com.android.systemui.battery.BatteryMeterView
+import com.android.systemui.battery.BatteryMeterViewController
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.qs.carrier.QSCarrierGroupController
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+
+@SmallTest
+class SplitShadeHeaderControllerTest : SysuiTestCase() {
+
+ @Mock private lateinit var view: View
+ @Mock private lateinit var statusIcons: StatusIconContainer
+ @Mock private lateinit var statusBarIconController: StatusBarIconController
+ @Mock private lateinit var qsCarrierGroupController: QSCarrierGroupController
+ @Mock private lateinit var qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder
+ @Mock private lateinit var featureFlags: FeatureFlags
+ @Mock private lateinit var batteryMeterView: BatteryMeterView
+ @Mock private lateinit var batteryMeterViewController: BatteryMeterViewController
+ @Mock private lateinit var resources: Resources
+ @Mock private lateinit var context: Context
+ @JvmField @Rule val mockitoRule = MockitoJUnit.rule()
+ var viewVisibility = View.GONE
+
+ private lateinit var splitShadeHeaderController: SplitShadeHeaderController
+
+ @Before
+ fun setup() {
+ whenever<BatteryMeterView>(view.findViewById(R.id.batteryRemainingIcon))
+ .thenReturn(batteryMeterView)
+ whenever<StatusIconContainer>(view.findViewById(R.id.statusIcons)).thenReturn(statusIcons)
+ whenever(statusIcons.context).thenReturn(context)
+ whenever(context.resources).thenReturn(resources)
+ whenever(qsCarrierGroupControllerBuilder.setQSCarrierGroup(any()))
+ .thenReturn(qsCarrierGroupControllerBuilder)
+ whenever(qsCarrierGroupControllerBuilder.build()).thenReturn(qsCarrierGroupController)
+ whenever(view.setVisibility(anyInt())).then {
+ viewVisibility = it.arguments[0] as Int
+ null
+ }
+ whenever(view.visibility).thenAnswer { _ -> viewVisibility }
+ splitShadeHeaderController = SplitShadeHeaderController(view, statusBarIconController,
+ qsCarrierGroupControllerBuilder, featureFlags, batteryMeterViewController)
+ }
+
+ @Test
+ fun setVisible_onlyInSplitShade() {
+ splitShadeHeaderController.splitShadeMode = true
+ splitShadeHeaderController.shadeExpanded = true
+ assertThat(viewVisibility).isEqualTo(View.VISIBLE)
+
+ splitShadeHeaderController.splitShadeMode = false
+ assertThat(viewVisibility).isEqualTo(View.GONE)
+ }
+
+ @Test
+ fun updateListeners_registersWhenVisible() {
+ splitShadeHeaderController.splitShadeMode = true
+ splitShadeHeaderController.shadeExpanded = true
+ verify(qsCarrierGroupController).setListening(true)
+ verify(statusBarIconController).addIconGroup(any())
+ }
+
+ @Test
+ fun shadeExpandedFraction_updatesAlpha() {
+ splitShadeHeaderController.splitShadeMode = true
+ splitShadeHeaderController.shadeExpanded = true
+ splitShadeHeaderController.shadeExpandedFraction = 0.5f
+ verify(view).setAlpha(ShadeInterpolation.getContentAlpha(0.5f))
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index c80c072..4e6b0a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -37,6 +37,7 @@
import com.android.systemui.ForegroundServiceNotificationListener;
import com.android.systemui.InitController;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -51,6 +52,7 @@
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
@@ -109,12 +111,15 @@
mock(DozeScrimController.class), mock(ScrimController.class),
mock(NotificationShadeWindowController.class), mock(DynamicPrivacyController.class),
mock(KeyguardStateController.class),
- mock(KeyguardIndicationController.class), mStatusBar,
+ mock(KeyguardIndicationController.class),
+ mock(FeatureFlags.class),
+ mStatusBar,
mock(ShadeControllerImpl.class), mock(LockscreenShadeTransitionController.class),
mCommandQueue,
mock(NotificationViewHierarchyManager.class),
mock(NotificationLockscreenUserManager.class),
mock(SysuiStatusBarStateController.class),
+ mock(NotifShadeEventSource.class),
mock(NotificationEntryManager.class),
mock(NotificationMediaManager.class),
mock(NotificationGutsManager.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 943d3c7..ca8b6c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -121,6 +121,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl;
@@ -210,6 +211,7 @@
@Mock private NotificationShadeWindowView mNotificationShadeWindowView;
@Mock private BroadcastDispatcher mBroadcastDispatcher;
@Mock private AssistManager mAssistManager;
+ @Mock private NotifShadeEventSource mNotifShadeEventSource;
@Mock private NotificationEntryManager mNotificationEntryManager;
@Mock private NotificationGutsManager mNotificationGutsManager;
@Mock private NotificationMediaManager mNotificationMediaManager;
@@ -377,6 +379,7 @@
new FalsingManagerFake(),
new FalsingCollectorFake(),
mBroadcastDispatcher,
+ mNotifShadeEventSource,
mNotificationEntryManager,
mNotificationGutsManager,
notificationLogger,
diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
index 4946ad4..1af8ad3 100644
--- a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
+++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
@@ -187,7 +187,7 @@
@NonNull IPredictionCallback callback) {
final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
if (sessionInfo == null) return;
- final boolean serviceExists = resolveService(sessionId, false,
+ final boolean serviceExists = resolveService(sessionId, true,
sessionInfo.mUsesPeopleService,
s -> s.registerPredictionUpdates(sessionId, callback));
if (serviceExists) {
diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
index 2a0adeb..2e8fb47 100644
--- a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
+++ b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
@@ -32,7 +32,7 @@
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.companion.Association;
+import android.companion.AssociationInfo;
import android.companion.AssociationRequest;
import android.companion.CompanionDeviceManager;
import android.companion.ICompanionDeviceDiscoveryService;
@@ -226,7 +226,7 @@
// Throttle frequent associations
long now = System.currentTimeMillis();
- Set<Association> recentAssociations = filter(
+ Set<AssociationInfo> recentAssociations = filter(
mService.getAllAssociations(userId, packageName),
a -> now - a.getTimeApprovedMs() < ASSOCIATE_WITHOUT_PROMPT_WINDOW_MS);
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index ad4c35c..cdce3e6 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -56,7 +56,7 @@
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
-import android.companion.Association;
+import android.companion.AssociationInfo;
import android.companion.AssociationRequest;
import android.companion.DeviceId;
import android.companion.DeviceNotAssociatedException;
@@ -182,7 +182,7 @@
/** Maps a {@link UserIdInt} to a set of associations for the user. */
@GuardedBy("mLock")
- private final SparseArray<Set<Association>> mCachedAssociations = new SparseArray<>();
+ private final SparseArray<Set<AssociationInfo>> mCachedAssociations = new SparseArray<>();
/**
* A structure that consist of two nested maps, and effectively maps (userId + packageName) to
* a list of IDs that have been previously assigned to associations for that package.
@@ -270,7 +270,7 @@
@Override
public void onUserUnlocking(@NonNull TargetUser user) {
int userHandle = user.getUserIdentifier();
- Set<Association> associations = getAllAssociations(userHandle);
+ Set<AssociationInfo> associations = getAllAssociations(userHandle);
if (associations == null || associations.isEmpty()) {
return;
}
@@ -293,11 +293,11 @@
}
try {
- Set<Association> associations = getAllAssociations(userId);
+ Set<AssociationInfo> associations = getAllAssociations(userId);
if (associations == null) {
continue;
}
- for (Association a : associations) {
+ for (AssociationInfo a : associations) {
try {
int uid = pm.getPackageUidAsUser(a.getPackageName(), userId);
exemptFromAutoRevoke(a.getPackageName(), uid);
@@ -355,7 +355,7 @@
}
@Override
- public List<Association> getAssociationsForUser(int userId) {
+ public List<AssociationInfo> getAssociationsForUser(int userId) {
if (!callerCanManageCompanionDevices()) {
throw new SecurityException("Caller must hold "
+ android.Manifest.permission.MANAGE_COMPANION_DEVICES);
@@ -467,7 +467,7 @@
checkCallerIsSystemOr(packageName);
int userId = getCallingUserId();
- Set<Association> deviceAssociations = filter(
+ Set<AssociationInfo> deviceAssociations = filter(
getAllAssociations(userId, packageName),
association -> deviceAddress.equals(association.getDeviceMacAddress()));
@@ -619,7 +619,7 @@
void createAssociationInternal(
int userId, String deviceMacAddress, String packageName, String deviceProfile) {
- final Association association = new Association(
+ final AssociationInfo association = new AssociationInfo(
getNewAssociationIdForPackage(userId, packageName),
userId,
packageName,
@@ -648,7 +648,7 @@
// First: collect all IDs currently in use for this user's Associations.
final SparseBooleanArray usedIds = new SparseBooleanArray();
- for (Association it : getAllAssociations(userId)) {
+ for (AssociationInfo it : getAllAssociations(userId)) {
usedIds.put(it.getAssociationId(), true);
}
@@ -698,7 +698,7 @@
}
}
- void onAssociationPreRemove(Association association) {
+ void onAssociationPreRemove(AssociationInfo association) {
if (association.isNotifyOnDeviceNearby()) {
mCompanionDevicePresenceController.unbindDevicePresenceListener(
association.getPackageName(), association.getUserId());
@@ -706,7 +706,7 @@
String deviceProfile = association.getDeviceProfile();
if (deviceProfile != null) {
- Association otherAssociationWithDeviceProfile = find(
+ AssociationInfo otherAssociationWithDeviceProfile = find(
getAllAssociations(association.getUserId()),
a -> !a.equals(association) && deviceProfile.equals(a.getDeviceProfile()));
if (otherAssociationWithDeviceProfile != null) {
@@ -737,7 +737,7 @@
}
}
- private void updateSpecialAccessPermissionForAssociatedPackage(Association association) {
+ private void updateSpecialAccessPermissionForAssociatedPackage(AssociationInfo association) {
PackageInfo packageInfo = getPackageInfo(
association.getPackageName(),
association.getUserId());
@@ -751,7 +751,7 @@
}
private void updateSpecialAccessPermissionAsSystem(
- Association association, PackageInfo packageInfo) {
+ AssociationInfo association, PackageInfo packageInfo) {
if (containsEither(packageInfo.requestedPermissions,
android.Manifest.permission.RUN_IN_BACKGROUND,
android.Manifest.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND)) {
@@ -816,20 +816,20 @@
}, getContext(), packageName, userId).recycleOnUse());
}
- private void recordAssociation(Association association, int userId) {
+ private void recordAssociation(AssociationInfo association, int userId) {
Slog.i(LOG_TAG, "recordAssociation(" + association + ")");
updateAssociations(associations -> add(associations, association), userId);
}
- private void updateAssociations(Function<Set<Association>, Set<Association>> update,
+ private void updateAssociations(Function<Set<AssociationInfo>, Set<AssociationInfo>> update,
int userId) {
synchronized (mLock) {
if (DEBUG) Slog.d(LOG_TAG, "Updating Associations set...");
- final Set<Association> prevAssociations = getAllAssociations(userId);
+ final Set<AssociationInfo> prevAssociations = getAllAssociations(userId);
if (DEBUG) Slog.d(LOG_TAG, " > Before : " + prevAssociations + "...");
- final Set<Association> updatedAssociations = update.apply(
+ final Set<AssociationInfo> updatedAssociations = update.apply(
new ArraySet<>(prevAssociations));
if (DEBUG) Slog.d(LOG_TAG, " > After: " + updatedAssociations);
@@ -846,9 +846,9 @@
}
}
- private void updateAtm(int userId, Set<Association> associations) {
+ private void updateAtm(int userId, Set<AssociationInfo> associations) {
final Set<Integer> companionAppUids = new ArraySet<>();
- for (Association association : associations) {
+ for (AssociationInfo association : associations) {
final int uid = mPackageManagerInternal.getPackageUid(association.getPackageName(),
0, userId);
if (uid >= 0) {
@@ -864,7 +864,7 @@
}
}
- @NonNull Set<Association> getAllAssociations(int userId) {
+ @NonNull Set<AssociationInfo> getAllAssociations(int userId) {
synchronized (mLock) {
readPersistedStateForUserIfNeededLocked(userId);
// This returns non-null, because the readAssociationsInfoForUserIfNeededLocked() method
@@ -879,7 +879,7 @@
Slog.i(LOG_TAG, "Reading state for user " + userId + " from the disk");
- final Set<Association> associations = new ArraySet<>();
+ final Set<AssociationInfo> associations = new ArraySet<>();
final Map<String, Set<Integer>> previouslyUsedIds = new ArrayMap<>();
mPersistentDataStore.readStateForUser(userId, associations, previouslyUsedIds);
@@ -901,17 +901,17 @@
}
}
- Set<Association> getAllAssociations(int userId, @Nullable String packageFilter) {
+ Set<AssociationInfo> getAllAssociations(int userId, @Nullable String packageFilter) {
return filter(
getAllAssociations(userId),
// Null filter == get all associations
a -> packageFilter == null || Objects.equals(packageFilter, a.getPackageName()));
}
- private Set<Association> getAllAssociations() {
+ private Set<AssociationInfo> getAllAssociations() {
final long identity = Binder.clearCallingIdentity();
try {
- ArraySet<Association> result = new ArraySet<>();
+ ArraySet<AssociationInfo> result = new ArraySet<>();
for (UserInfo user : mUserManager.getAliveUsers()) {
result.addAll(getAllAssociations(user.id));
}
@@ -921,7 +921,7 @@
}
}
- private Set<Association> getAllAssociations(
+ private Set<AssociationInfo> getAllAssociations(
int userId, @Nullable String packageFilter, @Nullable String addressFilter) {
return filter(
getAllAssociations(userId),
@@ -937,7 +937,7 @@
mCurrentlyConnectedDevices.add(address);
for (UserInfo user : getAllUsers()) {
- for (Association association : getAllAssociations(user.id)) {
+ for (AssociationInfo association : getAllAssociations(user.id)) {
if (Objects.equals(address, association.getDeviceMacAddress())) {
if (association.getDeviceProfile() != null) {
Slog.i(LOG_TAG, "Granting role " + association.getDeviceProfile()
@@ -952,7 +952,7 @@
onDeviceNearby(address);
}
- private void grantDeviceProfile(Association association) {
+ private void grantDeviceProfile(AssociationInfo association) {
Slog.i(LOG_TAG, "grantDeviceProfile(association = " + association + ")");
if (association.getDeviceProfile() != null) {
@@ -1059,7 +1059,7 @@
Date lastNearby = mDevicesLastNearby.valueAt(i);
if (isDeviceDisappeared(lastNearby)) {
- for (Association association : getAllAssociations(address)) {
+ for (AssociationInfo association : getAllAssociations(address)) {
if (association.isNotifyOnDeviceNearby()) {
mCompanionDevicePresenceController.unbindDevicePresenceListener(
association.getPackageName(), association.getUserId());
@@ -1101,12 +1101,12 @@
}
}
- private Set<Association> getAllAssociations(String deviceAddress) {
+ private Set<AssociationInfo> getAllAssociations(String deviceAddress) {
List<UserInfo> aliveUsers = mUserManager.getAliveUsers();
- Set<Association> result = new ArraySet<>();
+ Set<AssociationInfo> result = new ArraySet<>();
for (int i = 0, size = aliveUsers.size(); i < size; i++) {
UserInfo user = aliveUsers.get(i);
- for (Association association : getAllAssociations(user.id)) {
+ for (AssociationInfo association : getAllAssociations(user.id)) {
if (Objects.equals(association.getDeviceMacAddress(), deviceAddress)) {
result.add(association);
}
@@ -1130,7 +1130,7 @@
|| timestamp.getTime() - oldTimestamp.getTime() >= DEVICE_DISAPPEARED_TIMEOUT_MS;
if (justAppeared) {
Slog.i(LOG_TAG, "onDeviceNearby(justAppeared, address = " + address + ")");
- for (Association association : getAllAssociations(address)) {
+ for (AssociationInfo association : getAllAssociations(address)) {
if (association.isNotifyOnDeviceNearby()) {
mCompanionDevicePresenceController.onDeviceNotifyAppeared(association,
getContext(), mMainHandler);
@@ -1143,7 +1143,7 @@
Slog.i(LOG_TAG, "onDeviceDisappeared(address = " + address + ")");
boolean hasDeviceListeners = false;
- for (Association association : getAllAssociations(address)) {
+ for (AssociationInfo association : getAllAssociations(address)) {
if (association.isNotifyOnDeviceNearby()) {
mCompanionDevicePresenceController.onDeviceNotifyDisappeared(
association, getContext(), mMainHandler);
@@ -1212,7 +1212,7 @@
private List<ScanFilter> getBleScanFilters() {
ArrayList<ScanFilter> result = new ArrayList<>();
ArraySet<String> addressesSeen = new ArraySet<>();
- for (Association association : getAllAssociations()) {
+ for (AssociationInfo association : getAllAssociations()) {
String address = association.getDeviceMacAddress();
if (addressesSeen.contains(address)) {
continue;
diff --git a/services/companion/java/com/android/server/companion/CompanionDevicePresenceController.java b/services/companion/java/com/android/server/companion/CompanionDevicePresenceController.java
index 328a8b3..a79db2c 100644
--- a/services/companion/java/com/android/server/companion/CompanionDevicePresenceController.java
+++ b/services/companion/java/com/android/server/companion/CompanionDevicePresenceController.java
@@ -22,7 +22,7 @@
import static com.android.internal.util.CollectionUtils.filter;
import android.annotation.NonNull;
-import android.companion.Association;
+import android.companion.AssociationInfo;
import android.companion.CompanionDeviceService;
import android.companion.ICompanionDeviceService;
import android.content.ComponentName;
@@ -60,7 +60,7 @@
};
}
- void onDeviceNotifyAppeared(Association association, Context context, Handler handler) {
+ void onDeviceNotifyAppeared(AssociationInfo association, Context context, Handler handler) {
ServiceConnector<ICompanionDeviceService> primaryConnector =
getPrimaryServiceConnector(association, context, handler);
if (primaryConnector != null) {
@@ -71,7 +71,7 @@
}
}
- void onDeviceNotifyDisappeared(Association association, Context context, Handler handler) {
+ void onDeviceNotifyDisappeared(AssociationInfo association, Context context, Handler handler) {
ServiceConnector<ICompanionDeviceService> primaryConnector =
getPrimaryServiceConnector(association, context, handler);
if (primaryConnector != null) {
@@ -94,7 +94,7 @@
}
private ServiceConnector<ICompanionDeviceService> getPrimaryServiceConnector(
- Association association, Context context, Handler handler) {
+ AssociationInfo association, Context context, Handler handler) {
for (BoundService boundService: getDeviceListenerServiceConnector(association, context,
handler)) {
if (boundService.mIsPrimary) {
@@ -104,15 +104,15 @@
return null;
}
- private List<BoundService> getDeviceListenerServiceConnector(Association a, Context context,
+ private List<BoundService> getDeviceListenerServiceConnector(AssociationInfo a, Context context,
Handler handler) {
return mBoundServices.forUser(a.getUserId()).computeIfAbsent(
a.getPackageName(),
pkg -> createDeviceListenerServiceConnector(a, context, handler));
}
- private List<BoundService> createDeviceListenerServiceConnector(Association a, Context context,
- Handler handler) {
+ private List<BoundService> createDeviceListenerServiceConnector(AssociationInfo a,
+ Context context, Handler handler) {
List<ResolveInfo> resolveInfos = context
.getPackageManager()
.queryIntentServicesAsUser(new Intent(CompanionDeviceService.SERVICE_INTERFACE),
@@ -161,7 +161,7 @@
}
private boolean validatePackageInfo(List<ResolveInfo> packageResolveInfos,
- Association association) {
+ AssociationInfo association) {
if (packageResolveInfos.size() == 0 || packageResolveInfos.size() > 5) {
Slog.e(LOG_TAG, "Device presence listener package must have at least one and not "
+ "more than five CompanionDeviceService(s) declared. But "
diff --git a/services/companion/java/com/android/server/companion/PersistentDataStore.java b/services/companion/java/com/android/server/companion/PersistentDataStore.java
index 73d45ad..5b8d7e5 100644
--- a/services/companion/java/com/android/server/companion/PersistentDataStore.java
+++ b/services/companion/java/com/android/server/companion/PersistentDataStore.java
@@ -34,7 +34,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
-import android.companion.Association;
+import android.companion.AssociationInfo;
import android.companion.DeviceId;
import android.os.Environment;
import android.util.AtomicFile;
@@ -179,11 +179,11 @@
* Reads previously persisted data for the given user "into" the provided containers.
*
* @param userId Android UserID
- * @param associationsOut a container to read the {@link Association}s "into".
+ * @param associationsOut a container to read the {@link AssociationInfo}s "into".
* @param previouslyUsedIdsPerPackageOut a container to read the used IDs "into".
*/
void readStateForUser(@UserIdInt int userId,
- @NonNull Set<Association> associationsOut,
+ @NonNull Set<AssociationInfo> associationsOut,
@NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackageOut) {
Slog.i(LOG_TAG, "Reading associations for user " + userId + " from disk");
final AtomicFile file = getStorageFileForUser(userId);
@@ -244,7 +244,7 @@
* @param associations a set of user's associations.
* @param previouslyUsedIdsPerPackage a set previously used Association IDs for the user.
*/
- void persistStateForUser(@UserIdInt int userId, @NonNull Set<Association> associations,
+ void persistStateForUser(@UserIdInt int userId, @NonNull Set<AssociationInfo> associations,
@NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackage) {
Slog.i(LOG_TAG, "Writing associations for user " + userId + " to disk");
if (DEBUG) Slog.d(LOG_TAG, " > " + associations);
@@ -257,7 +257,7 @@
}
private int readStateFromFileLocked(@UserIdInt int userId, @NonNull AtomicFile file,
- @NonNull String rootTag, @Nullable Set<Association> associationsOut,
+ @NonNull String rootTag, @Nullable Set<AssociationInfo> associationsOut,
@NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackageOut) {
try (FileInputStream in = file.openRead()) {
final TypedXmlPullParser parser = Xml.resolvePullParser(in);
@@ -289,7 +289,7 @@
}
private void persistStateToFileLocked(@NonNull AtomicFile file,
- @Nullable Set<Association> associations,
+ @Nullable Set<AssociationInfo> associations,
@NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackage) {
file.write(out -> {
try {
@@ -328,7 +328,7 @@
}
private static void readAssociationsV0(@NonNull TypedXmlPullParser parser,
- @UserIdInt int userId, @NonNull Set<Association> out)
+ @UserIdInt int userId, @NonNull Set<AssociationInfo> out)
throws XmlPullParserException, IOException {
requireStartOfTag(parser, XML_TAG_ASSOCIATIONS);
@@ -349,7 +349,7 @@
}
private static void readAssociationV0(@NonNull TypedXmlPullParser parser, @UserIdInt int userId,
- int associationId, @NonNull Set<Association> out) throws XmlPullParserException {
+ int associationId, @NonNull Set<AssociationInfo> out) throws XmlPullParserException {
requireStartOfTag(parser, XML_TAG_ASSOCIATION);
final String appPackage = readStringAttribute(parser, XML_ATTR_PACKAGE);
@@ -366,12 +366,12 @@
// "Convert" MAC address into a DeviceId.
final List<DeviceId> deviceIds = Arrays.asList(
new DeviceId(TYPE_MAC_ADDRESS, deviceAddress));
- out.add(new Association(associationId, userId, appPackage, deviceIds, profile,
+ out.add(new AssociationInfo(associationId, userId, appPackage, deviceIds, profile,
/* managedByCompanionApp */false, notify, timeApproved));
}
private static void readAssociationsV1(@NonNull TypedXmlPullParser parser,
- @UserIdInt int userId, @NonNull Set<Association> out)
+ @UserIdInt int userId, @NonNull Set<AssociationInfo> out)
throws XmlPullParserException, IOException {
requireStartOfTag(parser, XML_TAG_ASSOCIATIONS);
@@ -385,7 +385,7 @@
}
private static void readAssociationV1(@NonNull TypedXmlPullParser parser, @UserIdInt int userId,
- @NonNull Set<Association> out) throws XmlPullParserException, IOException {
+ @NonNull Set<AssociationInfo> out) throws XmlPullParserException, IOException {
requireStartOfTag(parser, XML_TAG_ASSOCIATION);
final int associationId = readIntAttribute(parser, XML_ATTR_ID);
@@ -406,8 +406,8 @@
deviceIds.add(new DeviceId(type, value));
}
- out.add(new Association(associationId, userId, appPackage, deviceIds, profile, managedByApp,
- notify, timeApproved));
+ out.add(new AssociationInfo(associationId, userId, appPackage, deviceIds, profile,
+ managedByApp, notify, timeApproved));
}
private static void readPreviouslyUsedIdsV1(@NonNull TypedXmlPullParser parser,
@@ -437,13 +437,13 @@
}
private static void writeAssociations(@NonNull XmlSerializer parent,
- @Nullable Set<Association> associations) throws IOException {
+ @Nullable Set<AssociationInfo> associations) throws IOException {
final XmlSerializer serializer = parent.startTag(null, XML_TAG_ASSOCIATIONS);
forEach(associations, it -> writeAssociation(serializer, it));
serializer.endTag(null, XML_TAG_ASSOCIATIONS);
}
- private static void writeAssociation(@NonNull XmlSerializer parent, @NonNull Association a)
+ private static void writeAssociation(@NonNull XmlSerializer parent, @NonNull AssociationInfo a)
throws IOException {
final XmlSerializer serializer = parent.startTag(null, XML_TAG_ASSOCIATION);
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
new file mode 100644
index 0000000..18cf6f8
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion.virtual;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.companion.virtual.IVirtualDevice;
+import android.companion.virtual.IVirtualDeviceManager;
+import android.content.Context;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.util.ExceptionUtils;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.DumpUtils;
+import com.android.server.SystemService;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+
+/** @hide */
+@SuppressLint("LongLogTag")
+public class VirtualDeviceManagerService extends SystemService {
+
+ private static final boolean DEBUG = false;
+ private static final String LOG_TAG = "VirtualDeviceManagerService";
+ private final VirtualDeviceManagerImpl mImpl;
+ @GuardedBy("mVirtualDevices")
+ private final ArrayList<VirtualDeviceImpl> mVirtualDevices = new ArrayList<>();
+
+ public VirtualDeviceManagerService(Context context) {
+ super(context);
+ mImpl = new VirtualDeviceManagerImpl();
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.VIRTUAL_DEVICE_SERVICE, mImpl);
+ }
+
+ private class VirtualDeviceImpl extends IVirtualDevice.Stub {
+
+ private VirtualDeviceImpl() {}
+
+ @Override
+ public void close() {
+ synchronized (mVirtualDevices) {
+ mVirtualDevices.remove(this);
+ }
+ }
+ }
+
+ class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub {
+
+ @Override
+ public IVirtualDevice createVirtualDevice() {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.CREATE_VIRTUAL_DEVICE,
+ "createVirtualDevice");
+ VirtualDeviceImpl virtualDevice = new VirtualDeviceImpl();
+ synchronized (mVirtualDevices) {
+ mVirtualDevices.add(virtualDevice);
+ }
+ return virtualDevice;
+ }
+
+ @Override
+ public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+ throws RemoteException {
+ try {
+ return super.onTransact(code, data, reply, flags);
+ } catch (Throwable e) {
+ Slog.e(LOG_TAG, "Error during IPC", e);
+ throw ExceptionUtils.propagate(e, RemoteException.class);
+ }
+ }
+
+ @Override
+ public void dump(@NonNull FileDescriptor fd,
+ @NonNull PrintWriter fout,
+ @Nullable String[] args) {
+ if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), LOG_TAG, fout)) {
+ return;
+ }
+ fout.println("Created virtual devices: ");
+ synchronized (mVirtualDevices) {
+ for (VirtualDeviceImpl virtualDevice : mVirtualDevices) {
+ fout.println(virtualDevice.toString());
+ }
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java
index 5bc69943..76754d3 100644
--- a/services/core/java/com/android/server/dreams/DreamController.java
+++ b/services/core/java/com/android/server/dreams/DreamController.java
@@ -117,7 +117,8 @@
}
public void startDream(Binder token, ComponentName name,
- boolean isTest, boolean canDoze, int userId, PowerManager.WakeLock wakeLock) {
+ boolean isTest, boolean canDoze, int userId, PowerManager.WakeLock wakeLock,
+ ComponentName overlayComponentName) {
stopDream(true /*immediate*/, "starting new dream");
Trace.traceBegin(Trace.TRACE_TAG_POWER, "startDream");
@@ -138,6 +139,7 @@
Intent intent = new Intent(DreamService.SERVICE_INTERFACE);
intent.setComponent(name);
intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ intent.putExtra(DreamService.EXTRA_DREAM_OVERLAY_COMPONENT, overlayComponentName);
try {
if (!mContext.bindServiceAsUser(intent, mCurrentDream,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 3a7220f7..258689a 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -95,6 +95,8 @@
private int mCurrentDreamDozeScreenState = Display.STATE_UNKNOWN;
private int mCurrentDreamDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
+ private ComponentName mDreamOverlayServiceName;
+
private AmbientDisplayConfiguration mDozeConfig;
@VisibleForTesting
@@ -421,7 +423,8 @@
if (!mCurrentDreamName.equals(mAmbientDisplayComponent)) {
mUiEventLogger.log(DreamManagerEvent.DREAM_START);
}
- mController.startDream(newToken, name, isTest, canDoze, userId, wakeLock);
+ mController.startDream(newToken, name, isTest, canDoze, userId, wakeLock,
+ mDreamOverlayServiceName);
}));
}
@@ -592,6 +595,15 @@
}
@Override // Binder call
+ public void registerDreamOverlayService(ComponentName overlayComponent) {
+ checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
+
+ // Store the overlay service component so that it can be passed to the dream when it is
+ // invoked.
+ mDreamOverlayServiceName = overlayComponent;
+ }
+
+ @Override // Binder call
public ComponentName getDefaultDreamComponentForUser(int userId) {
checkPermission(android.Manifest.permission.READ_DREAM_STATE);
userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
diff --git a/services/core/java/com/android/server/pm/IncrementalProgressListener.java b/services/core/java/com/android/server/pm/IncrementalProgressListener.java
index bb797cb..43a938e 100644
--- a/services/core/java/com/android/server/pm/IncrementalProgressListener.java
+++ b/services/core/java/com/android/server/pm/IncrementalProgressListener.java
@@ -37,7 +37,20 @@
if (ps == null) {
return;
}
- ps.setLoadingProgress(progress);
+
+ boolean wasLoading = ps.isPackageLoading();
+ // Due to asynchronous progress reporting, incomplete progress might be received
+ // after the app is migrated off incremental. Ignore such progress updates.
+ if (wasLoading) {
+ ps.setLoadingProgress(progress);
+ // Only report the state change when loading state changes from loading to not
+ if (!ps.isPackageLoading()) {
+ // Unregister progress listener
+ mPm.mIncrementalManager.unregisterLoadingProgressCallbacks(ps.getPathString());
+ // Make sure the information is preserved
+ mPm.scheduleWriteSettingsLocked();
+ }
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/IncrementalStates.java b/services/core/java/com/android/server/pm/IncrementalStates.java
deleted file mode 100644
index 3101ca7..0000000
--- a/services/core/java/com/android/server/pm/IncrementalStates.java
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.pm;
-
-import android.content.pm.IncrementalStatesInfo;
-import android.os.Handler;
-import android.util.Slog;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.os.BackgroundThread;
-import com.android.internal.util.function.pooled.PooledLambda;
-
-/**
- * Manages state transitions of a package installed on Incremental File System. Currently manages:
- * 1. loading state (whether a package is still loading or has been fully loaded).
- *
- * The following events might change the states of a package:
- * 1. Installation commit
- * 2. Loading progress changes
- *
- * @hide
- */
-public final class IncrementalStates {
- private static final String TAG = "IncrementalStates";
- private static final boolean DEBUG = false;
- private final Handler mHandler = BackgroundThread.getHandler();
- private final Object mLock = new Object();
- @GuardedBy("mLock")
- private final LoadingState mLoadingState;
- @GuardedBy("mLock")
- private Callback mCallback = null;
-
- public IncrementalStates() {
- // By default the package is not fully loaded (i.e., is loading)
- this(true, 0);
- }
-
- public IncrementalStates(boolean isLoading, float loadingProgress) {
- mLoadingState = new LoadingState(isLoading, loadingProgress);
- }
-
- /**
- * Callback interface to report that the loading state of this package has changed.
- */
- public interface Callback {
- /**
- * Reports that package is fully loaded.
- */
- void onPackageFullyLoaded();
- }
-
- /**
- * By calling this method, the caller indicates that package installation has just been
- * committed. Set the initial loading state after the package
- * is committed. Incremental packages are by-default loading; non-Incremental packages are not.
- *
- * @param isIncremental whether a package is installed on Incremental or not.
- */
- public void onCommit(boolean isIncremental) {
- if (DEBUG) {
- Slog.i(TAG, "received package commit event");
- }
- if (!isIncremental) {
- synchronized (mLock) {
- updateProgressLocked(1.0f);
- }
- onLoadingStateChanged();
- }
- }
-
- private void onLoadingStateChanged() {
- mHandler.post(PooledLambda.obtainRunnable(
- IncrementalStates::reportFullyLoaded,
- IncrementalStates.this).recycleOnUse());
- }
-
- private void reportFullyLoaded() {
- final Callback callback;
- synchronized (mLock) {
- callback = mCallback;
- }
- if (callback != null) {
- callback.onPackageFullyLoaded();
- }
- }
-
- /**
- * Use the specified callback to report state changing events.
- *
- * @param callback Object to report new state.
- */
- public void setCallback(Callback callback) {
- if (DEBUG) {
- Slog.i(TAG, "registered callback");
- }
- synchronized (mLock) {
- mCallback = callback;
- }
- }
-
- /**
- * Update the package loading progress to specified value.
- *
- * @param progress Value between [0, 1].
- */
- public void setProgress(float progress) {
- final boolean oldLoadingState;
- final boolean newLoadingState;
- synchronized (mLock) {
- oldLoadingState = mLoadingState.isLoading();
- if (oldLoadingState) {
- // Due to asynchronous progress reporting, incomplete progress might be received
- // after the app is migrated off incremental. Ignore such progress updates.
- updateProgressLocked(progress);
- }
- newLoadingState = mLoadingState.isLoading();
- }
- if (oldLoadingState && !newLoadingState) {
- // Only report the state change when loading state changes from true to false
- onLoadingStateChanged();
- }
- }
-
- /**
- * @return all current states in a Parcelable.
- */
- public IncrementalStatesInfo getIncrementalStatesInfo() {
- synchronized (mLock) {
- return new IncrementalStatesInfo(
- mLoadingState.isLoading(),
- mLoadingState.getProgress());
- }
- }
-
- private void updateProgressLocked(float progress) {
- if (DEBUG) {
- Slog.i(TAG, "received progress update: " + progress);
- }
- mLoadingState.setProgress(progress);
- if (Math.abs(1.0f - progress) < 0.00000001f) {
- if (DEBUG) {
- Slog.i(TAG, "package is fully loaded");
- }
- mLoadingState.setProgress(1.0f);
- if (mLoadingState.isLoading()) {
- mLoadingState.adoptNewLoadingStateLocked(false);
- }
- }
- }
-
- private class LoadingState {
- private boolean mIsLoading;
- private float mProgress;
-
- LoadingState(boolean isLoading, float loadingProgress) {
- mIsLoading = isLoading;
- // loading progress is reset to 1 if loading has finished
- mProgress = isLoading ? loadingProgress : 1;
- }
-
- public boolean isLoading() {
- return mIsLoading;
- }
-
- public float getProgress() {
- return mProgress;
- }
-
- public void setProgress(float progress) {
- mProgress = progress;
- }
-
- public void adoptNewLoadingStateLocked(boolean nextState) {
- if (DEBUG) {
- Slog.i(TAG, "Loading state changed from " + mIsLoading + " to " + nextState);
- }
- mIsLoading = nextState;
- }
-
- @Override
- public boolean equals(Object o) {
- if (o == this) {
- return true;
- }
- if (!(o instanceof LoadingState)) {
- return false;
- }
- LoadingState l = (LoadingState) o;
- return l.mIsLoading == mIsLoading && l.mProgress == mProgress;
- }
-
- @Override
- public int hashCode() {
- int hashCode = Boolean.hashCode(mIsLoading);
- hashCode = 31 * hashCode + Float.hashCode(mProgress);
- return hashCode;
- }
- }
-
-
-
- @Override
- public boolean equals(Object o) {
- if (o == this) {
- return true;
- }
- if (!(o instanceof IncrementalStates)) {
- return false;
- }
- IncrementalStates l = (IncrementalStates) o;
- return l.mLoadingState.equals(mLoadingState);
- }
-
- @Override
- public int hashCode() {
- return mLoadingState.hashCode();
- }
-}
diff --git a/services/core/java/com/android/server/pm/IncrementalStatesCallback.java b/services/core/java/com/android/server/pm/IncrementalStatesCallback.java
deleted file mode 100644
index 478c99b..0000000
--- a/services/core/java/com/android/server/pm/IncrementalStatesCallback.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.pm;
-
-/**
- * Package states callback, used to listen for package state changes and send broadcasts
- */
-final class IncrementalStatesCallback implements IncrementalStates.Callback {
- private final String mPackageName;
- private final PackageManagerService mPm;
-
- IncrementalStatesCallback(String packageName, PackageManagerService pm) {
- mPackageName = packageName;
- mPm = pm;
- }
-
- @Override
- public void onPackageFullyLoaded() {
- final String codePath;
- synchronized (mPm.mLock) {
- final PackageSetting ps = mPm.mSettings.getPackageLPr(mPackageName);
- if (ps == null) {
- return;
- }
- codePath = ps.getPathString();
- }
- // Unregister progress listener
- mPm.mIncrementalManager.unregisterLoadingProgressCallbacks(codePath);
- // Make sure the information is preserved
- mPm.scheduleWriteSettingsLocked();
- }
-}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 7569900..4c70cda 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -59,6 +59,7 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.incremental.IncrementalManager;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
@@ -455,7 +456,10 @@
if (pkgSetting.getInstantApp(userId)) {
mPm.mInstantAppRegistry.addInstantAppLPw(userId, pkgSetting.getAppId());
}
- pkgSetting.setStatesOnCommit();
+
+ if (!IncrementalManager.isIncrementalPath(pkgSetting.getPathString())) {
+ pkgSetting.setLoadingProgress(1f);
+ }
return pkg;
}
diff --git a/services/core/java/com/android/server/pm/InstallParams.java b/services/core/java/com/android/server/pm/InstallParams.java
index bfb5f76..e138188 100644
--- a/services/core/java/com/android/server/pm/InstallParams.java
+++ b/services/core/java/com/android/server/pm/InstallParams.java
@@ -1781,9 +1781,6 @@
final String codePath = ps.getPathString();
if (IncrementalManager.isIncrementalPath(codePath)
&& mPm.mIncrementalManager != null) {
- final IncrementalStatesCallback incrementalStatesCallback =
- new IncrementalStatesCallback(ps.getPackageName(), mPm);
- ps.setIncrementalStatesCallback(incrementalStatesCallback);
mPm.mIncrementalManager.registerLoadingProgressCallback(codePath,
new IncrementalProgressListener(ps.getPackageName(), mPm));
}
diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS
index babe21c..1bdc9f3 100644
--- a/services/core/java/com/android/server/pm/OWNERS
+++ b/services/core/java/com/android/server/pm/OWNERS
@@ -12,16 +12,16 @@
per-file StagingManager.java = dariofreni@google.com, ioffe@google.com, olilan@google.com
# dex
-per-file AbstractStatsBase.java = calin@google.com, ngeoffray@google.com
-per-file BackgroundDexOptService.java = calin@google.com, ngeoffray@google.com
-per-file CompilerStats.java = calin@google.com, ngeoffray@google.com
-per-file DynamicCodeLoggingService.java = alanstokes@google.com, calin@google.com, ngeoffray@google.com
-per-file InstructionSets.java = calin@google.com, ngeoffray@google.com
-per-file OtaDexoptService.java = calin@google.com, ngeoffray@google.com
-per-file OtaDexoptShellCommand.java = calin@google.com, ngeoffray@google.com
-per-file PackageDexOptimizer.java = calin@google.com, ngeoffray@google.com
-per-file PackageManagerServiceCompilerMapping.java = calin@google.com, ngeoffray@google.com
-per-file PackageUsage.java = calin@google.com, ngeoffray@google.com
+per-file AbstractStatsBase.java = file:dex/OWNERS
+per-file BackgroundDexOptService.java = file:dex/OWNERS
+per-file CompilerStats.java = file:dex/OWNERS
+per-file DynamicCodeLoggingService.java = file:dex/OWNERS
+per-file InstructionSets.java = file:dex/OWNERS
+per-file OtaDexoptService.java = file:dex/OWNERS
+per-file OtaDexoptShellCommand.java = file:dex/OWNERS
+per-file PackageDexOptimizer.java = file:dex/OWNERS
+per-file PackageManagerServiceCompilerMapping.java = file:dex/OWNERS
+per-file PackageUsage.java = file:dex/OWNERS
# multi user / cross profile
per-file CrossProfileAppsServiceImpl.java = omakoto@google.com, yamasani@google.com
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 89e47b9..3d916ae4 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -142,7 +142,6 @@
import android.content.pm.overlay.OverlayPaths;
import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.component.ParsedActivity;
-import android.content.pm.parsing.component.ParsedActivityImpl;
import android.content.pm.parsing.component.ParsedInstrumentation;
import android.content.pm.parsing.component.ParsedIntentInfo;
import android.content.pm.parsing.component.ParsedMainComponent;
@@ -10009,7 +10008,7 @@
if (ps == null) {
return null;
}
- return ps.getIncrementalStatesInfo();
+ return new IncrementalStatesInfo(ps.isPackageLoading(), ps.getLoadingProgress());
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 65eb836..b8e354e 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -25,7 +25,6 @@
import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.pm.ApplicationInfo;
-import android.content.pm.IncrementalStatesInfo;
import android.content.pm.PackageManager;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.SigningDetails;
@@ -36,7 +35,6 @@
import android.content.pm.pkg.PackageUserState;
import android.content.pm.pkg.PackageUserStateInternal;
import android.os.PersistableBundle;
-import android.os.incremental.IncrementalManager;
import android.service.pm.PackageProto;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -103,9 +101,6 @@
@Nullable
Set<String> mOldCodePaths;
- @NonNull
- private IncrementalStates incrementalStates;
-
@Nullable
String[] usesStaticLibraries;
@@ -153,6 +148,8 @@
@NonNull
private String mPathString;
+ private float mLoadingProgress;
+
@Nullable
private String mPrimaryCpuAbi;
@@ -183,6 +180,7 @@
// TODO: Access is not locked.
// Whether this package is currently stopped, thus can not be
// started until explicitly launched by the user.
+ @NonNull
private final SparseArray<PackageUserStateInternalImpl> mUserState = new SparseArray<>();
@NonNull
@@ -242,7 +240,6 @@
this.versionCode = longVersionCode;
this.signatures = new PackageSignatures();
this.installSource = InstallSource.EMPTY;
- this.incrementalStates = new IncrementalStates();
this.sharedUserId = sharedUserId;
mDomainSetId = domainSetId;
copyMimeGroups(mimeGroups);
@@ -614,7 +611,7 @@
super.copySettingBase(other);
sharedUserId = other.sharedUserId;
mimeGroups = other.mimeGroups;
- incrementalStates = other.incrementalStates;
+ mLoadingProgress = other.mLoadingProgress;
legacyNativeLibraryPath = other.legacyNativeLibraryPath;
mName = other.mName;
mRealName = other.mRealName;
@@ -1220,42 +1217,13 @@
* @return True if package is still being loaded, false if the package is fully loaded.
*/
public boolean isPackageLoading() {
- return incrementalStates.getIncrementalStatesInfo().isLoading();
+ return Math.abs(1.0f - mLoadingProgress) >= 0.00000001f;
}
- /**
- * @return all current states in a Parcelable.
- */
- public IncrementalStatesInfo getIncrementalStatesInfo() {
- return incrementalStates.getIncrementalStatesInfo();
- }
-
- /**
- * Called to indicate that the package installation has been committed. This will create a
- * new startable state and a new loading state with default values. By default, the package is
- * startable after commit. For a package installed on Incremental, the loading state is true.
- * For non-Incremental packages, the loading state is false.
- */
- public void setStatesOnCommit() {
- incrementalStates.onCommit(IncrementalManager.isIncrementalPath(getPathString()));
+ public PackageSetting setLoadingProgress(float progress) {
+ mLoadingProgress = progress;
onChanged();
- }
-
- /**
- * Called to set the callback to listen for startable state changes.
- */
- public void setIncrementalStatesCallback(IncrementalStates.Callback callback) {
- incrementalStates.setCallback(callback);
- onChanged();
- }
-
- /**
- * Called to report progress changes. This might trigger loading state change.
- * @see IncrementalStates#setProgress(float)
- */
- public void setLoadingProgress(float progress) {
- incrementalStates.setProgress(progress);
- onChanged();
+ return this;
}
@NonNull
@@ -1381,13 +1349,6 @@
return this;
}
- public PackageSetting setIncrementalStates(
- IncrementalStates incrementalStates) {
- this.incrementalStates = incrementalStates;
- onChanged();
- return this;
- }
-
// Code below generated by codegen v1.0.23.
@@ -1416,11 +1377,6 @@
return mOldCodePaths;
}
- @DataClass.Generated.Member
- public @NonNull IncrementalStates getIncrementalStates() {
- return incrementalStates;
- }
-
/**
* The path under which native libraries have been unpacked. This path is
* always derived at runtime, and is only stored here for cleanup when a
@@ -1477,6 +1433,11 @@
}
@DataClass.Generated.Member
+ public float getLoadingProgress() {
+ return mLoadingProgress;
+ }
+
+ @DataClass.Generated.Member
public @Nullable String getPrimaryCpuAbi() {
return mPrimaryCpuAbi;
}
@@ -1576,10 +1537,10 @@
}
@DataClass.Generated(
- time = 1628017546382L,
+ time = 1635295317317L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/PackageSetting.java",
- inputSignatures = "static final android.content.pm.PackageUserState DEFAULT_USER_STATE\nprotected int sharedUserId\n @android.annotation.Nullable java.util.Map<java.lang.String,android.util.ArraySet<java.lang.String>> mimeGroups\n @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\nprivate @android.annotation.NonNull com.android.server.pm.IncrementalStates incrementalStates\n @android.annotation.Nullable java.lang.String[] usesStaticLibraries\n @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mRealName\nprivate int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackage pkg\nprivate @android.annotation.Nullable com.android.server.pm.SharedUserSetting sharedUser\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate long mLastModifiedTime\nprivate long firstInstallTime\nprivate long lastUpdateTime\nprivate long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate boolean installPermissionsFixed\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<android.content.pm.PackageUserState> mUserState\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate int categoryOverride\nprivate boolean updateAvailable\nprivate boolean forceQueryableOverride\nprivate @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic com.android.server.pm.PackageSetting snapshot()\npublic void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic java.util.List<java.lang.String> getMimeGroup(java.lang.String)\nprivate android.util.ArraySet<java.lang.String> getMimeGroupInternal(java.lang.String)\npublic boolean isMatch(int)\npublic boolean isSharedUser()\npublic com.android.server.pm.PackageSetting setAppId(int)\npublic com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic com.android.server.pm.PackageSetting setFirstInstallTime(long)\npublic com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic com.android.server.pm.PackageSetting setInstallerPackageName(java.lang.String)\npublic com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic boolean setMimeGroup(java.lang.String,java.util.List<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPkg(com.android.server.pm.parsing.pkg.AndroidPackage)\npublic com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic int getSharedUserIdInt()\npublic @java.lang.Override java.lang.String toString()\nprotected void copyMimeGroups(java.util.Map<java.lang.String,android.util.ArraySet<java.lang.String>>)\npublic void updateFrom(com.android.server.pm.PackageSetting)\n com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic boolean isPrivileged()\npublic boolean isOem()\npublic boolean isVendor()\npublic boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic boolean isSystemExt()\npublic boolean isOdm()\npublic boolean isSystem()\npublic android.content.pm.SigningDetails getSigningDetails()\npublic com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic void copyFrom(com.android.server.pm.PackageSetting)\nprivate void doCopy(com.android.server.pm.PackageSetting)\n @com.android.internal.annotations.VisibleForTesting android.content.pm.PackageUserState modifyUserState(int)\npublic @android.annotation.NonNull android.content.pm.PackageUserState readUserState(int)\npublic @android.annotation.Nullable android.content.pm.PackageUserState readUserStateNullable(int)\n void setEnabled(int,int,java.lang.String)\n int getEnabled(int)\n java.lang.String getLastDisabledAppCaller(int)\n void setInstalled(boolean,int)\n boolean getInstalled(int)\n int getInstallReason(int)\n void setInstallReason(int,int)\n int getUninstallReason(int)\n void setUninstallReason(int,int)\n boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getOverlayPathsForLibrary(int)\n @com.android.internal.annotations.VisibleForTesting android.util.SparseArray<android.content.pm.PackageUserState> getUserState()\n boolean isAnyInstalled(int[])\n int[] queryInstalledUsers(int[],boolean)\n long getCeDataInode(int)\n void setCeDataInode(long,int)\n boolean getStopped(int)\n void setStopped(boolean,int)\n boolean getNotLaunched(int)\n void setNotLaunched(boolean,int)\n boolean getHidden(int)\n void setHidden(boolean,int)\n int getDistractionFlags(int)\n void setDistractionFlags(int,int)\n boolean getSuspended(int)\n boolean isSuspendedBy(java.lang.String,int)\n void addOrUpdateSuspension(java.lang.String,android.content.pm.SuspendDialogInfo,android.os.PersistableBundle,android.os.PersistableBundle,int)\n void removeSuspension(java.lang.String,int)\n void removeSuspension(java.util.function.Predicate<java.lang.String>,int)\npublic boolean getInstantApp(int)\n void setInstantApp(boolean,int)\n boolean getVirtualPreload(int)\n void setVirtualPreload(boolean,int)\n void setUserState(int,long,int,boolean,boolean,boolean,boolean,int,boolean,android.util.ArrayMap<java.lang.String,android.content.pm.PackageUserState.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String)\n void setUserState(int,android.content.pm.PackageUserState)\n android.util.ArraySet<java.lang.String> getEnabledComponents(int)\n android.util.ArraySet<java.lang.String> getDisabledComponents(int)\n void setEnabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setEnabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n android.content.pm.PackageUserState modifyUserStateComponents(int,boolean,boolean)\n void addDisabledComponent(java.lang.String,int)\n void addEnabledComponent(java.lang.String,int)\n boolean enableComponentLPw(java.lang.String,int)\n boolean disableComponentLPw(java.lang.String,int)\n boolean restoreComponentLPw(java.lang.String,int)\n int getCurrentEnabledStateLPr(java.lang.String,int)\n void removeUser(int)\npublic int[] getNotInstalledUserIds()\n void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\n void setHarmfulAppWarning(int,java.lang.String)\n java.lang.String getHarmfulAppWarning(int)\n com.android.server.pm.PackageSetting setPath(java.io.File)\n java.lang.String getPathString()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic void resetOverrideComponentLabelIcon(int)\npublic void setSplashScreenTheme(int,java.lang.String)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic boolean isPackageLoading()\npublic android.content.pm.IncrementalStatesInfo getIncrementalStatesInfo()\npublic void setStatesOnCommit()\npublic void setIncrementalStatesCallback(com.android.server.pm.IncrementalStates.Callback)\npublic void setLoadingProgress(float)\npublic @android.annotation.NonNull @java.lang.Override long getLongVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackageApi getAndroidPackage()\npublic @android.annotation.Nullable @java.lang.Override java.lang.Integer getSharedUserId()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<android.content.pm.SharedLibraryInfo> getUsesLibraryInfos()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getSeInfoOverride()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Deprecated @java.lang.Override int[] getUserIds()\npublic @java.lang.Override android.content.pm.PackageUserState getUserState(int)\npublic com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic com.android.server.pm.PackageSetting setSharedUser(com.android.server.pm.SharedUserSetting)\npublic com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic com.android.server.pm.PackageSetting setIncrementalStates(com.android.server.pm.IncrementalStates)\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageState]\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
+ inputSignatures = "protected int sharedUserId\n @android.annotation.Nullable java.util.Map<java.lang.String,android.util.ArraySet<java.lang.String>> mimeGroups\n @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\n @android.annotation.Nullable java.lang.String[] usesStaticLibraries\n @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mRealName\nprivate int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackage pkg\nprivate @android.annotation.Nullable com.android.server.pm.SharedUserSetting sharedUser\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate float loadingProgress\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate long mLastModifiedTime\nprivate long firstInstallTime\nprivate long lastUpdateTime\nprivate long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate boolean installPermissionsFixed\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateInternalImpl> mUserState\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate int categoryOverride\nprivate boolean updateAvailable\nprivate boolean forceQueryableOverride\nprivate @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic com.android.server.pm.PackageSetting snapshot()\npublic void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic java.util.List<java.lang.String> getMimeGroup(java.lang.String)\nprivate android.util.ArraySet<java.lang.String> getMimeGroupInternal(java.lang.String)\npublic boolean isMatch(int)\npublic boolean isSharedUser()\npublic com.android.server.pm.PackageSetting setAppId(int)\npublic com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic com.android.server.pm.PackageSetting setFirstInstallTime(long)\npublic com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic com.android.server.pm.PackageSetting setInstallerPackageName(java.lang.String)\npublic com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic boolean setMimeGroup(java.lang.String,java.util.List<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPkg(com.android.server.pm.parsing.pkg.AndroidPackage)\npublic com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic int getSharedUserIdInt()\npublic @java.lang.Override java.lang.String toString()\nprotected void copyMimeGroups(java.util.Map<java.lang.String,android.util.ArraySet<java.lang.String>>)\npublic void updateFrom(com.android.server.pm.PackageSetting)\n com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic boolean isPrivileged()\npublic boolean isOem()\npublic boolean isVendor()\npublic boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic boolean isSystemExt()\npublic boolean isOdm()\npublic boolean isSystem()\npublic android.content.pm.SigningDetails getSigningDetails()\npublic com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic void copyPackageSetting(com.android.server.pm.PackageSetting)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateInternalImpl modifyUserState(int)\npublic @android.annotation.NonNull android.content.pm.pkg.PackageUserStateInternal readUserState(int)\npublic @android.annotation.Nullable android.content.pm.pkg.PackageUserState readUserStateNullable(int)\n void setEnabled(int,int,java.lang.String)\n int getEnabled(int)\n java.lang.String getLastDisabledAppCaller(int)\n void setInstalled(boolean,int)\n boolean getInstalled(int)\n int getInstallReason(int)\n void setInstallReason(int,int)\n int getUninstallReason(int)\n void setUninstallReason(int,int)\n boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getOverlayPathsForLibrary(int)\n @com.android.internal.annotations.VisibleForTesting android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateInternalImpl> getUserState()\n boolean isAnyInstalled(int[])\n int[] queryInstalledUsers(int[],boolean)\n long getCeDataInode(int)\n void setCeDataInode(long,int)\n boolean getStopped(int)\n void setStopped(boolean,int)\n boolean getNotLaunched(int)\n void setNotLaunched(boolean,int)\n boolean getHidden(int)\n void setHidden(boolean,int)\n int getDistractionFlags(int)\n void setDistractionFlags(int,int)\n boolean getSuspended(int)\n boolean isSuspendedBy(java.lang.String,int)\n boolean addOrUpdateSuspension(java.lang.String,android.content.pm.SuspendDialogInfo,android.os.PersistableBundle,android.os.PersistableBundle,int)\n boolean removeSuspension(java.lang.String,int)\n void removeSuspension(java.util.function.Predicate<java.lang.String>,int)\npublic boolean getInstantApp(int)\n void setInstantApp(boolean,int)\n boolean getVirtualPreload(int)\n void setVirtualPreload(boolean,int)\n void setUserState(int,long,int,boolean,boolean,boolean,boolean,int,boolean,android.util.ArrayMap<java.lang.String,android.content.pm.pkg.PackageUserState.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String)\n void setUserState(int,android.content.pm.pkg.PackageUserStateInternal)\n android.util.ArraySet<java.lang.String> getEnabledComponents(int)\n android.util.ArraySet<java.lang.String> getDisabledComponents(int)\n void setEnabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setEnabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n com.android.server.pm.pkg.PackageUserStateInternalImpl modifyUserStateComponents(int,boolean,boolean)\n void addDisabledComponent(java.lang.String,int)\n void addEnabledComponent(java.lang.String,int)\n boolean enableComponentLPw(java.lang.String,int)\n boolean disableComponentLPw(java.lang.String,int)\n boolean restoreComponentLPw(java.lang.String,int)\n int getCurrentEnabledStateLPr(java.lang.String,int)\n void removeUser(int)\npublic int[] getNotInstalledUserIds()\n void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\n void setHarmfulAppWarning(int,java.lang.String)\n java.lang.String getHarmfulAppWarning(int)\n com.android.server.pm.PackageSetting setPath(java.io.File)\n java.lang.String getPathString()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic void resetOverrideComponentLabelIcon(int)\npublic void setSplashScreenTheme(int,java.lang.String)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic boolean isPackageLoading()\npublic com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic @android.annotation.NonNull @java.lang.Override long getLongVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackageApi getAndroidPackage()\npublic @android.annotation.Nullable @java.lang.Override java.lang.Integer getSharedUserId()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<android.content.pm.SharedLibraryInfo> getUsesLibraryInfos()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getSeInfoOverride()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Deprecated @java.lang.Override int[] getUserIds()\npublic @java.lang.Override android.content.pm.pkg.PackageUserState getUserState(int)\npublic com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic com.android.server.pm.PackageSetting setSharedUser(com.android.server.pm.SharedUserSetting)\npublic com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageState]\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/ScanPackageHelper.java b/services/core/java/com/android/server/pm/ScanPackageHelper.java
index 8018011..731c6ca 100644
--- a/services/core/java/com/android/server/pm/ScanPackageHelper.java
+++ b/services/core/java/com/android/server/pm/ScanPackageHelper.java
@@ -1018,9 +1018,6 @@
if (mPm.mIncrementalManager != null && isIncrementalPath(parsedPackage.getPath())) {
if (pkgSetting != null && pkgSetting.isPackageLoading()) {
// Continue monitoring loading progress of active incremental packages
- final IncrementalStatesCallback incrementalStatesCallback =
- new IncrementalStatesCallback(parsedPackage.getPackageName(), mPm);
- pkgSetting.setIncrementalStatesCallback(incrementalStatesCallback);
mPm.mIncrementalManager.registerLoadingProgressCallback(parsedPackage.getPath(),
new IncrementalProgressListener(parsedPackage.getPackageName(), mPm));
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index f89d9eb..6df1006 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -73,7 +73,6 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.incremental.IncrementalManager;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.service.pm.PackageServiceDumpProto;
@@ -1087,9 +1086,6 @@
pkgSetting.setLegacyNativeLibraryPath(legacyNativeLibraryPath);
}
pkgSetting.setPath(codePath);
- if (IncrementalManager.isIncrementalPath(codePath.getAbsolutePath())) {
- pkgSetting.setIncrementalStates(new IncrementalStates());
- }
}
// If what we are scanning is a system (and possibly privileged) package,
// then make it so, regardless of whether it was previously installed only
@@ -2708,8 +2704,7 @@
} else {
serializer.attributeInt(null, "sharedUserId", pkg.getAppId());
}
- serializer.attributeFloat(null, "loadingProgress",
- pkg.getIncrementalStates().getIncrementalStatesInfo().getProgress());
+ serializer.attributeFloat(null, "loadingProgress", pkg.getLoadingProgress());
writeUsesStaticLibLPw(serializer, pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions);
@@ -2784,8 +2779,7 @@
if (pkg.isPackageLoading()) {
serializer.attributeBoolean(null, "isLoading", true);
}
- serializer.attributeFloat(null, "loadingProgress",
- pkg.getIncrementalStates().getIncrementalStatesInfo().getProgress());
+ serializer.attributeFloat(null, "loadingProgress", pkg.getLoadingProgress());
serializer.attribute(null, "domainSetId", pkg.getDomainSetId().toString());
@@ -3531,7 +3525,6 @@
PackageSetting packageSetting = null;
long versionCode = 0;
boolean installedForceQueryable = false;
- boolean isLoading = false;
float loadingProgress = 0;
UUID domainSetId;
try {
@@ -3549,7 +3542,6 @@
cpuAbiOverrideString = parser.getAttributeValue(null, "cpuAbiOverride");
updateAvailable = parser.getAttributeBoolean(null, "updateAvailable", false);
installedForceQueryable = parser.getAttributeBoolean(null, "forceQueryable", false);
- isLoading = parser.getAttributeBoolean(null, "isLoading", false);
loadingProgress = parser.getAttributeFloat(null, "loadingProgress", 0);
if (primaryCpuAbiString == null && legacyCpuAbiString != null) {
@@ -3706,7 +3698,7 @@
.setSecondaryCpuAbi(secondaryCpuAbiString)
.setUpdateAvailable(updateAvailable)
.setForceQueryableOverride(installedForceQueryable)
- .setIncrementalStates(new IncrementalStates(isLoading, loadingProgress));
+ .setLoadingProgress(loadingProgress);
// Handle legacy string here for single-user mode
final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED);
if (enabledStr != null) {
@@ -4681,8 +4673,7 @@
}
if (ps.isPackageLoading()) {
pw.print(prefix); pw.println(" loadingProgress=" +
- (int) (ps.getIncrementalStates().getIncrementalStatesInfo().getProgress()
- * 100) + "%");
+ (int) (ps.getLoadingProgress() * 100) + "%");
}
if (ps.getVolumeUuid() != null) {
pw.print(prefix); pw.print(" volumeUuid=");
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 0590e22..33970a2 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4916,6 +4916,12 @@
return mKeyguardDelegate.isInputRestricted();
}
+ /** {@inheritDoc} */
+ @Override
+ public boolean isKeyguardUnoccluding() {
+ return keyguardOn() && !mWindowManagerFuncs.isAppTransitionStateIdle();
+ }
+
@Override
public void dismissKeyguardLw(IKeyguardDismissCallback callback, CharSequence message) {
if (mKeyguardDelegate != null && mKeyguardDelegate.isShowing()) {
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 81dd9c5..4895c0b 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -362,6 +362,12 @@
* as the top display.
*/
void moveDisplayToTop(int displayId);
+
+ /**
+ * Return whether the app transition state is idle.
+ * @return {@code true} if app transition state is idle on the default display.
+ */
+ boolean isAppTransitionStateIdle();
}
/**
@@ -962,6 +968,14 @@
public boolean isKeyguardOccluded();
/**
+ * Return whether the keyguard is unoccluding.
+ * @return {@code true} if the keyguard is unoccluding.
+ */
+ default boolean isKeyguardUnoccluding() {
+ return false;
+ }
+
+ /**
* @return true if in keyguard is on.
*/
boolean isKeyguardShowing();
diff --git a/services/core/java/com/android/server/security/OWNERS b/services/core/java/com/android/server/security/OWNERS
index e6f5826..41070f3 100644
--- a/services/core/java/com/android/server/security/OWNERS
+++ b/services/core/java/com/android/server/security/OWNERS
@@ -1,3 +1,4 @@
# Bug component: 36824
+per-file AttestationVerificationService.java = file:/core/java/android/security/attestationverification/OWNERS
per-file FileIntegrityService.java = victorhsieh@google.com
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 46f8e9f..690397f 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -221,6 +221,7 @@
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.view.IRecentsAnimationRunner;
+import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
import android.view.WindowManager;
@@ -455,6 +456,10 @@
VrController mVrController;
KeyguardController mKeyguardController;
private final ClientLifecycleManager mLifecycleManager;
+
+ @Nullable
+ private final BackGestureController mBackGestureController;
+
private TaskChangeNotificationController mTaskChangeNotificationController;
/** The controller for all operations related to locktask. */
private LockTaskController mLockTaskController;
@@ -811,6 +816,8 @@
mSystemThread = ActivityThread.currentActivityThread();
mUiContext = mSystemThread.getSystemUiContext();
mLifecycleManager = new ClientLifecycleManager();
+ mBackGestureController = BackGestureController.isEnabled() ? new BackGestureController()
+ : null;
mVisibleActivityProcessTracker = new VisibleActivityProcessTracker(this);
mInternal = new LocalService();
GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version", GL_ES_VERSION_UNDEFINED);
@@ -1744,6 +1751,14 @@
}
}
+ @Override
+ public void startBackPreview(IRemoteAnimationRunner runner) {
+ if (mBackGestureController == null) {
+ return;
+ }
+ mBackGestureController.startBackPreview();
+ }
+
/**
* Public API to check if the client is allowed to start an activity on specified display.
*
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 7817f54..03d6590 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -292,6 +292,10 @@
setAppTransitionState(APP_STATE_IDLE);
}
+ boolean isIdle() {
+ return mAppTransitionState == APP_STATE_IDLE;
+ }
+
boolean isTimeout() {
return mAppTransitionState == APP_STATE_TIMEOUT;
}
diff --git a/services/core/java/com/android/server/wm/BackGestureController.java b/services/core/java/com/android/server/wm/BackGestureController.java
new file mode 100644
index 0000000..f8f6254
--- /dev/null
+++ b/services/core/java/com/android/server/wm/BackGestureController.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.os.SystemProperties;
+
+/**
+ * Controller to handle actions related to the back gesture on the server side.
+ */
+public class BackGestureController {
+
+ private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability";
+
+ public static boolean isEnabled() {
+ return SystemProperties.getInt(BACK_PREDICTABILITY_PROP, 0) > 0;
+ }
+
+ /**
+ * Start a remote animation the back gesture.
+ */
+ public void startBackPreview() {
+ }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index befb335..806d27a 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -114,10 +114,12 @@
import static com.android.server.wm.DisplayContentProto.INPUT_METHOD_INPUT_TARGET;
import static com.android.server.wm.DisplayContentProto.INPUT_METHOD_TARGET;
import static com.android.server.wm.DisplayContentProto.INSETS_SOURCE_PROVIDERS;
+import static com.android.server.wm.DisplayContentProto.IS_SLEEPING;
import static com.android.server.wm.DisplayContentProto.OPENING_APPS;
import static com.android.server.wm.DisplayContentProto.RESUMED_ACTIVITY;
import static com.android.server.wm.DisplayContentProto.ROOT_DISPLAY_AREA;
import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
+import static com.android.server.wm.DisplayContentProto.SLEEP_TOKENS;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
@@ -3258,6 +3260,11 @@
proto.write(FOCUSED_ROOT_TASK_ID, INVALID_TASK_ID);
}
proto.write(DISPLAY_READY, isReady());
+ proto.write(IS_SLEEPING, isSleeping());
+ for (int i = 0; i < mAllSleepTokens.size(); ++i) {
+ mAllSleepTokens.get(i).writeTagToProto(proto, SLEEP_TOKENS);
+ }
+
if (mImeLayeringTarget != null) {
mImeLayeringTarget.dumpDebug(proto, INPUT_METHOD_TARGET, logLevel);
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 7614165..e10ae37 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2639,6 +2639,7 @@
token = new SleepToken(tag, displayId);
mSleepTokens.put(tokenKey, token);
display.mAllSleepTokens.add(token);
+ ProtoLog.d(WM_DEBUG_STATES, "Create sleep token: tag=%s, displayId=%d", tag, displayId);
} else {
throw new RuntimeException("Create the same sleep token twice: " + token);
}
@@ -2657,6 +2658,8 @@
return;
}
+ ProtoLog.d(WM_DEBUG_STATES, "Remove sleep token: tag=%s, displayId=%d", token.mTag,
+ token.mDisplayId);
display.mAllSleepTokens.remove(token);
if (display.mAllSleepTokens.isEmpty()) {
mService.updateSleepIfNeededLocked();
@@ -3664,6 +3667,10 @@
return "{\"" + mTag + "\", display " + mDisplayId
+ ", acquire at " + TimeUtils.formatUptime(mAcquireTime) + "}";
}
+
+ void writeTagToProto(ProtoOutputStream proto, long fieldId) {
+ proto.write(fieldId, mTag);
+ }
}
private class RankTaskLayersRunnable implements Runnable {
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 810a853..08b1a2f 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -159,7 +159,8 @@
boolean needsShowWhenLockedWallpaper = false;
if ((w.mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0
&& mService.mPolicy.isKeyguardLocked()
- && mService.mPolicy.isKeyguardOccluded()) {
+ && (mService.mPolicy.isKeyguardOccluded()
+ || mService.mPolicy.isKeyguardUnoccluding())) {
// The lowest show when locked window decides whether we need to put the wallpaper
// behind.
needsShowWhenLockedWallpaper = !isFullscreen(w.mAttrs)
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c8e3c6a..ac00520 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3056,6 +3056,11 @@
syncInputTransactions(true /* waitForAnimations */);
}
+ @Override
+ public boolean isAppTransitionStateIdle() {
+ return getDefaultDisplayContentLocked().mAppTransition.isIdle();
+ }
+
/**
* Notifies activity manager that some Keyguard flags have changed and that it needs to
* reevaluate the visibilities of the activities.
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 4e4a5c3..eb2c8a6 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -114,6 +114,8 @@
"libutils",
"libui",
"libvibratorservice",
+ "PlatformProperties",
+ "InputFlingerProperties",
"libinput",
"libinputflinger",
"libinputflinger_base",
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 94b1ad1..790acbf 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -26,30 +26,14 @@
// Log debug messages about InputDispatcherPolicy
#define DEBUG_INPUT_DISPATCHER_POLICY 0
+#include <InputFlingerProperties.sysprop.h>
#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
#include <android/os/IInputConstants.h>
+#include <android/sysprop/InputProperties.sysprop.h>
+#include <android_os_MessageQueue.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/Log.h>
-#include <limits.h>
-#include <atomic>
-#include <cinttypes>
-
-#include <utils/Log.h>
-#include <utils/Looper.h>
-#include <utils/threads.h>
-#include <utils/Trace.h>
-
-#include <binder/IServiceManager.h>
-
-#include <input/PointerController.h>
-#include <input/SpriteController.h>
-#include <ui/Region.h>
-
-#include <batteryservice/include/batteryservice/BatteryServiceConstants.h>
-#include <inputflinger/InputManager.h>
-
-#include <android_os_MessageQueue.h>
#include <android_view_InputChannel.h>
#include <android_view_InputDevice.h>
#include <android_view_KeyEvent.h>
@@ -57,11 +41,25 @@
#include <android_view_PointerIcon.h>
#include <android_view_VerifiedKeyEvent.h>
#include <android_view_VerifiedMotionEvent.h>
-
+#include <batteryservice/include/batteryservice/BatteryServiceConstants.h>
+#include <binder/IServiceManager.h>
+#include <input/PointerController.h>
+#include <input/SpriteController.h>
+#include <inputflinger/InputManager.h>
+#include <limits.h>
#include <nativehelper/ScopedLocalFrame.h>
#include <nativehelper/ScopedLocalRef.h>
#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/ScopedUtfChars.h>
+#include <ui/Region.h>
+#include <utils/Log.h>
+#include <utils/Looper.h>
+#include <utils/Trace.h>
+#include <utils/threads.h>
+
+#include <atomic>
+#include <cinttypes>
+#include <vector>
#include "android_hardware_display_DisplayViewport.h"
#include "android_hardware_input_InputApplicationHandle.h"
@@ -69,8 +67,6 @@
#include "android_util_Binder.h"
#include "com_android_server_power_PowerManagerService.h"
-#include <vector>
-
#define INDENT " "
using android::base::ParseUint;
@@ -2089,11 +2085,24 @@
InputReaderConfiguration::CHANGE_DEVICE_ALIAS);
}
-static jstring nativeDump(JNIEnv* env, jclass /* clazz */, jlong ptr) {
- NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+static std::string dumpInputProperties() {
+ std::string out = "Input properties:\n";
+ const bool perWindowInputRotation =
+ sysprop::InputFlingerProperties::per_window_input_rotation().value_or(false);
+ out += StringPrintf(" per_window_input_rotation = %s\n", toString(perWindowInputRotation));
+ const std::string strategy =
+ sysprop::InputProperties::velocitytracker_strategy().value_or("default");
+ out += " persist.input.velocitytracker.strategy = " + strategy + "\n";
+ out += "\n";
+ return out;
+}
- std::string dump;
+static jstring nativeDump(JNIEnv* env, jclass /* clazz */, jlong ptr) {
+ std::string dump = dumpInputProperties();
+
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
im->dump(dump);
+
return env->NewStringUTF(dump.c_str());
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index f351a8c..ce03b59 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -251,6 +251,8 @@
"com.android.server.print.PrintManagerService";
private static final String COMPANION_DEVICE_MANAGER_SERVICE_CLASS =
"com.android.server.companion.CompanionDeviceManagerService";
+ private static final String VIRTUAL_DEVICE_MANAGER_SERVICE_CLASS =
+ "com.android.server.companion.virtual.VirtualDeviceManagerService";
private static final String STATS_COMPANION_APEX_PATH =
"/apex/com.android.os.statsd/javalib/service-statsd.jar";
private static final String SCHEDULING_APEX_PATH =
@@ -2322,6 +2324,11 @@
t.traceBegin("StartCompanionDeviceManager");
mSystemServiceManager.startService(COMPANION_DEVICE_MANAGER_SERVICE_CLASS);
t.traceEnd();
+
+ // VirtualDeviceManager depends on CDM to control the associations.
+ t.traceBegin("StartVirtualDeviceManager");
+ mSystemServiceManager.startService(VIRTUAL_DEVICE_MANAGER_SERVICE_CLASS);
+ t.traceEnd();
}
t.traceBegin("StartRestrictionManager");
diff --git a/tests/DynamicCodeLoggerIntegrationTests/OWNERS b/tests/DynamicCodeLoggerIntegrationTests/OWNERS
new file mode 100644
index 0000000..d9eb141
--- /dev/null
+++ b/tests/DynamicCodeLoggerIntegrationTests/OWNERS
@@ -0,0 +1 @@
+file:/services/core/java/com/android/server/pm/dex/OWNERS
diff --git a/tests/SharedLibraryLoadingTest/Android.bp b/tests/SharedLibraryLoadingTest/Android.bp
new file mode 100644
index 0000000..088278d
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/Android.bp
@@ -0,0 +1,37 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+java_test_host {
+ name: "SharedLibraryLoadingTests",
+ libs: [
+ "tradefed",
+ "junit",
+ ],
+ test_suites: ["general-tests"],
+ data: [
+ ":SharedLibraryLoadingTests_StandardSharedLibrary",
+ ":SharedLibraryLoadingTests_SharedLibraryLoadedAfter",
+ ":SharedLibraryLoadingTests_SharedLibraryClientTests",
+ ":SharedLibraryLoadingTests_Overlay",
+ ],
+}
diff --git a/tests/SharedLibraryLoadingTest/AndroidTest.xml b/tests/SharedLibraryLoadingTest/AndroidTest.xml
new file mode 100644
index 0000000..947453d
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/AndroidTest.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<configuration description="Host-driven test module config for SharedLibraryHostTests">
+ <option name="test-tag" value="SharedLibraryLoadingTests" />
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-instrumentation" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="false" />
+ <option name="remount-system" value="true" />
+ <option name="push"
+ value="SharedLibraryLoadingTests_StandardSharedLibrary.apk->/product/app/SharedLibraryLoadingTests_StandardSharedLibrary.apk" />
+ <option name="push"
+ value="SharedLibraryLoadingTests_SharedLibraryLoadedAfter.apk->/product/app/SharedLibraryLoadingTests_SharedLibraryLoadedAfter.apk" />
+ <option name="push"
+ value="SharedLibraryLoadingTests_Overlay.apk->/product/overlay/SharedLibraryLoadingTests_Overlay.apk" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.RebootTargetPreparer" />
+
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="SharedLibraryLoadingTests_SharedLibraryClientTests.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.sharedlibloadingtest.client" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/tests/SharedLibraryLoadingTest/OWNERS b/tests/SharedLibraryLoadingTest/OWNERS
new file mode 100644
index 0000000..d7b4569
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/OWNERS
@@ -0,0 +1,2 @@
+stenning@google.com
+
diff --git a/tests/SharedLibraryLoadingTest/test-apps/Overlay/Android.bp b/tests/SharedLibraryLoadingTest/test-apps/Overlay/Android.bp
new file mode 100644
index 0000000..b2f4e89
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/Overlay/Android.bp
@@ -0,0 +1,29 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test_helper_app {
+ name: "SharedLibraryLoadingTests_Overlay",
+ platform_apis: true,
+ certificate: "platform",
+ aaptflags: ["--no-resource-removal"],
+}
diff --git a/tests/SharedLibraryLoadingTest/test-apps/Overlay/AndroidManifest.xml b/tests/SharedLibraryLoadingTest/test-apps/Overlay/AndroidManifest.xml
new file mode 100644
index 0000000..ae2784c
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/Overlay/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.sharedlibloadingtest.overlay">
+ <application android:hasCode="false" />
+ <overlay android:targetPackage="android"
+ android:isStatic="true"
+ android:priority="1"/>
+</manifest>
\ No newline at end of file
diff --git a/core/java/android/companion/Association.aidl b/tests/SharedLibraryLoadingTest/test-apps/Overlay/res/values/config.xml
similarity index 60%
copy from core/java/android/companion/Association.aidl
copy to tests/SharedLibraryLoadingTest/test-apps/Overlay/res/values/config.xml
index 2a28f1f..15da3db 100644
--- a/core/java/android/companion/Association.aidl
+++ b/tests/SharedLibraryLoadingTest/test-apps/Overlay/res/values/config.xml
@@ -1,11 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 Google Inc.
*
* 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
+ * 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,
@@ -13,6 +15,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.companion;
-
-parcelable Association;
+-->
+<resources>
+ <string-array name="config_sharedLibrariesLoadedAfterApp" translatable="false">
+ <item>com.android.sharedlibloadingtest.shared_library_after</item>
+ </string-array>
+</resources>
\ No newline at end of file
diff --git a/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/Android.bp b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/Android.bp
new file mode 100644
index 0000000..0d20497
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/Android.bp
@@ -0,0 +1,35 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "SharedLibraryLoadingTests_SharedLibraryClientTests",
+ srcs: ["**/*.java"],
+ resource_dirs: ["res"],
+ libs: [
+ "SharedLibraryLoadingTests_StandardSharedLibrary",
+ "SharedLibraryLoadingTests_SharedLibraryLoadedAfter",
+ "android.test.base",
+ ],
+ static_libs: [
+ "androidx.test.ext.junit",
+ "androidx.test.rules",
+ "androidx.test.core",
+ "testng",
+ ],
+ platform_apis: true,
+}
diff --git a/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/AndroidManifest.xml b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/AndroidManifest.xml
new file mode 100644
index 0000000..e3a9b9b
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.sharedlibloadingtest.client">
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <uses-library android:name="com.android.sharedlibloadingtest.shared_library"/>
+ <uses-library android:name="com.android.sharedlibloadingtest.shared_library_after"/>
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.sharedlibloadingtest.client" />
+</manifest>
\ No newline at end of file
diff --git a/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/res/values/values.xml b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/res/values/values.xml
new file mode 100644
index 0000000..5e0544e
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/res/values/values.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <string name="identical_resource_key">client value</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/companion/Association.aidl b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/ClientClass.java
similarity index 72%
copy from core/java/android/companion/Association.aidl
copy to tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/ClientClass.java
index 2a28f1f..e48fb83 100644
--- a/core/java/android/companion/Association.aidl
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/ClientClass.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.companion;
-parcelable Association;
+package com.android.sharedlibloadingtest;
+
+public class ClientClass {
+ @Override
+ public String toString() {
+ return "Client Code";
+ }
+}
diff --git a/core/java/android/companion/Association.aidl b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/DuplicateClassA.java
similarity index 71%
copy from core/java/android/companion/Association.aidl
copy to tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/DuplicateClassA.java
index 2a28f1f..4c77155 100644
--- a/core/java/android/companion/Association.aidl
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/DuplicateClassA.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.companion;
-parcelable Association;
+package com.android.sharedlibloadingtest;
+
+public class DuplicateClassA {
+ @Override
+ public String toString() {
+ return "Client's Version";
+ }
+}
diff --git a/core/java/android/companion/Association.aidl b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/DuplicateClassB.java
similarity index 71%
copy from core/java/android/companion/Association.aidl
copy to tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/DuplicateClassB.java
index 2a28f1f..86aa6a1 100644
--- a/core/java/android/companion/Association.aidl
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/DuplicateClassB.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.companion;
-parcelable Association;
+package com.android.sharedlibloadingtest;
+
+public class DuplicateClassB {
+ @Override
+ public String toString() {
+ return "Client's Version B";
+ }
+}
diff --git a/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/client/SharedLibraryLoadingOrderTest.java b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/client/SharedLibraryLoadingOrderTest.java
new file mode 100644
index 0000000..43bcb1a
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/client/SharedLibraryLoadingOrderTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.sharedlibloadingtest.client;
+
+import static org.testng.Assert.assertEquals;
+
+import android.content.Context;
+import android.content.res.Resources;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.util.Preconditions;
+import com.android.sharedlibloadingtest.ClientClass;
+import com.android.sharedlibloadingtest.DuplicateClassA;
+import com.android.sharedlibloadingtest.DuplicateClassB;
+import com.android.sharedlibloadingtest.SharedClassAfter;
+import com.android.sharedlibloadingtest.StdSharedClass;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Collections;
+import java.util.HashSet;
+
+@RunWith(AndroidJUnit4.class)
+public class SharedLibraryLoadingOrderTest {
+
+ @Test
+ public void testLoadingOfStdShareLibsShouldBeFirst() {
+ Preconditions.checkArgument(!getLibsLoadedAfter()
+ .contains("com.android.sharedlibloadingtest.shared_library"));
+ DuplicateClassA clazz = new DuplicateClassA();
+ assertEquals(clazz.toString(), "Standard Shared Lib's Version");
+
+ StdSharedClass stdSharedClass = new StdSharedClass();
+ assertEquals(stdSharedClass.toString(), "Nothing Special Lib");
+
+ ClientClass clientCode = new ClientClass();
+ assertEquals(clientCode.toString(), "Client Code");
+ }
+
+ @Test
+ public void testLoadingOfShareLibsIsAfter() {
+ Preconditions.checkArgument(getLibsLoadedAfter()
+ .contains("com.android.sharedlibloadingtest.shared_library_after"));
+ DuplicateClassB clazz = new DuplicateClassB();
+ assertEquals(clazz.toString(), "Client's Version B");
+
+ SharedClassAfter stdSharedClass = new SharedClassAfter();
+ assertEquals(stdSharedClass.toString(), "Also Nothing Special");
+
+ ClientClass clientCode = new ClientClass();
+ assertEquals(clientCode.toString(), "Client Code");
+ }
+
+ @Test
+ public void testLoadingOfResource() {
+ // aapt compiler gives each lib their own namespace so this test just confirming
+ // the resources can be loaded from the same context object
+ Context context = ApplicationProvider.getApplicationContext();
+ String clientString = context.getResources().getString(R.string.identical_resource_key);
+ assertEquals(clientString, "client value");
+ assertEquals(StdSharedClass.getResString(context), "std lib value");
+ assertEquals(SharedClassAfter.getResString(context), "loaded after value");
+
+ }
+
+ private HashSet<String> getLibsLoadedAfter() {
+ Resources systemR = Resources.getSystem();
+ HashSet<String> libsToLoadAfter = new HashSet<>();
+ Collections.addAll(libsToLoadAfter, systemR.getStringArray(
+ com.android.internal.R.array.config_sharedLibrariesLoadedAfterApp));
+ return libsToLoadAfter;
+ }
+}
diff --git a/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/Android.bp b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/Android.bp
new file mode 100644
index 0000000..db9b3ed
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/Android.bp
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test_helper_app {
+ name: "SharedLibraryLoadingTests_SharedLibraryLoadedAfter",
+ srcs: ["**/*.java"],
+ resource_dirs: ["res"],
+ sdk_version: "current",
+ aaptflags: ["--shared-lib"],
+}
diff --git a/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/AndroidManifest.xml b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/AndroidManifest.xml
new file mode 100644
index 0000000..efedfcf
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.sharedlibloadingtest.shared_library_after">
+ <application>
+ <library android:name="com.android.sharedlibloadingtest.shared_library_after" />
+ </application>
+</manifest>
\ No newline at end of file
diff --git a/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/res/values/values.xml b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/res/values/values.xml
new file mode 100644
index 0000000..4525944
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/res/values/values.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <string name="identical_resource_key">loaded after value</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/companion/Association.aidl b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/src/com/android/sharedlibloadingtest/DuplicateClassB.java
similarity index 70%
copy from core/java/android/companion/Association.aidl
copy to tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/src/com/android/sharedlibloadingtest/DuplicateClassB.java
index 2a28f1f..1e1f5aa 100644
--- a/core/java/android/companion/Association.aidl
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/src/com/android/sharedlibloadingtest/DuplicateClassB.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.companion;
-parcelable Association;
+package com.android.sharedlibloadingtest;
+
+public class DuplicateClassB {
+ @Override
+ public String toString() {
+ return "Loaded After Shared Lib's Version";
+ }
+}
diff --git a/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/src/com/android/sharedlibloadingtest/SharedClassAfter.java b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/src/com/android/sharedlibloadingtest/SharedClassAfter.java
new file mode 100644
index 0000000..9e5b40f
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/src/com/android/sharedlibloadingtest/SharedClassAfter.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.sharedlibloadingtest;
+
+import android.content.Context;
+
+import com.android.sharedlibloadingtest.shared_library_after.R;
+
+public class SharedClassAfter {
+ @Override
+ public String toString() {
+ return "Also Nothing Special";
+ }
+
+ public static String getResString(Context context) {
+ return context.getResources().getString(R.string.identical_resource_key);
+ }
+}
diff --git a/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/Android.bp b/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/Android.bp
new file mode 100644
index 0000000..50456b0
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/Android.bp
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test_helper_app {
+ name: "SharedLibraryLoadingTests_StandardSharedLibrary",
+ srcs: ["**/*.java"],
+ resource_dirs: ["res"],
+ sdk_version: "current",
+ aaptflags: ["--shared-lib"],
+}
diff --git a/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/AndroidManifest.xml b/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/AndroidManifest.xml
new file mode 100644
index 0000000..f1a079f
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.sharedlibloadingtest.shared_library">
+ <application>
+ <library android:name="com.android.sharedlibloadingtest.shared_library" />
+ </application>
+</manifest>
\ No newline at end of file
diff --git a/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/res/values/values.xml b/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/res/values/values.xml
new file mode 100644
index 0000000..941351a
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/res/values/values.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <string name="identical_resource_key">std lib value</string>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/companion/Association.aidl b/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/src/com/android/sharedlibloadingtest/DuplicateClassA.java
similarity index 70%
copy from core/java/android/companion/Association.aidl
copy to tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/src/com/android/sharedlibloadingtest/DuplicateClassA.java
index 2a28f1f..a3874aa 100644
--- a/core/java/android/companion/Association.aidl
+++ b/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/src/com/android/sharedlibloadingtest/DuplicateClassA.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.companion;
-parcelable Association;
+package com.android.sharedlibloadingtest;
+
+public class DuplicateClassA {
+ @Override
+ public String toString() {
+ return "Standard Shared Lib's Version";
+ }
+}
diff --git a/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/src/com/android/sharedlibloadingtest/StdSharedClass.java b/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/src/com/android/sharedlibloadingtest/StdSharedClass.java
new file mode 100644
index 0000000..429d65c
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/src/com/android/sharedlibloadingtest/StdSharedClass.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.sharedlibloadingtest;
+
+import android.content.Context;
+
+import com.android.sharedlibloadingtest.shared_library.R;
+
+public class StdSharedClass {
+ @Override
+ public String toString() {
+ return "Nothing Special Lib";
+ }
+
+ public static String getResString(Context context) {
+ return context.getResources().getString(R.string.identical_resource_key);
+ }
+}
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index c1429be..11d02f0 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -120,6 +120,13 @@
static_cast<int>(parser.chunk()->type)));
}
}
+
+ if (!staged_entries_to_remove_.empty()) {
+ diag_->Error(DiagMessage(source_) << "didn't find " << staged_entries_to_remove_.size()
+ << " original staged resources");
+ return false;
+ }
+
return true;
}
@@ -393,6 +400,12 @@
return false;
}
+ if (const auto to_remove_it = staged_entries_to_remove_.find({name, res_id});
+ to_remove_it != staged_entries_to_remove_.end()) {
+ staged_entries_to_remove_.erase(to_remove_it);
+ continue;
+ }
+
NewResourceBuilder res_builder(name);
res_builder.SetValue(std::move(resource_value), config)
.SetId(res_id, OnIdConflict::CREATE_ENTRY)
@@ -533,9 +546,8 @@
// Since a the finalized resource entry is cloned and added to the resource table under the
// staged resource id, remove the cloned resource entry from the table.
if (!table_->RemoveResource(resource_name, staged_id)) {
- diag_->Error(DiagMessage(source_) << "failed to find resource entry for staged "
- << " resource ID " << staged_id);
- return false;
+ // If we haven't seen this resource yet let's add a record to skip it when parsing.
+ staged_entries_to_remove_.insert({resource_name, staged_id});
}
}
return true;
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.h b/tools/aapt2/format/binary/BinaryResourceParser.h
index cd71d16..1c83166 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.h
+++ b/tools/aapt2/format/binary/BinaryResourceParser.h
@@ -119,6 +119,10 @@
// A mapping of resource ID to type spec flags.
std::unordered_map<ResourceId, uint32_t> entry_type_spec_flags_;
+
+ // A collection of staged resources that got finalized already and we're supposed to prune -
+ // but the original staged resource record hasn't been parsed yet.
+ std::set<std::pair<ResourceName, ResourceId>> staged_entries_to_remove_;
};
} // namespace aapt