Merge "Add userId param to AccountManagerService.getApplicationLabel."
diff --git a/Android.bp b/Android.bp
index e1cb037..82da1e6 100644
--- a/Android.bp
+++ b/Android.bp
@@ -154,6 +154,7 @@
"framework-scheduling.stubs.module_lib",
"framework-sdkextensions.stubs.module_lib",
"framework-statsd.stubs.module_lib",
+ "framework-supplementalprocess.stubs.module_lib",
"framework-tethering.stubs.module_lib",
"framework-uwb.stubs.module_lib",
"framework-wifi.stubs.module_lib",
@@ -177,6 +178,7 @@
"framework-scheduling.impl",
"framework-sdkextensions.impl",
"framework-statsd.impl",
+ "framework-supplementalprocess.impl",
"framework-tethering.impl",
"framework-uwb.impl",
"framework-wifi.impl",
@@ -441,11 +443,8 @@
"core/java/android/util/LocalLog.java",
"core/java/com/android/internal/util/HexDump.java",
"core/java/com/android/internal/util/IndentingPrintWriter.java",
- "core/java/com/android/internal/util/IState.java",
"core/java/com/android/internal/util/MessageUtils.java",
"core/java/com/android/internal/util/RingBufferIndices.java",
- "core/java/com/android/internal/util/State.java",
- "core/java/com/android/internal/util/StateMachine.java",
"core/java/com/android/internal/util/WakeupMessage.java",
"core/java/com/android/internal/util/TokenBucket.java",
],
diff --git a/ApiDocs.bp b/ApiDocs.bp
index 31f7f6e..7a4ef2a 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -126,6 +126,7 @@
":framework-scheduling-sources",
":framework-sdkextensions-sources",
":framework-statsd-sources",
+ ":framework-supplementalprocess-sources",
":framework-tethering-srcs",
":framework-uwb-updatable-sources",
":framework-wifi-updatable-sources",
@@ -172,6 +173,7 @@
":framework-scheduling{.public.stubs.source}",
":framework-sdkextensions{.public.stubs.source}",
":framework-statsd{.public.stubs.source}",
+ ":framework-supplementalprocess{.public.stubs.source}",
":framework-tethering{.public.stubs.source}",
":framework-uwb{.public.stubs.source}",
":framework-wifi{.public.stubs.source}",
@@ -211,6 +213,7 @@
":framework-scheduling{.public.annotations.zip}",
":framework-sdkextensions{.public.annotations.zip}",
":framework-statsd{.public.annotations.zip}",
+ ":framework-supplementalprocess{.public.annotations.zip}",
":framework-tethering{.public.annotations.zip}",
":framework-uwb{.public.annotations.zip}",
":framework-wifi{.public.annotations.zip}",
diff --git a/StubLibraries.bp b/StubLibraries.bp
index cc118f3..b6fd708 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -250,6 +250,7 @@
"framework-scheduling.stubs",
"framework-sdkextensions.stubs",
"framework-statsd.stubs",
+ "framework-supplementalprocess.stubs",
"framework-tethering.stubs",
"framework-uwb.stubs",
"framework-wifi.stubs",
@@ -271,6 +272,7 @@
"framework-scheduling.stubs.system",
"framework-sdkextensions.stubs.system",
"framework-statsd.stubs.system",
+ "framework-supplementalprocess.stubs",
"framework-tethering.stubs.system",
"framework-uwb.stubs.system",
"framework-wifi.stubs.system",
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 31a5a050..392df73 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -490,8 +490,8 @@
* holding the AlarmManagerService.mLock lock.
*/
@VisibleForTesting
- final class Constants extends ContentObserver
- implements DeviceConfig.OnPropertiesChangedListener {
+ final class Constants implements DeviceConfig.OnPropertiesChangedListener,
+ EconomyManagerInternal.TareStateChangeListener {
@VisibleForTesting
static final int MAX_EXACT_ALARM_DENY_LIST_SIZE = 250;
@@ -695,7 +695,6 @@
private int mVersion = 0;
Constants(Handler handler) {
- super(handler);
updateAllowWhileIdleWhitelistDurationLocked();
for (int i = 0; i < APP_STANDBY_QUOTAS.length; i++) {
APP_STANDBY_QUOTAS[i] = DEFAULT_APP_STANDBY_QUOTAS[i];
@@ -709,11 +708,12 @@
}
public void start() {
- mInjector.registerContentObserver(this,
- Settings.Global.getUriFor(Settings.Global.ENABLE_TARE));
mInjector.registerDeviceConfigListener(this);
+ final EconomyManagerInternal economyManagerInternal =
+ LocalServices.getService(EconomyManagerInternal.class);
+ economyManagerInternal.registerTareStateChangeListener(this);
onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_ALARM_MANAGER));
- updateTareSettings();
+ updateTareSettings(economyManagerInternal.isEnabled());
}
public void updateAllowWhileIdleWhitelistDurationLocked() {
@@ -886,15 +886,12 @@
}
@Override
- public void onChange(boolean selfChange) {
- updateTareSettings();
+ public void onTareEnabledStateChanged(boolean isTareEnabled) {
+ updateTareSettings(isTareEnabled);
}
- private void updateTareSettings() {
+ private void updateTareSettings(boolean isTareEnabled) {
synchronized (mLock) {
- final boolean isTareEnabled = Settings.Global.getInt(
- getContext().getContentResolver(),
- Settings.Global.ENABLE_TARE, Settings.Global.DEFAULT_ENABLE_TARE) == 1;
if (USE_TARE_POLICY != isTareEnabled) {
USE_TARE_POLICY = isTareEnabled;
final boolean changed = mAlarmStore.updateAlarmDeliveries(alarm -> {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 78140dc..714c90b 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -43,7 +43,6 @@
import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -54,7 +53,6 @@
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.ServiceInfo;
-import android.database.ContentObserver;
import android.net.Uri;
import android.os.BatteryStats;
import android.os.BatteryStatsInternal;
@@ -118,6 +116,7 @@
import com.android.server.job.restrictions.JobRestriction;
import com.android.server.job.restrictions.ThermalStatusRestriction;
import com.android.server.pm.UserManagerInternal;
+import com.android.server.tare.EconomyManagerInternal;
import com.android.server.usage.AppStandbyInternal;
import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
import com.android.server.utils.quota.Categorizer;
@@ -356,41 +355,22 @@
// (ScheduledJobStateChanged and JobStatusDumpProto).
public static final int RESTRICTED_INDEX = 5;
- private class ConstantsObserver extends ContentObserver
- implements DeviceConfig.OnPropertiesChangedListener {
- private final ContentResolver mContentResolver;
-
- ConstantsObserver(Handler handler, Context context) {
- super(handler);
- mContentResolver = context.getContentResolver();
- }
-
+ private class ConstantsObserver implements DeviceConfig.OnPropertiesChangedListener,
+ EconomyManagerInternal.TareStateChangeListener {
public void start() {
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
JobSchedulerBackgroundThread.getExecutor(), this);
- mContentResolver.registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.ENABLE_TARE), false, this);
+ final EconomyManagerInternal economyManagerInternal =
+ LocalServices.getService(EconomyManagerInternal.class);
+ economyManagerInternal.registerTareStateChangeListener(this);
// Load all the constants.
synchronized (mLock) {
- mConstants.updateSettingsConstantsLocked(mContentResolver);
+ mConstants.updateTareSettingsLocked(economyManagerInternal.isEnabled());
}
onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER));
}
@Override
- public void onChange(boolean selfChange) {
- synchronized (mLock) {
- if (mConstants.updateSettingsConstantsLocked(mContentResolver)) {
- for (int controller = 0; controller < mControllers.size(); controller++) {
- final StateController sc = mControllers.get(controller);
- sc.onConstantsUpdatedLocked();
- }
- onControllerStateChanged(null);
- }
- }
- }
-
- @Override
public void onPropertiesChanged(DeviceConfig.Properties properties) {
boolean apiQuotaScheduleUpdated = false;
boolean concurrencyUpdated = false;
@@ -465,6 +445,17 @@
}
}
}
+
+ @Override
+ public void onTareEnabledStateChanged(boolean isTareEnabled) {
+ if (mConstants.updateTareSettingsLocked(isTareEnabled)) {
+ for (int controller = 0; controller < mControllers.size(); controller++) {
+ final StateController sc = mControllers.get(controller);
+ sc.onConstantsUpdatedLocked();
+ }
+ onControllerStateChanged(null);
+ }
+ }
}
@VisibleForTesting
@@ -719,10 +710,8 @@
DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS));
}
- private boolean updateSettingsConstantsLocked(ContentResolver contentResolver) {
+ private boolean updateTareSettingsLocked(boolean isTareEnabled) {
boolean changed = false;
- final boolean isTareEnabled = Settings.Global.getInt(contentResolver,
- Settings.Global.ENABLE_TARE, Settings.Global.DEFAULT_ENABLE_TARE) == 1;
if (USE_TARE_POLICY != isTareEnabled) {
USE_TARE_POLICY = isTareEnabled;
changed = true;
@@ -1673,7 +1662,7 @@
mHandler = new JobHandler(context.getMainLooper());
mConstants = new Constants();
- mConstantsObserver = new ConstantsObserver(mHandler, context);
+ mConstantsObserver = new ConstantsObserver();
mJobSchedulerStub = new JobSchedulerStub();
mConcurrencyManager = new JobConcurrencyManager(this);
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java
index 29aa946..630f1e7 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java
@@ -119,6 +119,11 @@
boolean canAfford);
}
+ /** Listener for various TARE state changes. */
+ interface TareStateChangeListener {
+ void onTareEnabledStateChanged(boolean isTareEnabled);
+ }
+
/**
* Return {@code true} if the app is able to pay for the anticipated actions.
*/
@@ -130,6 +135,9 @@
*/
long getMaxDurationMs(int userId, @NonNull String pkgName, @NonNull ActionBill bill);
+ /** Returns true if TARE is enabled. */
+ boolean isEnabled();
+
/**
* Register an {@link AffordabilityChangeListener} to track when an app's ability to afford the
* indicated bill changes.
@@ -145,6 +153,16 @@
@NonNull AffordabilityChangeListener listener, @NonNull ActionBill bill);
/**
+ * Register a {@link TareStateChangeListener} to track when TARE's state changes.
+ */
+ void registerTareStateChangeListener(@NonNull TareStateChangeListener listener);
+
+ /**
+ * Unregister a {@link TareStateChangeListener} from being notified when TARE's state changes.
+ */
+ void unregisterTareStateChangeListener(@NonNull TareStateChangeListener listener);
+
+ /**
* Note that an instantaneous event has occurred. The event must be specified in one of the
* EconomicPolicies.
*
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 ed11097..437a101 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -53,6 +53,7 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
@@ -68,11 +69,13 @@
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.pm.UserManagerInternal;
+import com.android.server.tare.EconomyManagerInternal.TareStateChangeListener;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.CopyOnWriteArraySet;
/**
* Responsible for handling app's ARC count based on events, ensuring ARCs are credited when
@@ -169,6 +172,9 @@
@GuardedBy("mPackageToUidCache")
private final SparseArrayMap<String, Integer> mPackageToUidCache = new SparseArrayMap<>();
+ private final CopyOnWriteArraySet<TareStateChangeListener> mStateChangeListeners =
+ new CopyOnWriteArraySet<>();
+
/** List of packages that are "exempted" from battery restrictions. */
// TODO(144864180): include userID
@GuardedBy("mLock")
@@ -261,6 +267,7 @@
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_FORCE_RECLAIM = 3;
+ private static final int MSG_NOTIFY_STATE_CHANGE_LISTENERS = 4;
private static final String ALARM_TAG_WEALTH_RECLAMATION = "*tare.reclamation*";
/**
@@ -802,6 +809,13 @@
}
break;
+ case MSG_NOTIFY_STATE_CHANGE_LISTENERS: {
+ for (TareStateChangeListener listener : mStateChangeListeners) {
+ listener.onTareEnabledStateChanged(mIsEnabled);
+ }
+ }
+ break;
+
case MSG_PROCESS_USAGE_EVENT: {
final int userId = msg.arg1;
final UsageEvents.Event event = (UsageEvents.Event) msg.obj;
@@ -892,6 +906,16 @@
}
@Override
+ public void registerTareStateChangeListener(@NonNull TareStateChangeListener listener) {
+ mStateChangeListeners.add(listener);
+ }
+
+ @Override
+ public void unregisterTareStateChangeListener(@NonNull TareStateChangeListener listener) {
+ mStateChangeListeners.remove(listener);
+ }
+
+ @Override
public boolean canPayFor(int userId, @NonNull String pkgName, @NonNull ActionBill bill) {
if (!mIsEnabled) {
return true;
@@ -944,6 +968,11 @@
}
@Override
+ public boolean isEnabled() {
+ return mIsEnabled;
+ }
+
+ @Override
public void noteInstantaneousEvent(int userId, @NonNull String pkgName, int eventId,
@Nullable String tag) {
if (!mIsEnabled) {
@@ -980,7 +1009,10 @@
}
}
- private class ConfigObserver extends ContentObserver {
+ private class ConfigObserver extends ContentObserver
+ implements DeviceConfig.OnPropertiesChangedListener {
+ private static final String KEY_DC_ENABLE_TARE = "enable_tare";
+
private final ContentResolver mContentResolver;
ConfigObserver(Handler handler, Context context) {
@@ -989,12 +1021,15 @@
}
public void start() {
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_TARE,
+ TareHandlerThread.getExecutor(), this);
mContentResolver.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.ENABLE_TARE), false, this);
mContentResolver.registerContentObserver(
Settings.Global.getUriFor(TARE_ALARM_MANAGER_CONSTANTS), false, this);
mContentResolver.registerContentObserver(
Settings.Global.getUriFor(TARE_JOB_SCHEDULER_CONSTANTS), false, this);
+ onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_TARE));
updateEnabledStatus();
}
@@ -1008,9 +1043,31 @@
}
}
+ @Override
+ public void onPropertiesChanged(DeviceConfig.Properties properties) {
+ synchronized (mLock) {
+ for (String name : properties.getKeyset()) {
+ if (name == null) {
+ continue;
+ }
+ switch (name) {
+ case KEY_DC_ENABLE_TARE:
+ updateEnabledStatus();
+ break;
+ }
+ }
+ }
+ }
+
private void updateEnabledStatus() {
+ // User setting should override DeviceConfig setting.
+ // NOTE: There's currently no way for a user to reset the value (via UI), so if a user
+ // manually toggles TARE via UI, we'll always defer to the user's current setting
+ // TODO: add a "reset" value if the user toggle is an issue
+ final boolean isTareEnabledDC = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_TARE,
+ KEY_DC_ENABLE_TARE, Settings.Global.DEFAULT_ENABLE_TARE == 1);
final boolean isTareEnabled = Settings.Global.getInt(mContentResolver,
- Settings.Global.ENABLE_TARE, Settings.Global.DEFAULT_ENABLE_TARE) == 1;
+ Settings.Global.ENABLE_TARE, isTareEnabledDC ? 1 : 0) == 1;
if (mIsEnabled != isTareEnabled) {
mIsEnabled = isTareEnabled;
if (mIsEnabled) {
@@ -1018,6 +1075,7 @@
} else {
tearDownEverything();
}
+ mHandler.sendEmptyMessage(MSG_NOTIFY_STATE_CHANGE_LISTENERS);
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/TareHandlerThread.java b/apex/jobscheduler/service/java/com/android/server/tare/TareHandlerThread.java
index 5b2b660..65ef8bf 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/TareHandlerThread.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/TareHandlerThread.java
@@ -17,10 +17,13 @@
package com.android.server.tare;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Trace;
+import java.util.concurrent.Executor;
+
/**
* Singleton thread for all of TARE.
*
@@ -29,6 +32,7 @@
final class TareHandlerThread extends HandlerThread {
private static TareHandlerThread sInstance;
+ private static Executor sHandlerExecutor;
private static Handler sHandler;
private TareHandlerThread() {
@@ -42,6 +46,7 @@
final Looper looper = sInstance.getLooper();
looper.setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
sHandler = new Handler(sInstance.getLooper());
+ sHandlerExecutor = new HandlerExecutor(sHandler);
}
}
@@ -52,6 +57,14 @@
return sInstance;
}
+ /** Returns the singleton handler executor for TareHandlerThread */
+ public static Executor getExecutor() {
+ synchronized (TareHandlerThread.class) {
+ ensureThreadLocked();
+ return sHandlerExecutor;
+ }
+ }
+
/** Returns the singleton handler for TareHandlerThread. */
public static Handler getHandler() {
synchronized (TareHandlerThread.class) {
diff --git a/api/Android.bp b/api/Android.bp
index 1bc50bd..1ec1b3c 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -106,6 +106,7 @@
":framework-scheduling{.public.api.txt}",
":framework-sdkextensions{.public.api.txt}",
":framework-statsd{.public.api.txt}",
+ ":framework-supplementalprocess{.public.api.txt}",
":framework-tethering{.public.api.txt}",
":framework-uwb{.public.api.txt}",
":framework-wifi{.public.api.txt}",
@@ -167,6 +168,7 @@
":framework-scheduling{.public.stubs.source}",
":framework-sdkextensions{.public.stubs.source}",
":framework-statsd{.public.stubs.source}",
+ ":framework-supplementalprocess{.public.stubs.source}",
":framework-tethering{.public.stubs.source}",
":framework-uwb{.public.stubs.source}",
":framework-wifi{.public.stubs.source}",
@@ -195,6 +197,7 @@
":framework-scheduling{.public.removed-api.txt}",
":framework-sdkextensions{.public.removed-api.txt}",
":framework-statsd{.public.removed-api.txt}",
+ ":framework-supplementalprocess{.public.removed-api.txt}",
":framework-tethering{.public.removed-api.txt}",
":framework-uwb{.public.removed-api.txt}",
":framework-wifi{.public.removed-api.txt}",
@@ -237,6 +240,7 @@
":framework-scheduling{.system.api.txt}",
":framework-sdkextensions{.system.api.txt}",
":framework-statsd{.system.api.txt}",
+ ":framework-supplementalprocess{.system.api.txt}",
":framework-tethering{.system.api.txt}",
":framework-uwb{.system.api.txt}",
":framework-wifi{.system.api.txt}",
@@ -297,6 +301,7 @@
":framework-scheduling{.system.removed-api.txt}",
":framework-sdkextensions{.system.removed-api.txt}",
":framework-statsd{.system.removed-api.txt}",
+ ":framework-supplementalprocess{.system.removed-api.txt}",
":framework-tethering{.system.removed-api.txt}",
":framework-uwb{.system.removed-api.txt}",
":framework-wifi{.system.removed-api.txt}",
@@ -339,6 +344,7 @@
":framework-scheduling{.module-lib.api.txt}",
":framework-sdkextensions{.module-lib.api.txt}",
":framework-statsd{.module-lib.api.txt}",
+ ":framework-supplementalprocess{.module-lib.api.txt}",
":framework-tethering{.module-lib.api.txt}",
":framework-uwb{.module-lib.api.txt}",
":framework-wifi{.module-lib.api.txt}",
@@ -401,6 +407,7 @@
":framework-scheduling{.module-lib.removed-api.txt}",
":framework-sdkextensions{.module-lib.removed-api.txt}",
":framework-statsd{.module-lib.removed-api.txt}",
+ ":framework-supplementalprocess{.module-lib.removed-api.txt}",
":framework-tethering{.module-lib.removed-api.txt}",
":framework-uwb{.module-lib.removed-api.txt}",
":framework-wifi{.module-lib.removed-api.txt}",
@@ -521,6 +528,7 @@
":framework-scheduling.stubs{.jar}",
":framework-sdkextensions.stubs{.jar}",
":framework-statsd.stubs{.jar}",
+ ":framework-supplementalprocess.stubs{.jar}",
":framework-tethering.stubs{.jar}",
":framework-uwb.stubs{.jar}",
":framework-wifi.stubs{.jar}",
diff --git a/boot/Android.bp b/boot/Android.bp
index d88e839..3273f2c 100644
--- a/boot/Android.bp
+++ b/boot/Android.bp
@@ -96,6 +96,10 @@
module: "com.android.sdkext-bootclasspath-fragment",
},
{
+ apex: "com.android.supplementalprocess",
+ module: "com.android.supplementalprocess-bootclasspath-fragment",
+ },
+ {
apex: "com.android.tethering",
module: "com.android.tethering-bootclasspath-fragment",
},
diff --git a/config/README.md b/config/README.md
index 5597ae2..450a5c6 100644
--- a/config/README.md
+++ b/config/README.md
@@ -1,7 +1,7 @@
# Configuration files for ART compiling the framework
* boot-image-profile.txt: A list of methods from the boot classpath to be compiled by dex2oat.
- The order in the file is not relelvant.
+ The order in the file is not relevant.
* boot-profile.txt: An ordered list of methods from the boot classpath to be compiled by
the JIT in the order provided in the file. Used by JIT zygote, when on-device
signing failed.
diff --git a/core/api/current.txt b/core/api/current.txt
index fe0a85d..5656c31 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -411,6 +411,7 @@
field public static final int calendarViewShown = 16843596; // 0x101034c
field public static final int calendarViewStyle = 16843613; // 0x101035d
field public static final int canControlMagnification = 16844039; // 0x1010507
+ field public static final int canDisplayOnRemoteDevices;
field public static final int canPauseRecording = 16844314; // 0x101061a
field public static final int canPerformGestures = 16844045; // 0x101050d
field public static final int canRecord = 16844060; // 0x101051c
@@ -31676,6 +31677,7 @@
method public float readFloat();
method public void readFloatArray(@NonNull float[]);
method @Nullable public java.util.HashMap readHashMap(@Nullable ClassLoader);
+ method @Nullable public <K, V> java.util.HashMap<K,V> readHashMap(@Nullable ClassLoader, @NonNull Class<? extends K>, @NonNull Class<? extends V>);
method public int readInt();
method public void readIntArray(@NonNull int[]);
method public <T extends android.os.IInterface> void readInterfaceArray(@NonNull T[], @NonNull java.util.function.Function<android.os.IBinder,T>);
@@ -31685,6 +31687,7 @@
method public long readLong();
method public void readLongArray(@NonNull long[]);
method public void readMap(@NonNull java.util.Map, @Nullable ClassLoader);
+ method public <K, V> void readMap(@NonNull java.util.Map<? super K,? super V>, @Nullable ClassLoader, @NonNull Class<K>, @NonNull Class<V>);
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);
@@ -40937,6 +40940,7 @@
method @NonNull public java.util.List<java.lang.Integer> getBands();
method @NonNull public java.util.List<java.lang.String> getMccMncs();
method public int getPriority();
+ method @NonNull public java.util.List<android.telephony.RadioAccessSpecifier> getRadioAccessSpecifiers();
method public int getSubId();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.AvailableNetworkInfo> CREATOR;
@@ -40945,6 +40949,14 @@
field public static final int PRIORITY_MED = 2; // 0x2
}
+ public static final class AvailableNetworkInfo.Builder {
+ ctor public AvailableNetworkInfo.Builder(int);
+ method @NonNull public android.telephony.AvailableNetworkInfo build();
+ method @NonNull public android.telephony.AvailableNetworkInfo.Builder setMccMncs(@NonNull java.util.List<java.lang.String>);
+ method @NonNull public android.telephony.AvailableNetworkInfo.Builder setPriority(int);
+ method @NonNull public android.telephony.AvailableNetworkInfo.Builder setRadioAccessSpecifiers(@NonNull java.util.List<android.telephony.RadioAccessSpecifier>);
+ }
+
public final class BarringInfo implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.telephony.BarringInfo.BarringServiceInfo getBarringServiceInfo(int);
@@ -41053,6 +41065,7 @@
field public static final String KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL = "carrier_rcs_provisioning_required_bool";
field public static final String KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING = "carrier_settings_activity_component_name_string";
field public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool";
+ field public static final String KEY_CARRIER_SUPPORTS_OPP_DATA_AUTO_PROVISIONING_BOOL = "carrier_supports_opp_data_auto_provisioning_bool";
field public static final String KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL = "carrier_supports_ss_over_ut_bool";
field public static final String KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL = "carrier_use_ims_first_for_emergency_bool";
field public static final String KEY_CARRIER_USSD_METHOD_INT = "carrier_ussd_method_int";
@@ -41114,6 +41127,8 @@
field public static final String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
field public static final String KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL = "enhanced_4g_lte_on_by_default_bool";
field public static final String KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT = "enhanced_4g_lte_title_variant_int";
+ field public static final String KEY_ESIM_DOWNLOAD_RETRY_BACKOFF_TIMER_SEC_INT = "esim_download_retry_backoff_timer_sec_int";
+ field public static final String KEY_ESIM_MAX_DOWNLOAD_RETRY_ATTEMPTS_INT = "esim_max_download_retry_attempts_int";
field public static final String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
field public static final String KEY_GSM_DTMF_TONE_DELAY_INT = "gsm_dtmf_tone_delay_int";
field public static final String KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY = "gsm_nonroaming_networks_string_array";
@@ -41216,6 +41231,7 @@
field public static final String KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL = "show_wfc_location_privacy_policy_bool";
field public static final String KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL = "simplified_network_settings_bool";
field public static final String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
+ field public static final String KEY_SMDP_SERVER_ADDRESS_STRING = "smdp_server_address_string";
field public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool";
field public static final String KEY_SUPPORTS_CALL_COMPOSER_BOOL = "supports_call_composer_bool";
field public static final String KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL = "supports_device_to_device_communication_using_dtmf_bool";
@@ -52256,6 +52272,7 @@
method public boolean commitContent(@NonNull android.view.inputmethod.InputContentInfo, int, @Nullable android.os.Bundle);
method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public boolean commitText(CharSequence, int);
+ method public default boolean commitText(@NonNull CharSequence, int, @Nullable android.view.inputmethod.TextAttribute);
method public boolean deleteSurroundingText(int, int);
method public boolean deleteSurroundingTextInCodePoints(int, int);
method public boolean endBatchEdit();
@@ -52275,7 +52292,9 @@
method public boolean requestCursorUpdates(int);
method public boolean sendKeyEvent(android.view.KeyEvent);
method public boolean setComposingRegion(int, int);
+ method public default boolean setComposingRegion(int, int, @Nullable android.view.inputmethod.TextAttribute);
method public boolean setComposingText(CharSequence, int);
+ method public default boolean setComposingText(@NonNull CharSequence, int, @Nullable android.view.inputmethod.TextAttribute);
method public default boolean setImeConsumesInput(boolean);
method public boolean setSelection(int, int);
method @Nullable public default android.view.inputmethod.TextSnapshot takeSnapshot();
@@ -52491,6 +52510,21 @@
field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.SurroundingText> CREATOR;
}
+ public final class TextAttribute implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.os.PersistableBundle getExtras();
+ method @NonNull public java.util.List<java.lang.String> getTextConversionSuggestions();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.TextAttribute> CREATOR;
+ }
+
+ public static final class TextAttribute.TextAttributeBuilder {
+ ctor public TextAttribute.TextAttributeBuilder();
+ method @NonNull public android.view.inputmethod.TextAttribute build();
+ method @NonNull public android.view.inputmethod.TextAttribute.TextAttributeBuilder setExtras(@NonNull android.os.PersistableBundle);
+ method @NonNull public android.view.inputmethod.TextAttribute.TextAttributeBuilder setTextConversionSuggestions(@NonNull java.util.List<java.lang.String>);
+ }
+
public final class TextSnapshot {
ctor public TextSnapshot(@NonNull android.view.inputmethod.SurroundingText, @IntRange(from=0xffffffff) int, @IntRange(from=0xffffffff) int, int);
method @IntRange(from=0xffffffff) public int getCompositionEnd();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 2647d90..d0a73bd 100755
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -14,6 +14,7 @@
field public static final String ACCESS_MTP = "android.permission.ACCESS_MTP";
field public static final String ACCESS_NETWORK_CONDITIONS = "android.permission.ACCESS_NETWORK_CONDITIONS";
field public static final String ACCESS_NOTIFICATIONS = "android.permission.ACCESS_NOTIFICATIONS";
+ field public static final String ACCESS_PDB_STATE = "android.permission.ACCESS_PDB_STATE";
field public static final String ACCESS_RCS_USER_CAPABILITY_EXCHANGE = "android.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE";
field public static final String ACCESS_SHARED_LIBRARIES = "android.permission.ACCESS_SHARED_LIBRARIES";
field public static final String ACCESS_SHORTCUTS = "android.permission.ACCESS_SHORTCUTS";
@@ -85,6 +86,7 @@
field public static final String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA";
field public static final String COMPANION_APPROVE_WIFI_CONNECTIONS = "android.permission.COMPANION_APPROVE_WIFI_CONNECTIONS";
field public static final String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
+ field public static final String CONFIGURE_INTERACT_ACROSS_PROFILES = "android.permission.CONFIGURE_INTERACT_ACROSS_PROFILES";
field public static final String CONFIGURE_WIFI_DISPLAY = "android.permission.CONFIGURE_WIFI_DISPLAY";
field @Deprecated public static final String CONNECTIVITY_INTERNAL = "android.permission.CONNECTIVITY_INTERNAL";
field public static final String CONNECTIVITY_USE_RESTRICTED_NETWORKS = "android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS";
@@ -121,6 +123,7 @@
field public static final String INJECT_EVENTS = "android.permission.INJECT_EVENTS";
field public static final String INSTALL_DPC_PACKAGES = "android.permission.INSTALL_DPC_PACKAGES";
field public static final String INSTALL_DYNAMIC_SYSTEM = "android.permission.INSTALL_DYNAMIC_SYSTEM";
+ field public static final String INSTALL_EXISTING_PACKAGES = "com.android.permission.INSTALL_EXISTING_PACKAGES";
field public static final String INSTALL_GRANT_RUNTIME_PERMISSIONS = "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS";
field public static final String INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE = "android.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE";
field public static final String INSTALL_PACKAGE_UPDATES = "android.permission.INSTALL_PACKAGE_UPDATES";
@@ -147,12 +150,14 @@
field public static final String MANAGE_CONTENT_CAPTURE = "android.permission.MANAGE_CONTENT_CAPTURE";
field public static final String MANAGE_CONTENT_SUGGESTIONS = "android.permission.MANAGE_CONTENT_SUGGESTIONS";
field public static final String MANAGE_DEBUGGING = "android.permission.MANAGE_DEBUGGING";
+ field public static final String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION";
field public static final String MANAGE_HOTWORD_DETECTION = "android.permission.MANAGE_HOTWORD_DETECTION";
field public static final String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
field public static final String MANAGE_MUSIC_RECOGNITION = "android.permission.MANAGE_MUSIC_RECOGNITION";
field public static final String MANAGE_NOTIFICATION_LISTENERS = "android.permission.MANAGE_NOTIFICATION_LISTENERS";
field public static final String MANAGE_ONE_TIME_PERMISSION_SESSIONS = "android.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS";
+ field public static final String MANAGE_PROFILE_AND_DEVICE_OWNERS = "android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS";
field public static final String MANAGE_ROLE_HOLDERS = "android.permission.MANAGE_ROLE_HOLDERS";
field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
field public static final String MANAGE_ROTATION_RESOLVER = "android.permission.MANAGE_ROTATION_RESOLVER";
@@ -169,6 +174,7 @@
field public static final String MANAGE_USERS = "android.permission.MANAGE_USERS";
field public static final String MANAGE_USER_OEM_UNLOCK_STATE = "android.permission.MANAGE_USER_OEM_UNLOCK_STATE";
field public static final String MANAGE_WIFI_COUNTRY_CODE = "android.permission.MANAGE_WIFI_COUNTRY_CODE";
+ field public static final String MARK_DEVICE_ORGANIZATION_OWNED = "android.permission.MARK_DEVICE_ORGANIZATION_OWNED";
field public static final String MODIFY_APPWIDGET_BIND_PERMISSIONS = "android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS";
field public static final String MODIFY_AUDIO_ROUTING = "android.permission.MODIFY_AUDIO_ROUTING";
field public static final String MODIFY_CELL_BROADCASTS = "android.permission.MODIFY_CELL_BROADCASTS";
@@ -247,6 +253,7 @@
field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
field public static final String RENOUNCE_PERMISSIONS = "android.permission.RENOUNCE_PERMISSIONS";
field public static final String REQUEST_COMPANION_PROFILE_APP_STREAMING = "android.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING";
+ field public static final String REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION = "android.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION";
field public static final String REQUEST_NETWORK_SCORES = "android.permission.REQUEST_NETWORK_SCORES";
field public static final String REQUEST_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE";
field public static final String RESET_PASSWORD = "android.permission.RESET_PASSWORD";
@@ -300,6 +307,7 @@
field public static final String UPDATE_TIME_ZONE_RULES = "android.permission.UPDATE_TIME_ZONE_RULES";
field public static final String UPGRADE_RUNTIME_PERMISSIONS = "android.permission.UPGRADE_RUNTIME_PERMISSIONS";
field public static final String USER_ACTIVITY = "android.permission.USER_ACTIVITY";
+ field public static final String USE_COLORIZED_NOTIFICATIONS = "android.permission.USE_COLORIZED_NOTIFICATIONS";
field public static final String USE_RESERVED_DISK = "android.permission.USE_RESERVED_DISK";
field public static final String UWB_PRIVILEGED = "android.permission.UWB_PRIVILEGED";
field public static final String WHITELIST_AUTO_REVOKE_PERMISSIONS = "android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS";
@@ -369,6 +377,7 @@
field public static final int config_defaultCallScreening = 17039398; // 0x1040026
field public static final int config_defaultDialer = 17039395; // 0x1040023
field public static final int config_defaultSms = 17039396; // 0x1040024
+ field public static final int config_deviceManager;
field public static final int config_feedbackIntentExtraKey = 17039391; // 0x104001f
field public static final int config_feedbackIntentNameKey = 17039392; // 0x1040020
field public static final int config_helpIntentExtraKey = 17039389; // 0x104001d
@@ -949,7 +958,7 @@
method @Nullable public android.content.Intent createProvisioningIntentFromNfcIntent(@NonNull android.content.Intent);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean getBluetoothContactSharingDisabled(@NonNull android.os.UserHandle);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwner();
- method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, "android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"}) public android.content.ComponentName getDeviceOwnerComponentOnAnyUser();
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public android.content.ComponentName getDeviceOwnerComponentOnAnyUser();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwnerNameOnAnyUser();
method @Nullable public CharSequence getDeviceOwnerOrganizationName();
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.UserHandle getDeviceOwnerUser();
@@ -967,7 +976,7 @@
method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long);
method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long, boolean);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public boolean packageHasActiveAdmins(String);
- method @Deprecated @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public boolean setActiveProfileOwner(@NonNull android.content.ComponentName, String) throws java.lang.IllegalArgumentException;
+ method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public boolean setActiveProfileOwner(@NonNull android.content.ComponentName, String) throws java.lang.IllegalArgumentException;
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setDeviceProvisioningConfigApplied();
method @Deprecated @RequiresPermission(value=android.Manifest.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS, conditional=true) public void setProfileOwnerCanAccessDeviceIds(@NonNull android.content.ComponentName);
method public void setSecondaryLockscreenEnabled(@NonNull android.content.ComponentName, boolean);
@@ -2334,6 +2343,7 @@
public final class AssociationRequest implements android.os.Parcelable {
field @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING) public static final String DEVICE_PROFILE_APP_STREAMING = "android.app.role.COMPANION_DEVICE_APP_STREAMING";
+ field @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION) public static final String DEVICE_PROFILE_AUTOMOTIVE_PROJECTION = "android.app.role.SYSTEM_AUTOMOTIVE_PROJECTION";
}
public final class CompanionDeviceManager {
@@ -10462,7 +10472,7 @@
package android.service.persistentdata {
public class PersistentDataBlockManager {
- method @RequiresPermission("android.permission.ACCESS_PDB_STATE") public int getDataBlockSize();
+ method @RequiresPermission(android.Manifest.permission.ACCESS_PDB_STATE) public int getDataBlockSize();
method @android.service.persistentdata.PersistentDataBlockManager.FlashLockState @RequiresPermission(anyOf={android.Manifest.permission.READ_OEM_UNLOCK_STATE, "android.permission.OEM_UNLOCK_STATE"}) public int getFlashLockState();
method public long getMaximumDataBlockSize();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_OEM_UNLOCK_STATE, "android.permission.OEM_UNLOCK_STATE"}) public boolean getOemUnlockEnabled();
diff --git a/core/api/system-removed.txt b/core/api/system-removed.txt
index 9a8a493..9cb9ddc 100644
--- a/core/api/system-removed.txt
+++ b/core/api/system-removed.txt
@@ -1,12 +1,4 @@
// Signature format: 2.0
-package android {
-
- public static final class Manifest.permission {
- field public static final String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
- }
-
-}
-
package android.app {
public class AppOpsManager {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 591fe55..5f87630 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -442,11 +442,11 @@
public class DevicePolicyManager {
method public int checkProvisioningPreCondition(@Nullable String, @NonNull String);
- method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void clearOrganizationId();
+ method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void clearOrganizationId();
method @RequiresPermission(android.Manifest.permission.CLEAR_FREEZE_PERIOD) public void clearSystemUpdatePolicyFreezePeriodRecord();
method @Nullable public android.os.UserHandle createAndProvisionManagedProfile(@NonNull android.app.admin.ManagedProfileProvisioningParams) throws android.app.admin.ProvisioningException;
method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceNetworkLogs();
- method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void forceRemoveActiveAdmin(@NonNull android.content.ComponentName, int);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void forceRemoveActiveAdmin(@NonNull android.content.ComponentName, int);
method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceSecurityLogs();
method public void forceUpdateUserSetupComplete(int);
method @NonNull public java.util.Set<java.lang.String> getDefaultCrossProfilePackages();
@@ -455,18 +455,18 @@
method public long getLastNetworkLogRetrievalTime();
method public long getLastSecurityLogRetrievalTime();
method public java.util.List<java.lang.String> getOwnerInstalledCaCerts(@NonNull android.os.UserHandle);
- method @NonNull @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public java.util.Set<java.lang.String> getPolicyExemptApps();
+ method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public java.util.Set<java.lang.String> getPolicyExemptApps();
method public boolean isCurrentInputMethodSetByOwner();
method public boolean isFactoryResetProtectionPolicySupported();
- method @RequiresPermission(anyOf={"android.permission.MARK_DEVICE_ORGANIZATION_OWNED", "android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"}, conditional=true) public void markProfileOwnerOnOrganizationOwnedDevice(@NonNull android.content.ComponentName);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MARK_DEVICE_ORGANIZATION_OWNED, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}, conditional=true) public void markProfileOwnerOnOrganizationOwnedDevice(@NonNull android.content.ComponentName);
method @NonNull public static String operationSafetyReasonToString(int);
method @NonNull public static String operationToString(int);
- method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
- method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void resetDefaultCrossProfileIntentFilters(int);
- method @RequiresPermission(allOf={"android.permission.MANAGE_DEVICE_ADMINS", android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void setActiveAdmin(@NonNull android.content.ComponentName, boolean, int);
- method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public boolean setDeviceOwner(@NonNull android.content.ComponentName, @Nullable String, int);
- method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public boolean setDeviceOwnerOnly(@NonNull android.content.ComponentName, @Nullable String, int);
- method @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public void setNextOperationSafety(int, int);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
+ method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void resetDefaultCrossProfileIntentFilters(int);
+ method @RequiresPermission(allOf={android.Manifest.permission.MANAGE_DEVICE_ADMINS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void setActiveAdmin(@NonNull android.content.ComponentName, boolean, int);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean setDeviceOwner(@NonNull android.content.ComponentName, @Nullable String, int);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean setDeviceOwnerOnly(@NonNull android.content.ComponentName, @Nullable String, int);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public void setNextOperationSafety(int, int);
field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED";
field public static final String ACTION_DEVICE_POLICY_CONSTANTS_CHANGED = "android.app.action.DEVICE_POLICY_CONSTANTS_CHANGED";
field public static final int CODE_ACCOUNTS_NOT_EMPTY = 6; // 0x6
@@ -799,6 +799,7 @@
field public static final float OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE = 1.7777778f;
field public static final long OVERRIDE_MIN_ASPECT_RATIO_MEDIUM = 180326845L; // 0xabf91bdL
field public static final float OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE = 1.5f;
+ field public static final long OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY = 203647190L; // 0xc2368d6L
field public static final int RESIZE_MODE_RESIZEABLE = 2; // 0x2
}
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 737360d..6a0f5c7 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -241,6 +241,14 @@
*/
public static final String EXTRA_NEW_WALLPAPER_ID = "android.service.wallpaper.extra.ID";
+ /**
+ * Extra passed on {@link Intent.ACTION_WALLPAPER_CHANGED} indicating if wallpaper was set from
+ * a foreground app.
+ * @hide
+ */
+ public static final String EXTRA_FROM_FOREGROUND_APP =
+ "android.service.wallpaper.extra.FROM_FOREGROUND_APP";
+
// flags for which kind of wallpaper to act on
/** @hide */
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java
index 24d1248..7d1aabc 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -94,6 +94,21 @@
public static final String DEVICE_PROFILE_APP_STREAMING =
"android.app.role.COMPANION_DEVICE_APP_STREAMING";
+ /**
+ * Device profile: Android Automotive Projection
+ *
+ * Only applications that have been granted
+ * {@link android.Manifest.permission#REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION} are
+ * allowed to request to be associated with such devices.
+ *
+ * @see AssociationRequest.Builder#setDeviceProfile
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION)
+ @SystemApi
+ public static final String DEVICE_PROFILE_AUTOMOTIVE_PROJECTION =
+ "android.app.role.SYSTEM_AUTOMOTIVE_PROJECTION";
+
/** @hide */
@StringDef(value = { DEVICE_PROFILE_WATCH })
public @interface DeviceProfile {}
@@ -498,10 +513,10 @@
};
@DataClass.Generated(
- time = 1634716126923L,
+ time = 1635190605212L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/companion/AssociationRequest.java",
- inputSignatures = "private static final java.lang.String LOG_TAG\npublic static final java.lang.String DEVICE_PROFILE_WATCH\npublic static final @android.annotation.RequiresPermission @android.annotation.SystemApi java.lang.String DEVICE_PROFILE_APP_STREAMING\nprivate boolean mSingleDevice\nprivate @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.String mCallingPackage\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\nprivate long mCreationTime\nprivate boolean mSkipPrompt\nprivate void onConstructed()\npublic void setCallingPackage(java.lang.String)\npublic void setDeviceProfilePrivilegesDescription(java.lang.String)\npublic void setSkipPrompt(boolean)\npublic @android.compat.annotation.UnsupportedAppUsage boolean isSingleDevice()\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genHiddenConstructor=true, genBuilder=false, genConstDefs=false)")
+ inputSignatures = "private static final java.lang.String LOG_TAG\npublic static final java.lang.String DEVICE_PROFILE_WATCH\npublic static final @android.annotation.RequiresPermission @android.annotation.SystemApi java.lang.String DEVICE_PROFILE_APP_STREAMING\npublic static final @android.annotation.RequiresPermission @android.annotation.SystemApi java.lang.String DEVICE_PROFILE_AUTOMOTIVE_PROJECTION\nprivate boolean mSingleDevice\nprivate @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.String mCallingPackage\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\nprivate long mCreationTime\nprivate boolean mSkipPrompt\nprivate void onConstructed()\npublic void setCallingPackage(java.lang.String)\npublic void setDeviceProfilePrivilegesDescription(java.lang.String)\npublic void setSkipPrompt(boolean)\npublic @android.compat.annotation.UnsupportedAppUsage boolean isSingleDevice()\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genHiddenConstructor=true, genBuilder=false, genConstDefs=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 665f626..e6df79e 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3000,12 +3000,17 @@
*
* @param receiver The BroadcastReceiver to handle the broadcast.
* @param filter Selects the Intent broadcasts to be received.
- * @param flags Additional options for the receiver. As of
- * Android T, either {@link #RECEIVER_EXPORTED} or
+ * @param flags Additional options for the receiver. For apps targeting
+ * {@link android.os.Build.VERSION_CODES#TIRAMISU},
+ * either {@link #RECEIVER_EXPORTED} or
* {@link #RECEIVER_NOT_EXPORTED} must be specified if the receiver isn't being registered
- * for protected broadcasts, and may additionally specify
- * {@link #RECEIVER_VISIBLE_TO_INSTANT_APPS} if {@link #RECEIVER_EXPORTED} is
- * specified.
+ * for protected broadcasts or an exception will be thrown. If
+ * {@link #RECEIVER_EXPORTED} is specified, a receiver may additionally
+ * specify {@link #RECEIVER_VISIBLE_TO_INSTANT_APPS}. For a complete list of
+ * protected broadcast actions, see the BROADCAST_ACTIONS.TXT file in the
+ * Android SDK. If both {@link #RECEIVER_EXPORTED} and
+ * {@link #RECEIVER_NOT_EXPORTED} are specified, an exception will be thrown as
+ * well.
*
* @return The first sticky intent found that matches <var>filter</var>,
* or null if there are none.
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index e838d93..14e43b3 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1148,6 +1148,10 @@
* numbers. Applications can <strong>dial</strong> emergency numbers using
* {@link #ACTION_DIAL}, however.
*
+ * <p>Note: An app filling the {@link android.app.role.RoleManager#ROLE_DIALER} role should use
+ * {@link android.telecom.TelecomManager#placeCall(Uri, Bundle)} to place calls rather than
+ * relying on this intent.
+ *
* <p>Note: if you app targets {@link android.os.Build.VERSION_CODES#M M}
* and above and declares as using the {@link android.Manifest.permission#CALL_PHONE}
* permission which is not granted, then attempting to use this action will
@@ -1857,14 +1861,19 @@
"android.intent.action.MANAGE_APP_PERMISSIONS";
/**
- * Activity action: Launch UI to manage a specific permissions of an app.
+ * Activity action: Launch UI to manage a specific permission group of an app.
* <p>
* Input: {@link #EXTRA_PACKAGE_NAME} specifies the package whose permission
* will be managed by the launched UI.
* </p>
* <p>
* Input: {@link #EXTRA_PERMISSION_NAME} specifies the (individual) permission
- * that should be managed by the launched UI.
+ * whose group should be managed by the launched UI.
+ * </p>
+ * <p>
+ * Input: {@link #EXTRA_PERMISSION_GROUP_NAME} specifies the permission group
+ * that should be managed by the launched UI. Do not send both this and EXTRA_PERMISSION_NAME
+ * together.
* </p>
* <p>
* <li> {@link #EXTRA_USER} specifies the {@link UserHandle} of the user that owns the app.
@@ -1875,6 +1884,7 @@
*
* @see #EXTRA_PACKAGE_NAME
* @see #EXTRA_PERMISSION_NAME
+ * @see #EXTRA_PERMISSION_GROUP_NAME
* @see #EXTRA_USER
*
* @hide
@@ -2163,14 +2173,15 @@
/**
* Activity action: Launch UI to manage which apps have a given permission.
* <p>
- * Input: {@link #EXTRA_PERMISSION_NAME} specifies the permission group
- * which will be managed by the launched UI.
+ * Input: {@link #EXTRA_PERMISSION_NAME} or {@link #EXTRA_PERMISSION_GROUP_NAME} specifies the
+ * permission group which will be managed by the launched UI.
* </p>
* <p>
* Output: Nothing.
* </p>
*
* @see #EXTRA_PERMISSION_NAME
+ * @see #EXTRA_PERMISSION_GROUP_NAME
*
* @hide
*/
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index e7ca76e..58f20ef 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -448,6 +448,12 @@
* @see android.app.Activity#setVrModeEnabled(boolean, ComponentName)
*/
public static final int FLAG_ENABLE_VR_MODE = 0x8000;
+ /**
+ * Bit in {@link #flags} indicating if the activity can be displayed on a remote device.
+ * Corresponds to {@link android.R.attr#canDisplayOnRemoteDevices}
+ * @hide
+ */
+ public static final int FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES = 0x10000;
/**
* Bit in {@link #flags} indicating if the activity is always focusable regardless of if it is
@@ -996,10 +1002,9 @@
* OVERRIDE_MIN_ASPECT_RATIO_MEDIUM
* OVERRIDE_MIN_ASPECT_RATIO_LARGE
*
- * If OVERRIDE_MIN_ASPECT_RATIO is applied, and the activity's orientation is fixed to
- * portrait, the min aspect ratio given in the app's manifest will be overridden to the
- * largest enabled aspect ratio treatment unless the app's manifest value is higher.
- * TODO(b/203647190): add OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY instead of portrait by default
+ * If OVERRIDE_MIN_ASPECT_RATIO is applied, the min aspect ratio given in the app's manifest
+ * will be overridden to the largest enabled aspect ratio treatment unless the app's manifest
+ * value is higher.
* @hide
*/
@ChangeId
@@ -1009,6 +1014,19 @@
public static final long OVERRIDE_MIN_ASPECT_RATIO = 174042980L; // buganizer id
/**
+ * This change id restricts treatments that force a given min aspect ratio to activities
+ * whose orientation is fixed to portrait.
+ *
+ * This treatment only takes effect if OVERRIDE_MIN_ASPECT_RATIO is also enabled.
+ * @hide
+ */
+ @ChangeId
+ @Overridable
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S_V2)
+ @TestApi
+ public static final long OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY = 203647190L; // buganizer id
+
+ /**
* This change id sets the activity's min aspect ratio to a medium value as defined by
* OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE.
*
@@ -1337,9 +1355,7 @@
*/
@SizeChangesSupportMode
public int supportsSizeChanges() {
- if (CompatChanges.isChangeEnabled(FORCE_NON_RESIZE_APP,
- applicationInfo.packageName,
- UserHandle.getUserHandleForUid(applicationInfo.uid))) {
+ if (isChangeEnabled(FORCE_NON_RESIZE_APP)) {
return SIZE_CHANGES_UNSUPPORTED_OVERRIDE;
}
@@ -1347,9 +1363,7 @@
return SIZE_CHANGES_SUPPORTED_METADATA;
}
- if (CompatChanges.isChangeEnabled(FORCE_RESIZE_APP,
- applicationInfo.packageName,
- UserHandle.getUserHandleForUid(applicationInfo.uid))) {
+ if (isChangeEnabled(FORCE_RESIZE_APP)) {
return SIZE_CHANGES_SUPPORTED_OVERRIDE;
}
@@ -1361,9 +1375,7 @@
* @hide
*/
public boolean neverSandboxDisplayApis() {
- return CompatChanges.isChangeEnabled(NEVER_SANDBOX_DISPLAY_APIS,
- applicationInfo.packageName,
- UserHandle.getUserHandleForUid(applicationInfo.uid))
+ return isChangeEnabled(NEVER_SANDBOX_DISPLAY_APIS)
|| ConstrainDisplayApisConfig.neverConstrainDisplayApis(applicationInfo);
}
@@ -1372,9 +1384,7 @@
* @hide
*/
public boolean alwaysSandboxDisplayApis() {
- return CompatChanges.isChangeEnabled(ALWAYS_SANDBOX_DISPLAY_APIS,
- applicationInfo.packageName,
- UserHandle.getUserHandleForUid(applicationInfo.uid))
+ return isChangeEnabled(ALWAYS_SANDBOX_DISPLAY_APIS)
|| ConstrainDisplayApisConfig.alwaysConstrainDisplayApis(applicationInfo);
}
@@ -1404,31 +1414,28 @@
* @hide
*/
public float getMinAspectRatio(@ScreenOrientation int orientation) {
- // TODO(b/203647190): check orientation only if OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY
- // In case the activity's orientation isn't fixed to portrait, OVERRIDE_MIN_ASPECT_RATIO
- // shouldn't be applied.
- if (applicationInfo == null || !CompatChanges.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO,
- applicationInfo.packageName,
- UserHandle.getUserHandleForUid(applicationInfo.uid))
- || !isFixedOrientationPortrait(orientation)) {
+ if (applicationInfo == null || !isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO) || (
+ isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY)
+ && !isFixedOrientationPortrait(orientation))) {
return mMinAspectRatio;
}
- if (CompatChanges.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_LARGE,
- applicationInfo.packageName,
- UserHandle.getUserHandleForUid(applicationInfo.uid))) {
+ if (isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_LARGE)) {
return Math.max(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE, mMinAspectRatio);
}
- if (CompatChanges.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_MEDIUM,
- applicationInfo.packageName,
- UserHandle.getUserHandleForUid(applicationInfo.uid))) {
+ if (isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_MEDIUM)) {
return Math.max(OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE, mMinAspectRatio);
}
return mMinAspectRatio;
}
+ private boolean isChangeEnabled(long changeId) {
+ return CompatChanges.isChangeEnabled(changeId, applicationInfo.packageName,
+ UserHandle.getUserHandleForUid(applicationInfo.uid));
+ }
+
/** @hide */
public float getManifestMinAspectRatio() {
return mMinAspectRatio;
@@ -1496,9 +1503,7 @@
* @hide
*/
public boolean shouldCheckMinWidthHeightForMultiWindow() {
- return CompatChanges.isChangeEnabled(CHECK_MIN_WIDTH_HEIGHT_FOR_MULTI_WINDOW,
- applicationInfo.packageName,
- UserHandle.getUserHandleForUid(applicationInfo.uid));
+ return isChangeEnabled(CHECK_MIN_WIDTH_HEIGHT_FOR_MULTI_WINDOW);
}
public void dump(Printer pw, String prefix) {
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 0a10aaa..e3a5de5 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -194,6 +194,8 @@
public static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect";
public static final String METADATA_SUPPORTS_SIZE_CHANGES = "android.supports_size_changes";
+ public static final String METADATA_CAN_DISPLAY_ON_REMOTE_DEVICES =
+ "android.can_display_on_remote_devices";
public static final String METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY =
"android.activity_window_layout_affinity";
public static final String METADATA_ACTIVITY_LAUNCH_MODE = "android.activity.launch_mode";
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
index 45241b0..2ddf923 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
@@ -420,6 +420,21 @@
}
}
+ if (!isAlias) {
+ // Default allow the activity to be displayed on a remote device unless it explicitly
+ // set to false.
+ boolean canDisplayOnRemoteDevices = array.getBoolean(
+ R.styleable.AndroidManifestActivity_canDisplayOnRemoteDevices, true);
+ if (activity.getMetaData() != null && !activity.getMetaData().getBoolean(
+ ParsingPackageUtils.METADATA_CAN_DISPLAY_ON_REMOTE_DEVICES, true)) {
+ canDisplayOnRemoteDevices = false;
+ }
+ if (canDisplayOnRemoteDevices) {
+ activity.setFlags(activity.getFlags()
+ | ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES);
+ }
+ }
+
ParseResult<ActivityInfo.WindowLayout> layoutResult =
resolveActivityWindowLayout(activity, input);
if (layoutResult.isError()) {
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index c8c122d..6b5bec9 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -408,6 +408,19 @@
}
/**
+ * Flag to decide if authentication should ignore enrollment state.
+ * Defaults to false (not ignoring enrollment state)
+ * @param ignoreEnrollmentState
+ * @return This builder.
+ * @hide
+ */
+ @NonNull
+ public Builder setIgnoreEnrollmentState(boolean ignoreEnrollmentState) {
+ mPromptInfo.setIgnoreEnrollmentState(ignoreEnrollmentState);
+ return this;
+ }
+
+ /**
* Creates a {@link BiometricPrompt}.
*
* @return An instance of {@link BiometricPrompt}.
diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java
index 339c654..e6b762a 100644
--- a/core/java/android/hardware/biometrics/PromptInfo.java
+++ b/core/java/android/hardware/biometrics/PromptInfo.java
@@ -45,6 +45,7 @@
private boolean mReceiveSystemEvents;
@NonNull private List<Integer> mAllowedSensorIds = new ArrayList<>();
private boolean mAllowBackgroundAuthentication;
+ private boolean mIgnoreEnrollmentState;
public PromptInfo() {
@@ -66,6 +67,7 @@
mReceiveSystemEvents = in.readBoolean();
mAllowedSensorIds = in.readArrayList(Integer.class.getClassLoader());
mAllowBackgroundAuthentication = in.readBoolean();
+ mIgnoreEnrollmentState = in.readBoolean();
}
public static final Creator<PromptInfo> CREATOR = new Creator<PromptInfo>() {
@@ -102,6 +104,7 @@
dest.writeBoolean(mReceiveSystemEvents);
dest.writeList(mAllowedSensorIds);
dest.writeBoolean(mAllowBackgroundAuthentication);
+ dest.writeBoolean(mIgnoreEnrollmentState);
}
public boolean containsTestConfigurations() {
@@ -192,6 +195,10 @@
mAllowBackgroundAuthentication = allow;
}
+ public void setIgnoreEnrollmentState(boolean ignoreEnrollmentState) {
+ mIgnoreEnrollmentState = ignoreEnrollmentState;
+ }
+
// Getters
public CharSequence getTitle() {
@@ -261,4 +268,8 @@
public boolean isAllowBackgroundAuthentication() {
return mAllowBackgroundAuthentication;
}
+
+ public boolean isIgnoreEnrollmentState() {
+ return mIgnoreEnrollmentState;
+ }
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index a3d595c..fe04e5d 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -531,7 +531,7 @@
@RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler) {
- authenticate(crypto, cancel, callback, handler, mContext.getUserId());
+ authenticate(crypto, cancel, callback, handler, SENSOR_ID_ANY, mContext.getUserId(), flags);
}
/**
@@ -541,7 +541,7 @@
@RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
@NonNull AuthenticationCallback callback, Handler handler, int userId) {
- authenticate(crypto, cancel, callback, handler, SENSOR_ID_ANY, userId);
+ authenticate(crypto, cancel, callback, handler, SENSOR_ID_ANY, userId, 0 /* flags */);
}
/**
@@ -550,7 +550,8 @@
*/
@RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
- @NonNull AuthenticationCallback callback, Handler handler, int sensorId, int userId) {
+ @NonNull AuthenticationCallback callback, Handler handler, int sensorId, int userId,
+ int flags) {
FrameworkStatsLog.write(FrameworkStatsLog.AUTH_DEPRECATED_API_USED,
AUTH_DEPRECATED_APIUSED__DEPRECATED_API__API_FINGERPRINT_MANAGER_AUTHENTICATE,
@@ -566,6 +567,8 @@
return;
}
+ final boolean ignoreEnrollmentState = flags == 0 ? false : true;
+
if (mService != null) {
try {
useHandler(handler);
@@ -573,7 +576,7 @@
mCryptoObject = crypto;
final long operationId = crypto != null ? crypto.getOpId() : 0;
final long authId = mService.authenticate(mToken, operationId, sensorId, userId,
- mServiceReceiver, mContext.getOpPackageName());
+ mServiceReceiver, mContext.getOpPackageName(), ignoreEnrollmentState);
if (cancel != null) {
cancel.setOnCancelListener(new OnAuthenticationCancelListener(authId));
}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index de94b2f..ba1dc6d 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -52,7 +52,8 @@
// permission. This is effectively deprecated, since it only comes through FingerprintManager
// now. A requestId is returned that can be used to cancel this operation.
long authenticate(IBinder token, long operationId, int sensorId, int userId,
- IFingerprintServiceReceiver receiver, String opPackageName);
+ IFingerprintServiceReceiver receiver, String opPackageName,
+ boolean shouldIgnoreEnrollmentState);
// Uses the fingerprint hardware to detect for the presence of a finger, without giving details
// about accept/reject/lockout. A requestId is returned that can be used to cancel this
diff --git a/core/java/android/inputmethodservice/RemoteInputConnection.java b/core/java/android/inputmethodservice/RemoteInputConnection.java
index ae97fe7..ed617af 100644
--- a/core/java/android/inputmethodservice/RemoteInputConnection.java
+++ b/core/java/android/inputmethodservice/RemoteInputConnection.java
@@ -31,6 +31,7 @@
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.SurroundingText;
+import android.view.inputmethod.TextAttribute;
import com.android.internal.inputmethod.CancellationGroup;
import com.android.internal.inputmethod.CompletableFutureUtil;
@@ -272,6 +273,17 @@
}
@AnyThread
+ public boolean commitText(@NonNull CharSequence text, int newCursorPosition,
+ @Nullable TextAttribute textAttribute) {
+ final boolean handled =
+ mInvoker.commitText(text, newCursorPosition, textAttribute);
+ if (handled) {
+ notifyUserActionIfNecessary();
+ }
+ return handled;
+ }
+
+ @AnyThread
private void notifyUserActionIfNecessary() {
final InputMethodServiceInternal imsInternal = mImsInternal.getAndWarnIfNull();
if (imsInternal == null) {
@@ -311,6 +323,11 @@
}
@AnyThread
+ public boolean setComposingRegion(int start, int end, @Nullable TextAttribute textAttribute) {
+ return mInvoker.setComposingRegion(start, end, textAttribute);
+ }
+
+ @AnyThread
public boolean setComposingText(CharSequence text, int newCursorPosition) {
final boolean handled = mInvoker.setComposingText(text, newCursorPosition);
if (handled) {
@@ -320,6 +337,16 @@
}
@AnyThread
+ public boolean setComposingText(CharSequence text, int newCursorPosition,
+ @Nullable TextAttribute textAttribute) {
+ final boolean handled = mInvoker.setComposingText(text, newCursorPosition, textAttribute);
+ if (handled) {
+ notifyUserActionIfNecessary();
+ }
+ return handled;
+ }
+
+ @AnyThread
public boolean finishComposingText() {
return mInvoker.finishComposingText();
}
diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java
index 7ef5bac..8605248 100644
--- a/core/java/android/net/IpSecAlgorithm.java
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -232,11 +232,10 @@
ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_HMAC_SHA512, SDK_VERSION_ZERO);
ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_CRYPT_AES_GCM, SDK_VERSION_ZERO);
- // STOPSHIP: b/170424293 Use Build.VERSION_CODES.S when it is defined
- ALGO_TO_REQUIRED_FIRST_SDK.put(CRYPT_AES_CTR, Build.VERSION_CODES.R + 1);
- ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_AES_XCBC, Build.VERSION_CODES.R + 1);
- ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_AES_CMAC, Build.VERSION_CODES.R + 1);
- ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_CRYPT_CHACHA20_POLY1305, Build.VERSION_CODES.R + 1);
+ ALGO_TO_REQUIRED_FIRST_SDK.put(CRYPT_AES_CTR, Build.VERSION_CODES.S);
+ ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_AES_XCBC, Build.VERSION_CODES.S);
+ ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_AES_CMAC, Build.VERSION_CODES.S);
+ ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_CRYPT_CHACHA20_POLY1305, Build.VERSION_CODES.S);
}
private static final Set<String> ENABLED_ALGOS =
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 5a2f27d..09e5a8f 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -2992,8 +2992,24 @@
* from the parcel at the current dataPosition().
*/
public final void readMap(@NonNull Map outVal, @Nullable ClassLoader loader) {
- int N = readInt();
- readMapInternal(outVal, N, loader);
+ int n = readInt();
+ readMapInternal(outVal, n, loader, /* clazzKey */ null, /* clazzValue */ null);
+ }
+
+ /**
+ * Same as {@link #readMap(Map, ClassLoader)} but accepts {@code clazzKey} and
+ * {@code clazzValue} parameter as the types required for each key and value pair.
+ *
+ * @throws BadParcelableException If the item to be deserialized is not an instance of that
+ * class or any of its children class
+ */
+ public <K, V> void readMap(@NonNull Map<? super K, ? super V> outVal,
+ @Nullable ClassLoader loader, @NonNull Class<K> clazzKey,
+ @NonNull Class<V> clazzValue) {
+ Objects.requireNonNull(clazzKey);
+ Objects.requireNonNull(clazzValue);
+ int n = readInt();
+ readMapInternal(outVal, n, loader, clazzKey, clazzValue);
}
/**
@@ -3031,16 +3047,38 @@
@Nullable
public final HashMap readHashMap(@Nullable ClassLoader loader)
{
- int N = readInt();
- if (N < 0) {
+ int n = readInt();
+ if (n < 0) {
return null;
}
- HashMap m = new HashMap(N);
- readMapInternal(m, N, loader);
+ HashMap m = new HashMap(n);
+ readMapInternal(m, n, loader, /* clazzKey */ null, /* clazzValue */ null);
return m;
}
/**
+ * Same as {@link #readHashMap(ClassLoader)} but accepts {@code clazzKey} and
+ * {@code clazzValue} parameter as the types required for each key and value pair.
+ *
+ * @throws BadParcelableException if the item to be deserialized is not an instance of that
+ * class or any of its children class
+ */
+ @SuppressLint({"ConcreteCollection", "NullableCollection"})
+ @Nullable
+ public <K, V> HashMap<K, V> readHashMap(@Nullable ClassLoader loader,
+ @NonNull Class<? extends K> clazzKey, @NonNull Class<? extends V> clazzValue) {
+ Objects.requireNonNull(clazzKey);
+ Objects.requireNonNull(clazzValue);
+ int n = readInt();
+ if (n < 0) {
+ return null;
+ }
+ HashMap<K, V> map = new HashMap<>(n);
+ readMapInternal(map, n, loader, clazzKey, clazzValue);
+ return map;
+ }
+
+ /**
* Read and return a new Bundle object from the parcel at the current
* dataPosition(). Returns null if the previously written Bundle object was
* null.
@@ -4472,13 +4510,23 @@
destroy();
}
- /* package */ void readMapInternal(@NonNull Map outVal, int N,
+ /**
+ * To be replaced by {@link #readMapInternal(Map, int, ClassLoader, Class, Class)}, but keep
+ * the old API for compatibility usages.
+ */
+ /* package */ void readMapInternal(@NonNull Map outVal, int n,
@Nullable ClassLoader loader) {
- while (N > 0) {
- Object key = readValue(loader);
- Object value = readValue(loader);
+ readMapInternal(outVal, n, loader, /* clazzKey */null, /* clazzValue */null);
+ }
+
+ /* package */ <K, V> void readMapInternal(@NonNull Map<? super K, ? super V> outVal, int n,
+ @Nullable ClassLoader loader, @Nullable Class<K> clazzKey,
+ @Nullable Class<V> clazzValue) {
+ while (n > 0) {
+ K key = readValue(loader, clazzKey);
+ V value = readValue(loader, clazzValue);
outVal.put(key, value);
- N--;
+ n--;
}
}
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 4a6216e..3d5abb3 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -23,10 +23,15 @@
import android.annotation.TestApi;
import android.annotation.UserIdInt;
import android.compat.annotation.UnsupportedAppUsage;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Random;
/**
* Representation of a user on the device.
@@ -119,19 +124,46 @@
public static final int MIN_SECONDARY_USER_ID = 10;
/**
- * Arbitrary user handle cache size. We use the cache even when {@link #MU_ENABLED} is false
- * anyway, so we can always assume in CTS that UserHandle.of(10) returns a cached instance
- * even on non-multiuser devices.
+ * (Arbitrary) user handle cache size.
+ * {@link #CACHED_USER_HANDLES} caches user handles in the range of
+ * [{@link #MIN_SECONDARY_USER_ID}, {@link #MIN_SECONDARY_USER_ID} + {@link #NUM_CACHED_USERS}).
+ *
+ * For other users, we cache UserHandles in {link #sExtraUserHandleCache}.
+ *
+ * Normally, {@link #CACHED_USER_HANDLES} should cover all existing users, but use
+ * {link #sExtraUserHandleCache} to ensure {@link UserHandle#of} will not cause too many
+ * object allocations even if the device happens to have a secondary user with a large number
+ * (e.g. the user kept creating and removing the guest user?).
*/
- private static final int NUM_CACHED_USERS = 4;
+ private static final int NUM_CACHED_USERS = MU_ENABLED ? 8 : 0;
- private static final UserHandle[] CACHED_USER_INFOS = new UserHandle[NUM_CACHED_USERS];
+ /** @see #NUM_CACHED_USERS} */
+ private static final UserHandle[] CACHED_USER_HANDLES = new UserHandle[NUM_CACHED_USERS];
+
+ /**
+ * Extra cache for users beyond CACHED_USER_HANDLES.
+ *
+ * @see #NUM_CACHED_USERS
+ * @hide
+ */
+ @GuardedBy("sExtraUserHandleCache")
+ @VisibleForTesting
+ public static final SparseArray<UserHandle> sExtraUserHandleCache = new SparseArray<>(0);
+
+ /**
+ * Max size of {@link #sExtraUserHandleCache}. Once it reaches this size, we select
+ * an element to remove at random.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public static final int MAX_EXTRA_USER_HANDLE_CACHE_SIZE = 32;
static {
// Not lazily initializing the cache, so that we can share them across processes.
// (We'll create them in zygote.)
- for (int i = 0; i < CACHED_USER_INFOS.length; i++) {
- CACHED_USER_INFOS[i] = new UserHandle(MIN_SECONDARY_USER_ID + i);
+ for (int i = 0; i < CACHED_USER_HANDLES.length; i++) {
+ CACHED_USER_HANDLES[i] = new UserHandle(MIN_SECONDARY_USER_ID + i);
}
}
@@ -302,13 +334,31 @@
return CURRENT_OR_SELF;
}
if (userId >= MIN_SECONDARY_USER_ID
- && userId < (MIN_SECONDARY_USER_ID + CACHED_USER_INFOS.length)) {
- return CACHED_USER_INFOS[userId - MIN_SECONDARY_USER_ID];
+ && userId < (MIN_SECONDARY_USER_ID + CACHED_USER_HANDLES.length)) {
+ return CACHED_USER_HANDLES[userId - MIN_SECONDARY_USER_ID];
}
if (userId == USER_NULL) { // Not common.
return NULL;
}
- return new UserHandle(userId);
+ return getUserHandleFromExtraCache(userId);
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ public static UserHandle getUserHandleFromExtraCache(@UserIdInt int userId) {
+ synchronized (sExtraUserHandleCache) {
+ final UserHandle extraCached = sExtraUserHandleCache.get(userId);
+ if (extraCached != null) {
+ return extraCached;
+ }
+ if (sExtraUserHandleCache.size() >= MAX_EXTRA_USER_HANDLE_CACHE_SIZE) {
+ sExtraUserHandleCache.removeAt(
+ (new Random()).nextInt(MAX_EXTRA_USER_HANDLE_CACHE_SIZE));
+ }
+ final UserHandle newHandle = new UserHandle(userId);
+ sExtraUserHandleCache.put(userId, newHandle);
+ return newHandle;
+ }
}
/**
diff --git a/core/java/android/os/health/HealthStats.java b/core/java/android/os/health/HealthStats.java
index 74ce515..6c648f1 100644
--- a/core/java/android/os/health/HealthStats.java
+++ b/core/java/android/os/health/HealthStats.java
@@ -32,7 +32,7 @@
* Each of the keys references data in one of five data types:
*
* <p>
- * A <b>measurement</b> metric contains a sinlge {@code long} value. That value may
+ * A <b>measurement</b> metric contains a single {@code long} value. That value may
* be a count, a time, or some other type of value. The unit for a measurement
* (COUNT, MS, etc) will always be in the name of the constant for the key to
* retrieve it. For example, the
diff --git a/core/java/android/os/health/UidHealthStats.java b/core/java/android/os/health/UidHealthStats.java
index afc9d78..488a542 100644
--- a/core/java/android/os/health/UidHealthStats.java
+++ b/core/java/android/os/health/UidHealthStats.java
@@ -43,14 +43,14 @@
/**
* How many milliseconds this statistics report covers in wall-clock time while the
- * device was on battery including both screen-on and screen-off time.
+ * device was on battery including only screen-off time.
*/
@HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
public static final int MEASUREMENT_REALTIME_SCREEN_OFF_BATTERY_MS = HealthKeys.BASE_UID + 3;
/**
* How many milliseconds this statistics report covers that the CPU was running while the
- * device was on battery including both screen-on and screen-off time.
+ * device was on battery including only screen-off time.
*/
@HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
public static final int MEASUREMENT_UPTIME_SCREEN_OFF_BATTERY_MS = HealthKeys.BASE_UID + 4;
@@ -65,7 +65,7 @@
/**
* Key for a TimerStat for the times a
- * {@link android.os.PowerManager#PARTIAL_WAKE_LOCK full wake lock}
+ * {@link android.os.PowerManager#PARTIAL_WAKE_LOCK partial wake lock}
* was acquired for this uid.
*/
@HealthKeys.Constant(type=HealthKeys.TYPE_TIMERS)
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index dd31e02..21c1feb 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -648,6 +648,14 @@
public static final String NAMESPACE_GAME_OVERLAY = "game_overlay";
/**
+ * Namespace for Android Virtualization Framework related features accessible by native code.
+ *
+ * @hide
+ */
+ public static final String NAMESPACE_VIRTUALIZATION_FRAMEWORK_NATIVE =
+ "virtualization_framework_native";
+
+ /**
* Namespace for Constrain Display APIs related features.
*
* @hide
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 295ed77..c54b56d8 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7158,6 +7158,13 @@
public static final String ACCESSIBILITY_ENABLED = "accessibility_enabled";
/**
+ * Whether select sound track with audio description by default.
+ * @hide
+ */
+ public static final String ENABLED_ACCESSIBILITY_AUDIO_DESCRIPTION_BY_DEFAULT =
+ "enabled_accessibility_audio_description_by_default";
+
+ /**
* Setting specifying if the accessibility shortcut is enabled.
* @hide
*/
@@ -10415,14 +10422,6 @@
"enable_accessibility_global_gesture_enabled";
/**
- * Whether select sound track with audio description by default.
- * @hide
- */
- @Readable
- public static final String ENABLE_ACCESSIBILITY_AUDIO_DESCRIPTION_BY_DEFAULT =
- "enable_accessibility_audio_description_by_default";
-
- /**
* Whether Airplane Mode is on.
*/
@Readable
@@ -16609,12 +16608,6 @@
public static final String WEAR_PLATFORM_MR_NUMBER = "wear_platform_mr_number";
/**
- * The different levels of screen brightness the user can select.
- * @hide
- */
- public static final String SCREEN_BRIGHTNESS_LEVEL = "screen_brightness_level";
-
- /**
* The mobile signal detector setting.
* @hide
*/
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index aa1bbc5..1b38f59 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -5374,5 +5374,21 @@
*/
public static final String COLUMN_NR_ADVANCED_CALLING_ENABLED =
"nr_advanced_calling_enabled";
+
+ /**
+ * TelephonyProvider column name for the phone number from source CARRIER
+ *
+ * @hide
+ */
+ public static final String COLUMN_PHONE_NUMBER_SOURCE_CARRIER =
+ "phone_number_source_carrier";
+
+ /**
+ * TelephonyProvider column name for the phone number from source IMS
+ *
+ * @hide
+ */
+ public static final String COLUMN_PHONE_NUMBER_SOURCE_IMS =
+ "phone_number_source_ims";
}
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 2d263a5..c77399f 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -1575,7 +1575,7 @@
void updatePage(EngineWindowPage currentPage, int pageIndx, int numPages,
float xOffsetStep) {
// to save creating a runnable, check twice
- long current = SystemClock.elapsedRealtime();
+ long current = System.currentTimeMillis();
long lapsed = current - currentPage.getLastUpdateTime();
// Always update the page when the last update time is <= 0
// This is important especially when the device first boots
@@ -1768,6 +1768,7 @@
return;
}
}
+ processLocalColors(mPendingXOffset, mPendingYOffset);
}
/**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index c88d4f8..ce96eca 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -211,13 +211,13 @@
import java.io.StringWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
-import java.util.function.Consumer;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Queue;
import java.util.concurrent.CountDownLatch;
+import java.util.function.Consumer;
/**
* The top of a view hierarchy, implementing the needed protocol between View
@@ -730,7 +730,7 @@
/**
* This is only used on the RenderThread when handling a blast sync. Specifically, it's only
- * used when calling {@link BLASTBufferQueue#setNextTransaction(Transaction)} and then merged
+ * used when calling {@link BLASTBufferQueue#setSyncTransaction(Transaction)} and then merged
* with a tmp transaction on the Render Thread. The tmp transaction is then merged into
* {@link #mSurfaceChangedTransaction} on the UI Thread, avoiding any threading issues.
*/
@@ -3999,7 +3999,7 @@
// draw attempt. The next transaction and transaction complete callback were only set
// for the current draw attempt.
if (frameWasNotDrawn) {
- mBlastBufferQueue.setNextTransaction(null);
+ mBlastBufferQueue.setSyncTransaction(null);
// Apply the transactions that were sent to mergeWithNextTransaction since the
// frame didn't draw on this vsync. It's possible the frame will draw later, but
// it's better to not be sync than to block on a frame that may never come.
@@ -4095,7 +4095,7 @@
// We don't need to synchronize mRtBLASTSyncTransaction here since it's not
// being modified and only sent to BlastBufferQueue.
- mBlastBufferQueue.setNextTransaction(mRtBLASTSyncTransaction);
+ mBlastBufferQueue.setSyncTransaction(mRtBLASTSyncTransaction);
}
};
registerRtFrameCallback(frameDrawingCallback);
diff --git a/core/java/android/view/accessibility/MagnificationAnimationCallback.java b/core/java/android/view/accessibility/MagnificationAnimationCallback.java
index bc9fb0a..72518db 100644
--- a/core/java/android/view/accessibility/MagnificationAnimationCallback.java
+++ b/core/java/android/view/accessibility/MagnificationAnimationCallback.java
@@ -21,6 +21,9 @@
* @hide
*/
public interface MagnificationAnimationCallback {
+ MagnificationAnimationCallback STUB_ANIMATION_CALLBACK = success -> {
+ };
+
/**
* Called when the animation is finished or interrupted during animating.
*
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index 4a52b1f..35e60b8 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -528,10 +528,6 @@
* If not {@code null}, this editor needs to talk to IMEs that run for the specified user, no
* matter what user ID the calling process has.
*
- * <p>Note: This field will be silently ignored when
- * {@link com.android.server.inputmethod.InputMethodSystemProperty#MULTI_CLIENT_IME_ENABLED} is
- * {@code true}.</p>
- *
* <p>Note also that pseudo handles such as {@link UserHandle#ALL} are not supported.</p>
*
* @hide
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index c3d7836..3b15db2 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -545,6 +545,33 @@
boolean setComposingText(CharSequence text, int newCursorPosition);
/**
+ * The variant of {@link #setComposingText(CharSequence, int)}. This method is
+ * used to allow the IME to provide extra information while setting up composing text.
+ *
+ * @param text The composing text with styles if necessary. If no style
+ * object attached to the text, the default style for composing text
+ * is used. See {@link android.text.Spanned} for how to attach style
+ * object to the text. {@link android.text.SpannableString} and
+ * {@link android.text.SpannableStringBuilder} are two
+ * implementations of the interface {@link android.text.Spanned}.
+ * @param newCursorPosition The new cursor position around the text. If
+ * > 0, this is relative to the end of the text - 1; if <= 0, this
+ * is relative to the start of the text. So a value of 1 will
+ * always advance you to the position after the full text being
+ * inserted. Note that this means you can't position the cursor
+ * within the text, because the editor can make modifications to
+ * the text you are providing so it is not possible to correctly
+ * specify locations there.
+ * @param textAttribute The extra information about the text.
+ * @return true on success, false if the input connection is no longer
+ *
+ */
+ default boolean setComposingText(@NonNull CharSequence text, int newCursorPosition,
+ @Nullable TextAttribute textAttribute) {
+ return setComposingText(text, newCursorPosition);
+ }
+
+ /**
* Mark a certain region of text as composing text. If there was a
* composing region, the characters are left as they were and the
* composing span removed, as if {@link #finishComposingText()}
@@ -579,6 +606,22 @@
boolean setComposingRegion(int start, int end);
/**
+ * The variant of {@link InputConnection#setComposingRegion(int, int)}. This method is
+ * used to allow the IME to provide extra information while setting up text.
+ *
+ * @param start the position in the text at which the composing region begins
+ * @param end the position in the text at which the composing region ends
+ * @param textAttribute The extra information about the text.
+ * @return {@code true} on success, {@code false} if the input connection is no longer valid.
+ * Since Android {@link android.os.Build.VERSION_CODES#N} until
+ * {@link android.os.Build.VERSION_CODES#TIRAMISU}, this API returned {@code false} when
+ * the target application does not implement this method.
+ */
+ default boolean setComposingRegion(int start, int end, @Nullable TextAttribute textAttribute) {
+ return setComposingRegion(start, end);
+ }
+
+ /**
* Have the text editor finish whatever composing text is
* currently active. This simply leaves the text as-is, removing
* any special composing styling or other state that was around
@@ -634,6 +677,28 @@
boolean commitText(CharSequence text, int newCursorPosition);
/**
+ * The variant of {@link InputConnection#commitText(CharSequence, int)}. This method is
+ * used to allow the IME to provide extra information while setting up text.
+ *
+ * @param text The text to commit. This may include styles.
+ * @param newCursorPosition The new cursor position around the text,
+ * in Java characters. If > 0, this is relative to the end
+ * of the text - 1; if <= 0, this is relative to the start
+ * of the text. So a value of 1 will always advance the cursor
+ * to the position after the full text being inserted. Note that
+ * this means you can't position the cursor within the text,
+ * because the editor can make modifications to the text
+ * you are providing so it is not possible to correctly specify
+ * locations there.
+ * @param textAttribute The extra information about the text.
+ * @return true on success, false if the input connection is no longer
+ */
+ default boolean commitText(@NonNull CharSequence text, int newCursorPosition,
+ @Nullable TextAttribute textAttribute) {
+ return commitText(text, newCursorPosition);
+ }
+
+ /**
* Commit a completion the user has selected from the possible ones
* previously reported to {@link InputMethodSession#displayCompletions
* InputMethodSession#displayCompletions(CompletionInfo[])} or
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
index a99e9b8..7a88a75 100644
--- a/core/java/android/view/inputmethod/InputConnectionWrapper.java
+++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java
@@ -17,6 +17,7 @@
package android.view.inputmethod;
import android.annotation.IntRange;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
import android.os.Handler;
@@ -158,6 +159,16 @@
* @throws NullPointerException if the target is {@code null}.
*/
@Override
+ public boolean setComposingText(@NonNull CharSequence text,
+ int newCursorPosition, @Nullable TextAttribute textAttribute) {
+ return mTarget.setComposingText(text, newCursorPosition, textAttribute);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws NullPointerException if the target is {@code null}.
+ */
+ @Override
public boolean setComposingRegion(int start, int end) {
return mTarget.setComposingRegion(start, end);
}
@@ -167,6 +178,15 @@
* @throws NullPointerException if the target is {@code null}.
*/
@Override
+ public boolean setComposingRegion(int start, int end, @Nullable TextAttribute textAttribute) {
+ return mTarget.setComposingRegion(start, end, textAttribute);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws NullPointerException if the target is {@code null}.
+ */
+ @Override
public boolean finishComposingText() {
return mTarget.finishComposingText();
}
@@ -185,6 +205,16 @@
* @throws NullPointerException if the target is {@code null}.
*/
@Override
+ public boolean commitText(@NonNull CharSequence text, int newCursorPosition,
+ @Nullable TextAttribute textAttribute) {
+ return mTarget.commitText(text, newCursorPosition, textAttribute);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws NullPointerException if the target is {@code null}.
+ */
+ @Override
public boolean commitCompletion(CompletionInfo text) {
return mTarget.commitCompletion(text);
}
diff --git a/core/java/android/view/inputmethod/TextAttribute.aidl b/core/java/android/view/inputmethod/TextAttribute.aidl
new file mode 100644
index 0000000..5f296d9
--- /dev/null
+++ b/core/java/android/view/inputmethod/TextAttribute.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.view.inputmethod;
+
+parcelable TextAttribute;
\ No newline at end of file
diff --git a/core/java/android/view/inputmethod/TextAttribute.java b/core/java/android/view/inputmethod/TextAttribute.java
new file mode 100644
index 0000000..bc76e78
--- /dev/null
+++ b/core/java/android/view/inputmethod/TextAttribute.java
@@ -0,0 +1,140 @@
+/*
+ * 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.view.inputmethod;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * The data class that IME can take extra information to applications when setting the text.
+ *
+ * See {@link InputConnection#commitText(CharSequence, int, TextAttribute)} and
+ * {@link InputConnection#setComposingRegion(int, int, TextAttribute)} and
+ * {@link InputConnection#setComposingText(CharSequence, int, TextAttribute)}
+ */
+public final class TextAttribute implements Parcelable {
+ private final @NonNull List<String> mTextConversionSuggestions;
+ private final @NonNull PersistableBundle mExtras;
+
+ private TextAttribute(TextAttributeBuilder builder) {
+ mTextConversionSuggestions = builder.mTextConversionSuggestions;
+ mExtras = builder.mExtras;
+ }
+
+ private TextAttribute(Parcel source) {
+ mTextConversionSuggestions = source.createStringArrayList();
+ mExtras = source.readPersistableBundle();
+ }
+
+ /**
+ * Get the list of text conversion suggestions. More text conversion details in
+ * {@link TextAttributeBuilder#setTextConversionSuggestions(List)}.
+ *
+ * @return List of text conversion suggestions. If the list is empty, it means that IME not set
+ * this field or IME didn't have suggestions for applications.
+ */
+ public @NonNull List<String> getTextConversionSuggestions() {
+ return mTextConversionSuggestions;
+ }
+
+ /**
+ * Get the extras data. More extras data details in
+ * {@link TextAttributeBuilder#setExtras(PersistableBundle)}.
+ *
+ * @return Extras data. If the Bundle is empty, it means that IME not set this field or IME
+ * didn't have extras data.
+ */
+ public @NonNull PersistableBundle getExtras() {
+ return mExtras;
+ }
+
+ /**
+ * Builder for creating a {@link TextAttribute}.
+ */
+ public static final class TextAttributeBuilder {
+ private List<String> mTextConversionSuggestions = new ArrayList<>();
+ private PersistableBundle mExtras = new PersistableBundle();
+
+ /**
+ * Sets text conversion suggestions.
+ *
+ * <p>Text conversion suggestion is for some transliteration languages which has
+ * pronunciation characters and target characters. When the user is typing the pronunciation
+ * characters, the input method can insert possible target characters into this list so that
+ * the editor authors can provide suggestion before the user enters the complete
+ * pronunciation characters.</p>
+ *
+ * @param textConversionSuggestions The list of text conversion suggestions.
+ * @return This builder
+ */
+ public @NonNull TextAttributeBuilder setTextConversionSuggestions(
+ @NonNull List<String> textConversionSuggestions) {
+ mTextConversionSuggestions = Collections.unmodifiableList(textConversionSuggestions);
+ return this;
+ }
+
+ /**
+ * Sets extras data.
+ *
+ * <p>Any extra data to supply to the applications. This field is for extended communication
+ * with IME if there is data not defined in framework.</p>
+ *
+ * @return This builder.
+ */
+ public @NonNull TextAttributeBuilder setExtras(@NonNull PersistableBundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ /**
+ * @return a new {@link TextAttribute}.
+ */
+ public @NonNull TextAttribute build() {
+ return new TextAttribute(this);
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeStringList(mTextConversionSuggestions);
+ dest.writePersistableBundle(mExtras);
+ }
+
+ public static final @NonNull Parcelable.Creator<TextAttribute> CREATOR =
+ new Parcelable.Creator<TextAttribute>() {
+ @Override
+ public TextAttribute createFromParcel(Parcel source) {
+ return new TextAttribute(source);
+ }
+
+ @Override
+ public TextAttribute[] newArray(int size) {
+ return new TextAttribute[size];
+ }
+ };
+}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 2357d13..f724285 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -5735,24 +5735,44 @@
return previousLayoutId == getLayoutId() && mViewId == overrideId;
}
- // Note: topLevel should be true only for calls on the topLevel RemoteViews, internal calls
- // should set it to false.
- private void reapply(Context context, View v, InteractionHandler handler, SizeF size,
- ColorResources colorResources, boolean topLevel) {
-
+ /**
+ * Returns the RemoteViews that should be used in the reapply operation.
+ *
+ * If the current RemoteViews has multiple layout, this will select the correct one.
+ *
+ * @throws RuntimeException If the current RemoteViews should not be reapplied onto the provided
+ * View.
+ */
+ private RemoteViews getRemoteViewsToReapply(Context context, View v, @Nullable SizeF size) {
RemoteViews rvToApply = getRemoteViewsToApply(context, size);
// In the case that a view has this RemoteViews applied in one orientation or size, is
// persisted across change, and has the RemoteViews re-applied in a different situation
// (orientation or size), we throw an exception, since the layouts may be completely
// unrelated.
- if (hasMultipleLayouts()) {
+ // If the ViewID has been changed on the view, or is changed by the RemoteViews, we also
+ // may throw an exception, as the RemoteViews will probably not apply properly.
+ // However, we need to let potentially unrelated RemoteViews apply, as this lack of testing
+ // is already used in production code in some apps.
+ if (hasMultipleLayouts()
+ || rvToApply.mViewId != View.NO_ID
+ || v.getTag(R.id.remote_views_override_id) != null) {
if (!rvToApply.canRecycleView(v)) {
throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
" that does not share the same root layout id.");
}
}
+ return rvToApply;
+ }
+
+ // Note: topLevel should be true only for calls on the topLevel RemoteViews, internal calls
+ // should set it to false.
+ private void reapply(Context context, View v, InteractionHandler handler, SizeF size,
+ ColorResources colorResources, boolean topLevel) {
+
+ RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);
+
rvToApply.performApply(v, (ViewGroup) v.getParent(), handler, colorResources);
// If the parent of the view is has is a root, resolve the recycling.
@@ -5789,17 +5809,7 @@
public CancellationSignal reapplyAsync(Context context, View v, Executor executor,
OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
ColorResources colorResources) {
- RemoteViews rvToApply = getRemoteViewsToApply(context, size);
-
- // In the case that a view has this RemoteViews applied in one orientation, is persisted
- // across orientation change, and has the RemoteViews re-applied in the new orientation,
- // we throw an exception, since the layouts may be completely unrelated.
- if (hasMultipleLayouts()) {
- if (!rvToApply.canRecycleView(v)) {
- throw new RuntimeException("Attempting to re-apply RemoteViews to a view that" +
- " that does not share the same root layout id.");
- }
- }
+ RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);
return new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(),
context, listener, handler, colorResources, v, true /* topLevel */)
diff --git a/core/java/com/android/internal/inputmethod/IInputContextInvoker.java b/core/java/com/android/internal/inputmethod/IInputContextInvoker.java
index efdf483..4dbd941 100644
--- a/core/java/com/android/internal/inputmethod/IInputContextInvoker.java
+++ b/core/java/com/android/internal/inputmethod/IInputContextInvoker.java
@@ -18,6 +18,7 @@
import android.annotation.AnyThread;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Bundle;
import android.os.RemoteException;
import android.view.KeyEvent;
@@ -27,6 +28,7 @@
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.SurroundingText;
+import android.view.inputmethod.TextAttribute;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.view.IInputContext;
@@ -211,6 +213,28 @@
}
/**
+ * Invokes {@link IInputContext#commitTextWithTextAttribute(InputConnectionCommandHeader, int,
+ * CharSequence)}.
+ *
+ * @param text {@code text} parameter to be passed.
+ * @param newCursorPosition {@code newCursorPosition} parameter to be passed.
+ * @param textAttribute The extra information about the text.
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean commitText(CharSequence text, int newCursorPosition,
+ @Nullable TextAttribute textAttribute) {
+ try {
+ mIInputContext.commitTextWithTextAttribute(
+ createHeader(), text, newCursorPosition, textAttribute);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
* Invokes {@link IInputContext#commitCompletion(InputConnectionCommandHeader, CompletionInfo)}.
*
* @param text {@code text} parameter to be passed.
@@ -315,6 +339,27 @@
}
/**
+ * Invokes {@link IInputContext#setComposingRegionWithTextAttribute(
+ * InputConnectionCommandHeader, int, int, TextAttribute)}.
+ *
+ * @param start {@code id} parameter to be passed.
+ * @param end {@code id} parameter to be passed.
+ * @param textAttribute The extra information about the text.
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean setComposingRegion(int start, int end, @Nullable TextAttribute textAttribute) {
+ try {
+ mIInputContext.setComposingRegionWithTextAttribute(
+ createHeader(), start, end, textAttribute);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
* Invokes
* {@link IInputContext#setComposingText(InputConnectionCommandHeader, CharSequence, int)}.
*
@@ -334,6 +379,28 @@
}
/**
+ * Invokes {@link IInputContext#setComposingTextWithTextAttribute(InputConnectionCommandHeader,
+ * CharSequence, int, TextAttribute)}.
+ *
+ * @param text {@code text} parameter to be passed.
+ * @param newCursorPosition {@code newCursorPosition} parameter to be passed.
+ * @param textAttribute The extra information about the text.
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean setComposingText(CharSequence text, int newCursorPosition,
+ @Nullable TextAttribute textAttribute) {
+ try {
+ mIInputContext.setComposingTextWithTextAttribute(
+ createHeader(), text, newCursorPosition, textAttribute);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
* Invokes {@link IInputContext#finishComposingText(InputConnectionCommandHeader)}.
*
* @return {@code true} if the invocation is completed without {@link RemoteException}.
diff --git a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
index 21358ab..5503226 100644
--- a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
@@ -42,6 +42,7 @@
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.TextAttribute;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.infra.AndroidFuture;
@@ -385,6 +386,23 @@
@Dispatching(cancellable = true)
@Override
+ public void commitTextWithTextAttribute(InputConnectionCommandHeader header, CharSequence text,
+ int newCursorPosition, @Nullable TextAttribute textAttribute) {
+ dispatchWithTracing("commitTextWithTextAttribute", () -> {
+ if (header.mSessionId != mCurrentSessionId.get()) {
+ return; // cancelled
+ }
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "commitText on inactive InputConnection");
+ return;
+ }
+ ic.commitText(text, newCursorPosition, textAttribute);
+ });
+ }
+
+ @Dispatching(cancellable = true)
+ @Override
public void commitCompletion(InputConnectionCommandHeader header, CompletionInfo text) {
dispatchWithTracing("commitCompletion", () -> {
if (header.mSessionId != mCurrentSessionId.get()) {
@@ -489,6 +507,23 @@
@Dispatching(cancellable = true)
@Override
+ public void setComposingRegionWithTextAttribute(InputConnectionCommandHeader header, int start,
+ int end, @Nullable TextAttribute textAttribute) {
+ dispatchWithTracing("setComposingRegionWithTextAttribute", () -> {
+ if (header.mSessionId != mCurrentSessionId.get()) {
+ return; // cancelled
+ }
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "setComposingRegion on inactive InputConnection");
+ return;
+ }
+ ic.setComposingRegion(start, end, textAttribute);
+ });
+ }
+
+ @Dispatching(cancellable = true)
+ @Override
public void setComposingText(InputConnectionCommandHeader header, CharSequence text,
int newCursorPosition) {
dispatchWithTracing("setComposingText", () -> {
@@ -504,6 +539,23 @@
});
}
+ @Dispatching(cancellable = true)
+ @Override
+ public void setComposingTextWithTextAttribute(InputConnectionCommandHeader header,
+ CharSequence text, int newCursorPosition, @Nullable TextAttribute textAttribute) {
+ dispatchWithTracing("setComposingTextWithTextAttribute", () -> {
+ if (header.mSessionId != mCurrentSessionId.get()) {
+ return; // cancelled
+ }
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "setComposingText on inactive InputConnection");
+ return;
+ }
+ ic.setComposingText(text, newCursorPosition, textAttribute);
+ });
+ }
+
/**
* Dispatches {@link InputConnection#finishComposingText()}.
*
diff --git a/core/java/com/android/internal/inputmethod/StartInputFlags.java b/core/java/com/android/internal/inputmethod/StartInputFlags.java
index ac83987..dd4ff67 100644
--- a/core/java/com/android/internal/inputmethod/StartInputFlags.java
+++ b/core/java/com/android/internal/inputmethod/StartInputFlags.java
@@ -30,7 +30,9 @@
@IntDef(flag = true, value = {
StartInputFlags.VIEW_HAS_FOCUS,
StartInputFlags.IS_TEXT_EDITOR,
- StartInputFlags.INITIAL_CONNECTION})
+ StartInputFlags.INITIAL_CONNECTION,
+ StartInputFlags.WINDOW_GAINED_FOCUS,
+})
public @interface StartInputFlags {
/**
* There is a focused view in the focused window.
@@ -40,17 +42,17 @@
/**
* The focused view is a text editor.
*/
- int IS_TEXT_EDITOR = 2;
+ int IS_TEXT_EDITOR = 1 << 1;
/**
* An internal concept to distinguish "start" and "restart". This concept doesn't look well
* documented hence we probably need to revisit this though.
*/
- int INITIAL_CONNECTION = 4;
+ int INITIAL_CONNECTION = 1 << 2;
/**
* The start input happens when the window gained focus to call
* {@code android.view.inputmethod.InputMethodManager#startInputAsyncOnWindowFocusGain}.
*/
- int WINDOW_GAINED_FOCUS = 8;
+ int WINDOW_GAINED_FOCUS = 1 << 3;
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 4e758e6..f3cdf82 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -46,7 +46,7 @@
void showWirelessChargingAnimation(int batteryLevel);
void setImeWindowStatus(int displayId, in IBinder token, int vis, int backDisposition,
- boolean showImeSwitcher, boolean isMultiClientImeEnabled);
+ boolean showImeSwitcher);
void setWindowState(int display, int window, int state);
void showRecentApps(boolean triggeredFromAltTab);
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 40a3951..1db7426 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -55,7 +55,7 @@
@UnsupportedAppUsage
void removeIcon(String slot);
void setImeWindowStatus(int displayId, in IBinder token, int vis, int backDisposition,
- boolean showImeSwitcher, boolean isMultiClientImeEnabled);
+ boolean showImeSwitcher);
void expandSettingsPanel(String subPanel);
// ---- Methods below are for use by the status bar policy services ----
diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl
index df55beb..7da0f11 100644
--- a/core/java/com/android/internal/view/IInputContext.aidl
+++ b/core/java/com/android/internal/view/IInputContext.aidl
@@ -22,6 +22,7 @@
import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputContentInfo;
+import android.view.inputmethod.TextAttribute;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.inputmethod.InputConnectionCommandHeader;
@@ -52,10 +53,16 @@
void setComposingText(in InputConnectionCommandHeader header, CharSequence text,
int newCursorPosition);
+ void setComposingTextWithTextAttribute(in InputConnectionCommandHeader header,
+ CharSequence text, int newCursorPosition, in TextAttribute textAttribute);
+
void finishComposingText(in InputConnectionCommandHeader header);
void commitText(in InputConnectionCommandHeader header, CharSequence text,
- int newCursorPosition);
+ int newCursorPosition);
+
+ void commitTextWithTextAttribute(in InputConnectionCommandHeader header, CharSequence text,
+ int newCursorPosition, in TextAttribute textAttribute);
void commitCompletion(in InputConnectionCommandHeader header, in CompletionInfo completion);
@@ -82,6 +89,9 @@
void setComposingRegion(in InputConnectionCommandHeader header, int start, int end);
+ void setComposingRegionWithTextAttribute(in InputConnectionCommandHeader header, int start,
+ int end, in TextAttribute textAttribute);
+
void getSelectedText(in InputConnectionCommandHeader header, int flags,
in AndroidFuture future /* T=CharSequence */);
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
index a7362ab..cdfd089 100644
--- a/core/jni/android_graphics_BLASTBufferQueue.cpp
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -61,10 +61,10 @@
queue->getSurface(includeSurfaceControlHandle));
}
-static void nativeSetNextTransaction(JNIEnv* env, jclass clazz, jlong ptr, jlong transactionPtr) {
+static void nativeSetSyncTransaction(JNIEnv* env, jclass clazz, jlong ptr, jlong transactionPtr) {
sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionPtr);
- queue->setNextTransaction(transaction);
+ queue->setSyncTransaction(transaction);
}
static void nativeUpdate(JNIEnv* env, jclass clazz, jlong ptr, jlong surfaceControl, jlong width,
@@ -98,7 +98,7 @@
{"nativeCreate", "(Ljava/lang/String;JJJI)J", (void*)nativeCreate},
{"nativeGetSurface", "(JZ)Landroid/view/Surface;", (void*)nativeGetSurface},
{"nativeDestroy", "(J)V", (void*)nativeDestroy},
- {"nativeSetNextTransaction", "(JJ)V", (void*)nativeSetNextTransaction},
+ {"nativeSetSyncTransaction", "(JJ)V", (void*)nativeSetSyncTransaction},
{"nativeUpdate", "(JJJJIJ)V", (void*)nativeUpdate},
{"nativeMergeWithNextTransaction", "(JJJ)V", (void*)nativeMergeWithNextTransaction},
{"nativeGetLastAcquiredFrameNum", "(J)J", (void*)nativeGetLastAcquiredFrameNum},
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 3248cf5..8ef3825 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -262,6 +262,7 @@
optional int32 user_rotation = 3 [(.android.typedef) = "android.view.Surface.Rotation"];
optional int32 fixed_to_user_rotation_mode = 4;
optional int32 last_orientation = 5 [(.android.typedef) = "android.content.pm.ActivityInfo.ScreenOrientation"];
+ optional bool is_fixed_to_user_rotation = 6;
}
/* represents DockedTaskDividerController */
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 007cf6b..6d96784 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1900,7 +1900,7 @@
@hide This should only be used by ManagedProvisioning app.
-->
<permission android:name="android.permission.NETWORK_MANAGED_PROVISIONING"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|role" />
<!-- Allows Carrier Provisioning to call methods in Networking services
<p>Not for use by any other third-party or privileged applications.
@@ -2356,10 +2356,10 @@
<permission android:name="android.permission.OEM_UNLOCK_STATE"
android:protectionLevel="signature" />
- <!-- @hide Allows querying state of PersistentDataBlock
+ <!-- @SystemApi @hide Allows querying state of PersistentDataBlock
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.ACCESS_PDB_STATE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|role" />
<!-- Allows testing if a passwords is forbidden by the admins.
@hide <p>Not for use by third-party applications. -->
@@ -2417,7 +2417,7 @@
<!-- @SystemApi @TestApi Allows read access to privileged phone state.
@hide Used internally. -->
<permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|role" />
<!-- Allows to read device identifiers and use ICC based authentication like EAP-AKA.
Often required in authentication to access the carrier's server and manage services
@@ -2733,25 +2733,25 @@
user-targeted broadcasts. This permission is not available to
third party applications. -->
<permission android:name="android.permission.INTERACT_ACROSS_USERS"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development|role" />
<!-- @SystemApi Fuller form of {@link android.Manifest.permission#INTERACT_ACROSS_USERS}
that removes restrictions on where broadcasts can be sent and allows other
types of interactions
@hide -->
<permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"
- android:protectionLevel="signature|installer" />
+ android:protectionLevel="signature|installer|role" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<!-- Allows interaction across profiles in the same profile group. -->
<permission android:name="android.permission.INTERACT_ACROSS_PROFILES"
android:protectionLevel="signature|appop" />
- <!-- Allows configuring apps to have the INTERACT_ACROSS_PROFILES permission so that they can
- interact across profiles in the same profile group.
+ <!-- @SystemApi Allows configuring apps to have the INTERACT_ACROSS_PROFILES permission so that
+ they can interact across profiles in the same profile group.
@hide -->
<permission android:name="android.permission.CONFIGURE_INTERACT_ACROSS_PROFILES"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|role" />
<!-- @SystemApi @hide Allows an application to call APIs that allow it to query and manage
users on the device. This permission is not available to
@@ -2772,10 +2772,10 @@
<permission android:name="android.permission.ACCESS_BLOBS_ACROSS_USERS"
android:protectionLevel="signature|privileged|development|role" />
- <!-- @hide Allows an application to set the profile owners and the device owner.
+ <!-- @SystemApi @hide Allows an application to set the profile owners and the device owner.
This permission is not available to third party applications.-->
<permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"
- android:protectionLevel="signature"
+ android:protectionLevel="signature|role"
android:label="@string/permlab_manageProfileAndDeviceOwners"
android:description="@string/permdesc_manageProfileAndDeviceOwners" />
@@ -2828,7 +2828,7 @@
<!-- @SystemApi @hide Allows an application to start activities from background -->
<permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"
- android:protectionLevel="signature|privileged|vendorPrivileged|oem|verifier" />
+ android:protectionLevel="signature|privileged|vendorPrivileged|oem|verifier|role" />
<!-- Allows an application to start foreground services from the background at any time.
<em>This permission is not for use by third-party applications</em>,
@@ -2990,6 +2990,17 @@
<permission android:name="android.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING"
android:protectionLevel="signature|privileged" />
+ <!-- Allows application to request to be associated with a vehicle head unit capable of
+ automotive projection
+ ({@link android.companion.AssociationRequest#DEVICE_PROFILE_AUTOMOTIVE_PROJECTION})
+ by {@link android.companion.CompanionDeviceManager}.
+ <p>Not for use by third-party applications.
+ @hide
+ @SystemApi
+ -->
+ <permission android:name="android.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION"
+ android:protectionLevel="internal|role" />
+
<!-- Allows a companion app to associate to Wi-Fi.
<p>Only for use by a single pre-approved app.
@hide
@@ -3054,7 +3065,7 @@
<!-- Allows applications to set the system time directly.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SET_TIME"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|role" />
<!-- Allows applications to set the system time zone directly.
<p>Not for use by third-party applications.
@@ -3062,7 +3073,7 @@
<permission android:name="android.permission.SET_TIME_ZONE"
android:label="@string/permlab_setTimeZone"
android:description="@string/permdesc_setTimeZone"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|role" />
<!-- Allows telephony to suggest the time / time zone.
<p>Not for use by third-party applications.
@@ -3176,7 +3187,7 @@
as locale.
<p>Protection level: signature|privileged|development -->
<permission android:name="android.permission.CHANGE_CONFIGURATION"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development|role" />
<!-- Allows an application to read or write the system settings.
@@ -3193,7 +3204,7 @@
<permission android:name="android.permission.WRITE_SETTINGS"
android:label="@string/permlab_writeSettings"
android:description="@string/permdesc_writeSettings"
- android:protectionLevel="signature|preinstalled|appop|pre23" />
+ android:protectionLevel="signature|preinstalled|appop|pre23|role" />
<!-- Allows an application to modify the Google service map.
<p>Not for use by third-party applications. -->
@@ -3446,7 +3457,7 @@
<!-- Allows an application to read or write the secure system settings.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.WRITE_SECURE_SETTINGS"
- android:protectionLevel="signature|privileged|development" />
+ android:protectionLevel="signature|privileged|development|role" />
<!-- Allows an application to retrieve state dump information from system services.
<p>Not for use by third-party applications. -->
@@ -3706,7 +3717,7 @@
to put the higher-level system there into a shutdown state.
@hide -->
<permission android:name="android.permission.SHUTDOWN"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|role" />
<!-- @SystemApi Allows an application to tell the activity manager to temporarily
stop application switches, putting it into a special mode that
@@ -4077,14 +4088,13 @@
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_DEVICE_ADMIN"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|role" />
<!-- @SystemApi Required to add or remove another application as a device admin.
<p>Not for use by third-party applications.
- @hide
- @removed -->
+ @hide -->
<permission android:name="android.permission.MANAGE_DEVICE_ADMINS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|role" />
<!-- @SystemApi Allows an app to reset the device password.
<p>Not for use by third-party applications.
@@ -4197,14 +4207,14 @@
<permission android:name="android.permission.INSTALL_PACKAGE_UPDATES"
android:protectionLevel="signature|privileged" />
- <!-- Allows an application to install existing system packages. This is a limited
+ <!-- @SystemApi Allows an application to install existing system packages. This is a limited
version of {@link android.Manifest.permission#INSTALL_PACKAGES}.
<p>Not for use by third-party applications.
TODO(b/80204953): remove this permission once we have a long-term solution.
@hide
-->
<permission android:name="com.android.permission.INSTALL_EXISTING_PACKAGES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|role" />
<!-- Allows an application to use the package installer v2 APIs.
<p>The package installer v2 APIs are still a work in progress and we're
@@ -4299,7 +4309,7 @@
when the application deleting the package is not the same application that installed the
package. -->
<permission android:name="android.permission.DELETE_PACKAGES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|role" />
<!-- @SystemApi Allows an application to move location of installed package.
@hide -->
@@ -4315,7 +4325,7 @@
enabled or not.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|role" />
<!-- @SystemApi Allows an application to grant specific permissions.
@hide -->
@@ -4767,7 +4777,7 @@
<!-- Not for use by third-party applications. -->
<permission android:name="android.permission.MASTER_CLEAR"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|role" />
<!-- Allows an application to call any phone number, including emergency
numbers, without going through the Dialer user interface for the user
@@ -4778,7 +4788,7 @@
<!-- @SystemApi Allows an application to perform CDMA OTA provisioning @hide -->
<permission android:name="android.permission.PERFORM_CDMA_PROVISIONING"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|role" />
<!-- @SystemApi Allows an application to perform SIM Activation @hide -->
<permission android:name="android.permission.PERFORM_SIM_ACTIVATION"
@@ -5005,7 +5015,7 @@
@hide
-->
<permission android:name="android.permission.CRYPT_KEEPER"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|role" />
<!-- @SystemApi Allows an application to read historical network usage for
specific networks and applications. @hide -->
@@ -5150,10 +5160,10 @@
android:protectionLevel="signature|installer" />
<uses-permission android:name="android.permission.MANAGE_NOTIFICATION_LISTENERS" />
- <!-- Allows notifications to be colorized
+ <!-- @SystemApi Allows notifications to be colorized
<p>Not for use by third-party applications. @hide -->
<permission android:name="android.permission.USE_COLORIZED_NOTIFICATIONS"
- android:protectionLevel="signature|setup" />
+ android:protectionLevel="signature|setup|role" />
<!-- Allows access to keyguard secure storage. Only allowed for system processes.
@hide -->
@@ -5394,7 +5404,7 @@
<!-- @SystemApi Allows access to MAC addresses of WiFi and Bluetooth peer devices.
@hide -->
<permission android:name="android.permission.PEERS_MAC_ADDRESS"
- android:protectionLevel="signature|setup" />
+ android:protectionLevel="signature|setup|role" />
<!-- Allows the Nfc stack to dispatch Nfc messages to applications. Applications
can use this permission to ensure incoming Nfc messages are from the Nfc stack
@@ -5699,11 +5709,11 @@
<permission android:name="android.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS"
android:protectionLevel="signature" />
- <!-- Allows an app to mark a profile owner as managing an organization-owned device.
+ <!-- @SystemApi Allows an app to mark a profile owner as managing an organization-owned device.
<p>Not for use by third-party applications.
@hide -->
<permission android:name="android.permission.MARK_DEVICE_ORGANIZATION_OWNED"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|role" />
<!-- Allows financial apps to read filtered sms messages.
Protection level: signature|appop
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 755938e..7805d46 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2953,6 +2953,9 @@
usually TVs.
<p>Requires permission {@code android.permission.DISABLE_SYSTEM_SOUND_EFFECTS}. -->
<attr name="playHomeTransitionSound" format="boolean"/>
+ <!-- Indicates whether the activity can be displayed on a remote device which may or
+ may not be running Android. -->
+ <attr name="canDisplayOnRemoteDevices" format="boolean"/>
</declare-styleable>
<!-- The <code>activity-alias</code> tag declares a new
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index e1b1e61..b924bd2 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2051,6 +2051,8 @@
<!-- The name of the package that will hold the television remote service role.
TODO(b/189347385) make this a @SystemAPI -->
<string name="config_systemTelevisionRemoteService" translatable="false">@string/config_tvRemoteServicePackage</string>
+ <!-- The name of the package that will hold the device management role -->
+ <string name="config_deviceManager" translatable="false"></string>
<!-- The name of the package that will be allowed to change its components' label/icon. -->
<string name="config_overrideComponentUiPackage" translatable="false">com.android.stk</string>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 366dccb..2820f86 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3303,6 +3303,7 @@
<public name="sharedUserMaxSdkVersion" />
<public name="requiredSplitTypes" />
<public name="splitTypes" />
+ <public name="canDisplayOnRemoteDevices" />
</staging-public-group>
<staging-public-group type="id" first-id="0x01de0000">
@@ -3312,12 +3313,14 @@
<public name="accessibilityActionSwipeDown" />
</staging-public-group>
- <staging-public-group type="style" first-id="0x0dfd0000">
+ <staging-public-group type="style" first-id="0x01dd0000">
</staging-public-group>
<staging-public-group type="string" first-id="0x01dc0000">
<!-- @hide @SystemApi -->
<public name="config_systemSupervision" />
+ <!-- @hide @SystemApi -->
+ <public name="config_deviceManager" />
</staging-public-group>
<staging-public-group type="dimen" first-id="0x01db0000">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 391c7dc..5f08125 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1495,7 +1495,7 @@
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=50]-->
<string name="permlab_nearby_wifi_devices">interact with nearby Wi\u2011Fi devices</string>
- <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=120]-->
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=140]-->
<string name="permdesc_nearby_wifi_devices">Allows the app to advertise, connect, and determine the relative position of nearby Wi\u2011Fi devices</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
diff --git a/core/tests/coretests/src/android/os/UserHandleTest.java b/core/tests/coretests/src/android/os/UserHandleTest.java
index 4a1cdbf..160b20d 100644
--- a/core/tests/coretests/src/android/os/UserHandleTest.java
+++ b/core/tests/coretests/src/android/os/UserHandleTest.java
@@ -24,12 +24,15 @@
import static android.os.UserHandle.getUserId;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Random;
+
@RunWith(AndroidJUnit4.class)
public class UserHandleTest {
// NOTE: keep logic in sync with system/core/libcutils/tests/multiuser_test.cpp
@@ -117,4 +120,31 @@
private static int multiuser_get_app_id(int uid) {
return getAppId(uid);
}
+
+ @Test
+ public void testExtraCache() {
+ assertEquals(0, UserHandle.sExtraUserHandleCache.size());
+
+ // This shouldn't hit the extra cache.
+ assertEquals(10, UserHandle.of(10).getIdentifier());
+
+ assertEquals(0, UserHandle.sExtraUserHandleCache.size());
+
+ // This should hit the cache
+ assertEquals(20, UserHandle.of(20).getIdentifier());
+
+ assertEquals(1, UserHandle.sExtraUserHandleCache.size());
+
+ // Make sure the cache works.
+ final Random rnd = new Random();
+ for (int i = 0; i < 10000; i++) {
+ final int userId = rnd.nextInt(100);
+ assertEquals(userId, UserHandle.of(userId).getIdentifier());
+ assertSame(UserHandle.of(userId), UserHandle.of(userId));
+ }
+
+ // Make sure the cache size doesn't exceed the max size.
+ assertEquals(UserHandle.MAX_EXTRA_USER_HANDLE_CACHE_SIZE,
+ UserHandle.sExtraUserHandleCache.size());
+ }
}
diff --git a/data/etc/com.android.cellbroadcastreceiver.xml b/data/etc/com.android.cellbroadcastreceiver.xml
index 01a28a8..bc62bbc 100644
--- a/data/etc/com.android.cellbroadcastreceiver.xml
+++ b/data/etc/com.android.cellbroadcastreceiver.xml
@@ -19,6 +19,7 @@
<permission name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"/>
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.MANAGE_USERS"/>
+ <permission name="android.permission.STATUS_BAR"/>
<permission name="android.permission.MODIFY_PHONE_STATE"/>
<permission name="android.permission.MODIFY_CELL_BROADCASTS"/>
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 33cc61b..6983aa4 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -49,6 +49,7 @@
<permission name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"/>
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.MANAGE_USERS"/>
+ <permission name="android.permission.STATUS_BAR"/>
<permission name="android.permission.MODIFY_PHONE_STATE"/>
<permission name="android.permission.MODIFY_CELL_BROADCASTS"/>
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
index 9af508a..405f66d 100644
--- a/graphics/java/android/graphics/BLASTBufferQueue.java
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -31,7 +31,7 @@
long height, int format);
private static native void nativeDestroy(long ptr);
private static native Surface nativeGetSurface(long ptr, boolean includeSurfaceControlHandle);
- private static native void nativeSetNextTransaction(long ptr, long transactionPtr);
+ private static native void nativeSetSyncTransaction(long ptr, long transactionPtr);
private static native void nativeUpdate(long ptr, long surfaceControl, long width, long height,
int format, long transactionPtr);
private static native void nativeMergeWithNextTransaction(long ptr, long transactionPtr,
@@ -70,8 +70,8 @@
* This gives the caller a chance to apply the transaction when it's ready.
* @param t The transaction to add the frame to. This can be null to clear the transaction.
*/
- public void setNextTransaction(@Nullable SurfaceControl.Transaction t) {
- nativeSetNextTransaction(mNativeObject, t == null ? 0 : t.mNativeObject);
+ public void setSyncTransaction(@Nullable SurfaceControl.Transaction t) {
+ nativeSetSyncTransaction(mNativeObject, t == null ? 0 : t.mNativeObject);
}
/**
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
index 1e9fda6..44af1a9 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
@@ -73,17 +73,55 @@
static boolean shouldFinishPrimaryWithSecondary(@NonNull SplitRule splitRule) {
final boolean isPlaceholderContainer = splitRule instanceof SplitPlaceholderRule;
final boolean shouldFinishPrimaryWithSecondary = (splitRule instanceof SplitPairRule)
- && ((SplitPairRule) splitRule).shouldFinishPrimaryWithSecondary();
+ && ((SplitPairRule) splitRule).getFinishPrimaryWithSecondary()
+ != SplitRule.FINISH_NEVER;
return shouldFinishPrimaryWithSecondary || isPlaceholderContainer;
}
static boolean shouldFinishSecondaryWithPrimary(@NonNull SplitRule splitRule) {
final boolean isPlaceholderContainer = splitRule instanceof SplitPlaceholderRule;
final boolean shouldFinishSecondaryWithPrimary = (splitRule instanceof SplitPairRule)
- && ((SplitPairRule) splitRule).shouldFinishSecondaryWithPrimary();
+ && ((SplitPairRule) splitRule).getFinishSecondaryWithPrimary()
+ != SplitRule.FINISH_NEVER;
return shouldFinishSecondaryWithPrimary || isPlaceholderContainer;
}
+ static boolean shouldFinishAssociatedContainerWhenStacked(int finishBehavior) {
+ return finishBehavior == SplitRule.FINISH_ALWAYS;
+ }
+
+ static boolean shouldFinishAssociatedContainerWhenAdjacent(int finishBehavior) {
+ return finishBehavior == SplitRule.FINISH_ALWAYS
+ || finishBehavior == SplitRule.FINISH_ADJACENT;
+ }
+
+ static int getFinishPrimaryWithSecondaryBehavior(@NonNull SplitRule splitRule) {
+ if (splitRule instanceof SplitPlaceholderRule) {
+ return ((SplitPlaceholderRule) splitRule).getFinishPrimaryWithSecondary();
+ }
+ if (splitRule instanceof SplitPairRule) {
+ return ((SplitPairRule) splitRule).getFinishPrimaryWithSecondary();
+ }
+ return SplitRule.FINISH_NEVER;
+ }
+
+ static int getFinishSecondaryWithPrimaryBehavior(@NonNull SplitRule splitRule) {
+ if (splitRule instanceof SplitPlaceholderRule) {
+ return SplitRule.FINISH_ALWAYS;
+ }
+ if (splitRule instanceof SplitPairRule) {
+ return ((SplitPairRule) splitRule).getFinishSecondaryWithPrimary();
+ }
+ return SplitRule.FINISH_NEVER;
+ }
+
+ static boolean isStickyPlaceholderRule(@NonNull SplitRule splitRule) {
+ if (!(splitRule instanceof SplitPlaceholderRule)) {
+ return false;
+ }
+ return ((SplitPlaceholderRule) splitRule).isSticky();
+ }
+
@Override
public String toString() {
return "SplitContainer{"
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 9014102..68c1904 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -16,6 +16,12 @@
package androidx.window.extensions.embedding;
+import static androidx.window.extensions.embedding.SplitContainer.getFinishPrimaryWithSecondaryBehavior;
+import static androidx.window.extensions.embedding.SplitContainer.getFinishSecondaryWithPrimaryBehavior;
+import static androidx.window.extensions.embedding.SplitContainer.isStickyPlaceholderRule;
+import static androidx.window.extensions.embedding.SplitContainer.shouldFinishAssociatedContainerWhenAdjacent;
+import static androidx.window.extensions.embedding.SplitContainer.shouldFinishAssociatedContainerWhenStacked;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
@@ -460,6 +466,11 @@
return false;
}
+ if (isStickyPlaceholderRule(splitContainer.getSplitRule())) {
+ // The placeholder should remain after it was first shown.
+ return false;
+ }
+
if (mPresenter.shouldShowSideBySide(splitContainer)) {
return false;
}
@@ -643,6 +654,52 @@
return false;
}
+ /**
+ * Checks whether the associated container should be destroyed together with a finishing
+ * container. There is a case when primary containers for placeholders should be retained
+ * despite the rule configuration to finish primary with secondary - if they are marked as
+ * 'sticky' and the placeholder was finished when fully overlapping the primary container.
+ * @return {@code true} if the associated container should be retained (and not be finished).
+ */
+ boolean shouldRetainAssociatedContainer(@NonNull TaskFragmentContainer finishingContainer,
+ @NonNull TaskFragmentContainer associatedContainer) {
+ SplitContainer splitContainer = getActiveSplitForContainers(associatedContainer,
+ finishingContainer);
+ if (splitContainer == null) {
+ // Containers are not in the same split, no need to retain.
+ return false;
+ }
+ // Find the finish behavior for the associated container
+ int finishBehavior;
+ SplitRule splitRule = splitContainer.getSplitRule();
+ if (finishingContainer == splitContainer.getPrimaryContainer()) {
+ finishBehavior = getFinishSecondaryWithPrimaryBehavior(splitRule);
+ } else {
+ finishBehavior = getFinishPrimaryWithSecondaryBehavior(splitRule);
+ }
+ // Decide whether the associated container should be retained based on the current
+ // presentation mode.
+ if (mPresenter.shouldShowSideBySide(splitContainer)) {
+ return !shouldFinishAssociatedContainerWhenAdjacent(finishBehavior);
+ } else {
+ return !shouldFinishAssociatedContainerWhenStacked(finishBehavior);
+ }
+ }
+
+ /**
+ * @see #shouldRetainAssociatedContainer(TaskFragmentContainer, TaskFragmentContainer)
+ */
+ boolean shouldRetainAssociatedActivity(@NonNull TaskFragmentContainer finishingContainer,
+ @NonNull Activity associatedActivity) {
+ TaskFragmentContainer associatedContainer = getContainerWithActivity(
+ associatedActivity.getActivityToken());
+ if (associatedContainer == null) {
+ return false;
+ }
+
+ return shouldRetainAssociatedContainer(finishingContainer, associatedContainer);
+ }
+
private final class LifecycleCallbacks implements ActivityLifecycleCallbacks {
@Override
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index 6805fde..a1a53bc 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -228,6 +228,9 @@
// Finish dependent containers
for (TaskFragmentContainer container : mContainersToFinishOnExit) {
+ if (controller.shouldRetainAssociatedContainer(this, container)) {
+ continue;
+ }
container.finish(true /* shouldFinishDependent */, presenter,
wct, controller);
}
@@ -235,6 +238,9 @@
// Finish associated activities
for (Activity activity : mActivitiesToFinishOnExit) {
+ if (controller.shouldRetainAssociatedActivity(this, activity)) {
+ continue;
+ }
activity.finish();
}
mActivitiesToFinishOnExit.clear();
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
index 830d13d..d6678bf 100644
--- a/libs/WindowManager/Jetpack/window-extensions-release.aar
+++ b/libs/WindowManager/Jetpack/window-extensions-release.aar
Binary files differ
diff --git a/libs/WindowManager/Shell/res/layout/pip_menu.xml b/libs/WindowManager/Shell/res/layout/pip_menu.xml
index 9fe0247..7dc2f31 100644
--- a/libs/WindowManager/Shell/res/layout/pip_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/pip_menu.xml
@@ -65,25 +65,28 @@
<LinearLayout
android:id="@+id/top_end_container"
android:layout_gravity="top|end"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
+
<ImageButton
android:id="@+id/settings"
android:layout_width="@dimen/pip_action_size"
android:layout_height="@dimen/pip_action_size"
android:contentDescription="@string/pip_phone_settings"
+ android:layout_gravity="top|start"
android:gravity="center"
android:src="@drawable/pip_ic_settings"
android:background="?android:selectableItemBackgroundBorderless" />
<ImageButton
- android:id="@+id/dismiss"
+ android:id="@+id/enter_split"
android:layout_width="@dimen/pip_action_size"
android:layout_height="@dimen/pip_action_size"
- android:contentDescription="@string/pip_phone_close"
+ android:layout_gravity="top|start"
android:gravity="center"
- android:src="@drawable/pip_ic_close_white"
+ android:contentDescription="@string/pip_phone_enter_split"
+ android:src="@drawable/pip_expand"
android:background="?android:selectableItemBackgroundBorderless" />
</LinearLayout>
@@ -97,4 +100,14 @@
android:padding="@dimen/pip_resize_handle_padding"
android:src="@drawable/pip_resize_handle"
android:background="?android:selectableItemBackgroundBorderless" />
+
+ <ImageButton
+ android:id="@+id/dismiss"
+ android:layout_width="@dimen/pip_action_size"
+ android:layout_height="@dimen/pip_action_size"
+ android:contentDescription="@string/pip_phone_close"
+ android:layout_gravity="top|end"
+ android:gravity="center"
+ android:src="@drawable/pip_ic_close_white"
+ android:background="?android:selectableItemBackgroundBorderless" />
</FrameLayout>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 764854a..c88fc16 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -24,6 +24,9 @@
<!-- Label for PIP settings button [CHAR LIMIT=NONE]-->
<string name="pip_phone_settings">Settings</string>
+ <!-- Label for the PIP enter split button [CHAR LIMIT=NONE] -->
+ <string name="pip_phone_enter_split">Enter split screen</string>
+
<!-- Title of menu shown over picture-in-picture. Used for accessibility. -->
<string name="pip_menu_title">Menu</string>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index f7af4e1..caa5327 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -1036,10 +1036,9 @@
// notification, so that the bubble will be re-created if shouldBubbleUp returns
// true.
mBubbleData.dismissBubbleWithKey(key, DISMISS_NO_BUBBLE_UP);
- } else if (entry != null && mTmpRanking.isBubble() && !isActiveBubble
- && !entry.getRanking().isSuspended()) {
+ } else if (entry != null && mTmpRanking.isBubble() && !isActiveBubble) {
entry.setFlagBubble(true);
- onEntryUpdated(entry, true /* shouldBubbleUp */);
+ onEntryUpdated(entry, shouldBubbleUp && !entry.getRanking().isSuspended());
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
index b80dcd0..711a0ac 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
@@ -43,6 +43,7 @@
import com.android.wm.shell.pip.tv.TvPipMenuController;
import com.android.wm.shell.pip.tv.TvPipNotificationController;
import com.android.wm.shell.pip.tv.TvPipTransition;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.transition.Transitions;
import java.util.Optional;
@@ -160,13 +161,14 @@
PipTransitionController pipTransitionController,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
Optional<LegacySplitScreenController> splitScreenOptional,
+ Optional<SplitScreenController> newSplitScreenOptional,
DisplayController displayController,
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
return new PipTaskOrganizer(context,
syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm,
tvPipMenuController, pipAnimationController, pipSurfaceTransactionHelper,
- pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger,
- shellTaskOrganizer, mainExecutor);
+ pipTransitionController, splitScreenOptional, newSplitScreenOptional,
+ displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 944dfed..ec70147 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -55,6 +55,7 @@
import com.android.wm.shell.pip.phone.PipController;
import com.android.wm.shell.pip.phone.PipMotionHelper;
import com.android.wm.shell.pip.phone.PipTouchHandler;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm;
import com.android.wm.shell.startingsurface.phone.PhoneStartingWindowTypeAlgorithm;
import com.android.wm.shell.transition.Transitions;
@@ -215,14 +216,15 @@
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
PipTransitionController pipTransitionController,
Optional<LegacySplitScreenController> splitScreenOptional,
+ Optional<SplitScreenController> newSplitScreenOptional,
DisplayController displayController,
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
return new PipTaskOrganizer(context,
syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm,
menuPhoneController, pipAnimationController, pipSurfaceTransactionHelper,
- pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger,
- shellTaskOrganizer, mainExecutor);
+ pipTransitionController, splitScreenOptional, newSplitScreenOptional,
+ displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index b6e5804..6cc5f09 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -77,6 +77,7 @@
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.pip.phone.PipMotionHelper;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
@@ -126,7 +127,8 @@
private final int mExitAnimationDuration;
private final int mCrossFadeAnimationDuration;
private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
- private final Optional<LegacySplitScreenController> mSplitScreenOptional;
+ private final Optional<LegacySplitScreenController> mLegacySplitScreenOptional;
+ private final Optional<SplitScreenController> mSplitScreenOptional;
protected final ShellTaskOrganizer mTaskOrganizer;
protected final ShellExecutor mMainExecutor;
@@ -252,7 +254,8 @@
@NonNull PipAnimationController pipAnimationController,
@NonNull PipSurfaceTransactionHelper surfaceTransactionHelper,
@NonNull PipTransitionController pipTransitionController,
- Optional<LegacySplitScreenController> splitScreenOptional,
+ Optional<LegacySplitScreenController> legacySplitScreenOptional,
+ Optional<SplitScreenController> splitScreenOptional,
@NonNull DisplayController displayController,
@NonNull PipUiEventLogger pipUiEventLogger,
@NonNull ShellTaskOrganizer shellTaskOrganizer,
@@ -274,6 +277,7 @@
mPipAnimationController = pipAnimationController;
mPipUiEventLoggerLogger = pipUiEventLogger;
mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
+ mLegacySplitScreenOptional = legacySplitScreenOptional;
mSplitScreenOptional = splitScreenOptional;
mTaskOrganizer = shellTaskOrganizer;
mMainExecutor = mainExecutor;
@@ -373,8 +377,11 @@
* activity render it's final configuration while the Task is still in PiP.
* - setWindowingMode to undefined at the end of transition
* @param animationDurationMs duration in millisecond for the exiting PiP transition
+ * @param requestEnterSplit whether the enterSplit button is pressed on PiP or not.
+ * Indicate the user wishes to directly put PiP into split screen
+ * mode.
*/
- public void exitPip(int animationDurationMs) {
+ public void exitPip(int animationDurationMs, boolean requestEnterSplit) {
if (!mPipTransitionState.isInPip()
|| mPipTransitionState.getTransitionState() == PipTransitionState.EXITING_PIP
|| mToken == null) {
@@ -387,7 +394,7 @@
PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN);
final WindowContainerTransaction wct = new WindowContainerTransaction();
final Rect destinationBounds = mPipBoundsState.getDisplayBounds();
- final int direction = syncWithSplitScreenBounds(destinationBounds)
+ final int direction = syncWithSplitScreenBounds(destinationBounds, requestEnterSplit)
? TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
: TRANSITION_DIRECTION_LEAVE_PIP;
final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
@@ -396,7 +403,7 @@
// We set to fullscreen here for now, but later it will be set to UNDEFINED for
// the proper windowing mode to take place. See #applyWindowingModeChangeOnExit.
wct.setActivityWindowingMode(mToken,
- direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
+ direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN && !requestEnterSplit
? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
: WINDOWING_MODE_FULLSCREEN);
wct.setBounds(mToken, destinationBounds);
@@ -435,7 +442,7 @@
wct.setWindowingMode(mToken, getOutPipWindowingMode());
// Simply reset the activity mode set prior to the animation running.
wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
- mSplitScreenOptional.ifPresent(splitScreen -> {
+ mLegacySplitScreenOptional.ifPresent(splitScreen -> {
if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
wct.reparent(mToken, splitScreen.getSecondaryRoot(), true /* onTop */);
}
@@ -1165,6 +1172,7 @@
@PipAnimationController.TransitionDirection int direction,
@PipAnimationController.AnimationType int type) {
final Rect preResizeBounds = new Rect(mPipBoundsState.getBounds());
+ final boolean isPipTopLeft = isPipTopLeft();
mPipBoundsState.setBounds(destinationBounds);
if (direction == TRANSITION_DIRECTION_REMOVE_STACK) {
removePipImmediately();
@@ -1210,10 +1218,10 @@
null /* callback */, false /* withStartDelay */);
});
} else {
- applyFinishBoundsResize(wct, direction);
+ applyFinishBoundsResize(wct, direction, isPipTopLeft);
}
} else {
- applyFinishBoundsResize(wct, direction);
+ applyFinishBoundsResize(wct, direction, isPipTopLeft);
}
finishResizeForMenu(destinationBounds);
@@ -1241,7 +1249,11 @@
} else if (isOutPipDirection(direction)) {
// If we are animating to fullscreen or split screen, then we need to reset the
// override bounds on the task to ensure that the task "matches" the parent's bounds.
- taskBounds = null;
+ if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
+ taskBounds = destinationBounds;
+ } else {
+ taskBounds = null;
+ }
applyWindowingModeChangeOnExit(wct, direction);
} else {
// Just a resize in PIP
@@ -1261,8 +1273,20 @@
* applying it.
*/
public void applyFinishBoundsResize(@NonNull WindowContainerTransaction wct,
- @PipAnimationController.TransitionDirection int direction) {
- mTaskOrganizer.applyTransaction(wct);
+ @PipAnimationController.TransitionDirection int direction, boolean wasPipTopLeft) {
+ if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
+ mSplitScreenOptional.get().enterSplitScreen(mTaskInfo.taskId, wasPipTopLeft, wct);
+ } else {
+ mTaskOrganizer.applyTransaction(wct);
+ }
+ }
+
+ private boolean isPipTopLeft() {
+ final Rect topLeft = new Rect();
+ final Rect bottomRight = new Rect();
+ mSplitScreenOptional.get().getStageBounds(topLeft, bottomRight);
+
+ return topLeft.contains(mPipBoundsState.getBounds());
}
/**
@@ -1347,18 +1371,27 @@
}
/**
- * Sync with {@link LegacySplitScreenController} on destination bounds if PiP is going to split
- * screen.
+ * Sync with {@link LegacySplitScreenController} or {@link SplitScreenController} on destination
+ * bounds if PiP is going to split screen.
*
* @param destinationBoundsOut contain the updated destination bounds if applicable
* @return {@code true} if destinationBounds is altered for split screen
*/
- private boolean syncWithSplitScreenBounds(Rect destinationBoundsOut) {
- if (!mSplitScreenOptional.isPresent()) {
+ private boolean syncWithSplitScreenBounds(Rect destinationBoundsOut, boolean enterSplit) {
+ if (enterSplit && mSplitScreenOptional.isPresent()) {
+ final Rect topLeft = new Rect();
+ final Rect bottomRight = new Rect();
+ mSplitScreenOptional.get().getStageBounds(topLeft, bottomRight);
+ final boolean isPipTopLeft = isPipTopLeft();
+ destinationBoundsOut.set(isPipTopLeft ? topLeft : bottomRight);
+ return true;
+ }
+
+ if (!mLegacySplitScreenOptional.isPresent()) {
return false;
}
- LegacySplitScreenController legacySplitScreen = mSplitScreenOptional.get();
+ LegacySplitScreenController legacySplitScreen = mLegacySplitScreenOptional.get();
if (!legacySplitScreen.isDividerVisible()) {
// fail early if system is not in split screen mode
return false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index ae8c1b6..5687f4d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -95,6 +95,11 @@
* Called when the PIP requested to show the menu.
*/
void onPipShowMenu();
+
+ /**
+ * Called when the PIP requested to enter Split.
+ */
+ void onEnterSplit();
}
private final Matrix mMoveTransform = new Matrix();
@@ -458,6 +463,10 @@
mListeners.forEach(Listener::onPipDismiss);
}
+ void onEnterSplit() {
+ mListeners.forEach(Listener::onEnterSplit);
+ }
+
/**
* @return the best set of actions to show in the PiP menu.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
index 47a8c67..69ae45d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
@@ -151,7 +151,7 @@
result = true;
break;
case AccessibilityNodeInfo.ACTION_EXPAND:
- mMotionHelper.expandLeavePip();
+ mMotionHelper.expandLeavePip(false /* skipAnimation */);
result = true;
break;
default:
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index a32eb16..aeeb73f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -482,7 +482,8 @@
false /* fromShelfAdjustment */,
wct /* windowContainerTransaction */);
if (wct != null) {
- mPipTaskOrganizer.applyFinishBoundsResize(wct, TRANSITION_DIRECTION_SAME);
+ mPipTaskOrganizer.applyFinishBoundsResize(wct, TRANSITION_DIRECTION_SAME,
+ false /* wasPipTopLeft */);
}
};
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java
index 3eeba6e..0644657 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java
@@ -18,8 +18,6 @@
import android.content.Context;
import android.graphics.Rect;
-import android.util.Log;
-import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -34,6 +32,7 @@
protected ViewGroup mViewRoot;
protected ViewGroup mTopEndContainer;
protected View mDragHandle;
+ protected View mEnterSplitButton;
protected View mSettingsButton;
protected View mDismissButton;
@@ -44,14 +43,13 @@
* Bind the necessary views.
*/
public void bindViews(ViewGroup viewRoot, ViewGroup topEndContainer, View dragHandle,
- View settingsButton, View dismissButton) {
+ View enterSplitButton, View settingsButton, View dismissButton) {
mViewRoot = viewRoot;
mTopEndContainer = topEndContainer;
mDragHandle = dragHandle;
+ mEnterSplitButton = enterSplitButton;
mSettingsButton = settingsButton;
mDismissButton = dismissButton;
-
- bindInitialViewState();
}
/**
@@ -72,22 +70,4 @@
v.setLayoutParams(params);
}
}
-
- /** Calculate the initial state of the menu icons. Called when the menu is first created. */
- private void bindInitialViewState() {
- if (mViewRoot == null || mTopEndContainer == null || mDragHandle == null
- || mSettingsButton == null || mDismissButton == null) {
- Log.e(TAG, "One of the required views is null.");
- return;
- }
- // The menu view layout starts out with the settings button aligned at the top|end of the
- // view group next to the dismiss button. On phones, the settings button should be aligned
- // to the top|start of the view, so move it to parent view group to then align it to the
- // top|start of the menu.
- mTopEndContainer.removeView(mSettingsButton);
- mViewRoot.addView(mSettingsButton);
-
- setLayoutGravity(mDragHandle, Gravity.START | Gravity.TOP);
- setLayoutGravity(mSettingsButton, Gravity.START | Gravity.TOP);
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index 8ef2b6b..7bbebe5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -99,7 +99,7 @@
private static final float MENU_BACKGROUND_ALPHA = 0.3f;
private static final float DISABLED_ACTION_ALPHA = 0.54f;
- private static final boolean ENABLE_RESIZE_HANDLE = false;
+ private static final boolean ENABLE_ENTER_SPLIT = false;
private int mMenuState;
private boolean mAllowMenuTimeout = true;
@@ -139,7 +139,7 @@
protected View mViewRoot;
protected View mSettingsButton;
protected View mDismissButton;
- protected View mResizeHandle;
+ protected View mEnterSplitButton;
protected View mTopEndContainer;
protected PipMenuIconsAlgorithm mPipMenuIconsAlgorithm;
@@ -177,14 +177,23 @@
}
});
- mResizeHandle = findViewById(R.id.resize_handle);
- mResizeHandle.setAlpha(0);
+ mEnterSplitButton = findViewById(R.id.enter_split);
+ mEnterSplitButton.setAlpha(0);
+ mEnterSplitButton.setOnClickListener(v -> {
+ if (mMenuContainer.getAlpha() != 0) {
+ enterSplit();
+ }
+ });
+
+ findViewById(R.id.resize_handle).setAlpha(0);
+
mActionsGroup = findViewById(R.id.actions_group);
mBetweenActionPaddingLand = getResources().getDimensionPixelSize(
R.dimen.pip_between_action_padding_land);
mPipMenuIconsAlgorithm = new PipMenuIconsAlgorithm(mContext);
mPipMenuIconsAlgorithm.bindViews((ViewGroup) mViewRoot, (ViewGroup) mTopEndContainer,
- mResizeHandle, mSettingsButton, mDismissButton);
+ findViewById(R.id.resize_handle), mEnterSplitButton, mSettingsButton,
+ mDismissButton);
mDismissFadeOutDurationMs = context.getResources()
.getInteger(R.integer.config_pipExitAnimationDuration);
@@ -268,14 +277,13 @@
mSettingsButton.getAlpha(), 1f);
ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
mDismissButton.getAlpha(), 1f);
- ObjectAnimator resizeAnim = ObjectAnimator.ofFloat(mResizeHandle, View.ALPHA,
- mResizeHandle.getAlpha(),
- ENABLE_RESIZE_HANDLE && showResizeHandle ? 1f : 0f);
+ ObjectAnimator enterSplitAnim = ObjectAnimator.ofFloat(mEnterSplitButton, View.ALPHA,
+ mEnterSplitButton.getAlpha(), ENABLE_ENTER_SPLIT ? 1f : 0f);
if (menuState == MENU_STATE_FULL) {
mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim,
- resizeAnim);
+ enterSplitAnim);
} else {
- mMenuContainerAnimator.playTogether(resizeAnim);
+ mMenuContainerAnimator.playTogether(enterSplitAnim);
}
mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN);
mMenuContainerAnimator.setDuration(ANIMATION_HIDE_DURATION_MS);
@@ -328,7 +336,7 @@
mMenuContainer.setAlpha(0f);
mSettingsButton.setAlpha(0f);
mDismissButton.setAlpha(0f);
- mResizeHandle.setAlpha(0f);
+ mEnterSplitButton.setAlpha(0f);
}
void pokeMenu() {
@@ -368,9 +376,10 @@
mSettingsButton.getAlpha(), 0f);
ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
mDismissButton.getAlpha(), 0f);
- ObjectAnimator resizeAnim = ObjectAnimator.ofFloat(mResizeHandle, View.ALPHA,
- mResizeHandle.getAlpha(), 0f);
- mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim, resizeAnim);
+ ObjectAnimator enterSplitAnim = ObjectAnimator.ofFloat(mEnterSplitButton, View.ALPHA,
+ mEnterSplitButton.getAlpha(), 0f);
+ mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim,
+ enterSplitAnim);
mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_OUT);
mMenuContainerAnimator.setDuration(getFadeOutDuration(animationType));
mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
@@ -522,6 +531,14 @@
}
}
+ private void enterSplit() {
+ // Do not notify menu visibility when hiding the menu, the controller will do this when it
+ // handles the message
+ hideMenu(mController::onEnterSplit, false /* notifyMenuVisibility */, true /* resize */,
+ ANIM_TYPE_HIDE);
+ }
+
+
private void showSettings() {
final Pair<ComponentName, Integer> topPipActivityInfo =
PipUtils.getTopPipActivity(mContext);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index dbd09fd..c634b7f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -338,22 +338,29 @@
* Resizes the pinned stack back to unknown windowing mode, which could be freeform or
* * fullscreen depending on the display area's windowing mode.
*/
- void expandLeavePip() {
- expandLeavePip(false /* skipAnimation */);
+ void expandLeavePip(boolean skipAnimation) {
+ expandLeavePip(skipAnimation, false /* enterSplit */);
+ }
+
+ /**
+ * Resizes the pinned task to split-screen mode.
+ */
+ void expandIntoSplit() {
+ expandLeavePip(false, true /* enterSplit */);
}
/**
* Resizes the pinned stack back to unknown windowing mode, which could be freeform or
* fullscreen depending on the display area's windowing mode.
*/
- void expandLeavePip(boolean skipAnimation) {
+ private void expandLeavePip(boolean skipAnimation, boolean enterSplit) {
if (DEBUG) {
Log.d(TAG, "exitPip: skipAnimation=" + skipAnimation
+ " callers=\n" + Debug.getCallers(5, " "));
}
cancelPhysicsAnimation();
mMenuController.hideMenu(ANIM_TYPE_NONE, false /* resize */);
- mPipTaskOrganizer.exitPip(skipAnimation ? 0 : LEAVE_PIP_DURATION);
+ mPipTaskOrganizer.exitPip(skipAnimation ? 0 : LEAVE_PIP_DURATION, enterSplit);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 9f2f6a5..570fd5e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -139,7 +139,12 @@
@Override
public void onPipExpand() {
- mMotionHelper.expandLeavePip();
+ mMotionHelper.expandLeavePip(false /* skipAnimation */);
+ }
+
+ @Override
+ public void onEnterSplit() {
+ mMotionHelper.expandIntoSplit();
}
@Override
@@ -899,7 +904,7 @@
// Expand to fullscreen if this is a double tap
// the PiP should be frozen until the transition ends
setTouchEnabled(false);
- mMotionHelper.expandLeavePip();
+ mMotionHelper.expandLeavePip(false /* skipAnimation */);
}
} else if (mMenuState != MENU_STATE_FULL) {
if (mPipBoundsState.isStashed()) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index a2e9b64..00083d9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -219,7 +219,7 @@
public void movePipToFullscreen() {
if (DEBUG) Log.d(TAG, "movePipToFullscreen(), state=" + stateToName(mState));
- mPipTaskOrganizer.exitPip(mResizeAnimationDuration);
+ mPipTaskOrganizer.exitPip(mResizeAnimationDuration, false /* requestEnterSplit */);
onPipDisappeared();
}
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 04058ed..7457be2 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
@@ -202,11 +202,25 @@
return moveToSideStage(task, sideStagePosition);
}
+ public boolean moveToSideStage(int taskId, @SplitPosition int sideStagePosition,
+ WindowContainerTransaction wct) {
+ final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
+ if (task == null) {
+ throw new IllegalArgumentException("Unknown taskId" + taskId);
+ }
+ return moveToSideStage(task, sideStagePosition, wct);
+ }
+
public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
@SplitPosition int sideStagePosition) {
return mStageCoordinator.moveToSideStage(task, sideStagePosition);
}
+ public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
+ @SplitPosition int sideStagePosition, WindowContainerTransaction wct) {
+ return mStageCoordinator.moveToSideStage(task, sideStagePosition, wct);
+ }
+
public boolean removeFromSideStage(int taskId) {
return mStageCoordinator.removeFromSideStage(taskId);
}
@@ -224,6 +238,11 @@
leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT);
}
+ public void enterSplitScreen(int taskId, boolean leftOrTop, WindowContainerTransaction wct) {
+ moveToSideStage(taskId,
+ leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT, wct);
+ }
+
public void exitSplitScreen(int toTopTaskId, @ExitReason int exitReason) {
mStageCoordinator.exitSplitScreen(toTopTaskId, exitReason);
}
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 3589f7c..95886c8 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
@@ -281,6 +281,11 @@
boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
@SplitPosition int sideStagePosition) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
+ return moveToSideStage(task, sideStagePosition, wct);
+ }
+
+ boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
+ @SplitPosition int sideStagePosition, WindowContainerTransaction wct) {
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
setSideStagePosition(sideStagePosition, wct);
mSideStage.evictAllChildren(evictWct);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 0270093..0172cf32 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -50,6 +50,7 @@
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.pip.phone.PhonePipMenuController;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import org.junit.Before;
import org.junit.Test;
@@ -75,7 +76,8 @@
@Mock private PipTransitionController mMockPipTransitionController;
@Mock private PipSurfaceTransactionHelper mMockPipSurfaceTransactionHelper;
@Mock private PipUiEventLogger mMockPipUiEventLogger;
- @Mock private Optional<LegacySplitScreenController> mMockOptionalSplitScreen;
+ @Mock private Optional<LegacySplitScreenController> mMockOptionalLegacySplitScreen;
+ @Mock private Optional<SplitScreenController> mMockOptionalSplitScreen;
@Mock private ShellTaskOrganizer mMockShellTaskOrganizer;
private TestShellExecutor mMainExecutor;
private PipBoundsState mPipBoundsState;
@@ -99,8 +101,9 @@
mMockSyncTransactionQueue, mPipTransitionState, mPipBoundsState,
mPipBoundsAlgorithm, mMockPhonePipMenuController,
mMockPipAnimationController, mMockPipSurfaceTransactionHelper,
- mMockPipTransitionController, mMockOptionalSplitScreen, mMockDisplayController,
- mMockPipUiEventLogger, mMockShellTaskOrganizer, mMainExecutor));
+ mMockPipTransitionController, mMockOptionalLegacySplitScreen,
+ mMockOptionalSplitScreen, mMockDisplayController, mMockPipUiEventLogger,
+ mMockShellTaskOrganizer, mMainExecutor));
mMainExecutor.flushAll();
preparePipTaskOrg();
}
diff --git a/libs/hwui/pipeline/skia/FunctorDrawable.h b/libs/hwui/pipeline/skia/FunctorDrawable.h
index 9bbd0a9..29ef2b8 100644
--- a/libs/hwui/pipeline/skia/FunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/FunctorDrawable.h
@@ -34,6 +34,8 @@
*/
class FunctorDrawable : public SkDrawable {
public:
+ constexpr static const char* const TYPE_NAME = "FunctorDrawable";
+
FunctorDrawable(int functor, SkCanvas* canvas)
: mBounds(canvas->getLocalClipBounds())
, mWebViewHandle(WebViewFunctorManager::instance().handleFor(functor)) {}
@@ -48,6 +50,8 @@
mWebViewHandle->onRemovedFromTree();
}
+ const char* getTypeName() const override { return TYPE_NAME; }
+
protected:
virtual SkRect onGetBounds() override { return mBounds; }
diff --git a/libs/hwui/pipeline/skia/TransformCanvas.cpp b/libs/hwui/pipeline/skia/TransformCanvas.cpp
index 6777c00..41e3687 100644
--- a/libs/hwui/pipeline/skia/TransformCanvas.cpp
+++ b/libs/hwui/pipeline/skia/TransformCanvas.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
#include "TransformCanvas.h"
+
+#include "FunctorDrawable.h"
#include "HolePunch.h"
#include "SkData.h"
#include "SkDrawable.h"
@@ -35,7 +37,17 @@
}
void TransformCanvas::onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) {
- drawable->draw(this, matrix);
+ // TransformCanvas filters all drawing commands while maintaining the current
+ // clip stack and transformation. We need to draw most SkDrawables, since their
+ // draw calls may call methods that affect the clip stack and transformation. (Any
+ // actual draw commands will then be filtered out.) But FunctorDrawables are used
+ // as leaf nodes which issue self-contained OpenGL/Vulkan commands. These won't
+ // affect the clip stack + transformation, and in some cases cause problems (e.g. if
+ // the surface only has an alpha channel). See b/203960959
+ const auto* drawableName = drawable->getTypeName();
+ if (drawableName == nullptr || strcmp(drawableName, FunctorDrawable::TYPE_NAME) != 0) {
+ drawable->draw(this, matrix);
+ }
}
bool TransformCanvas::onFilter(SkPaint& paint) const {
diff --git a/packages/SettingsLib/AndroidManifest.xml b/packages/SettingsLib/AndroidManifest.xml
index ad62f6e..a347345 100644
--- a/packages/SettingsLib/AndroidManifest.xml
+++ b/packages/SettingsLib/AndroidManifest.xml
@@ -18,6 +18,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.settingslib">
- <uses-sdk android:minSdkVersion="29" />
-
</manifest>
diff --git a/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml b/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml
index 5817f77..6e0d827 100644
--- a/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml
+++ b/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml
@@ -18,8 +18,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.settingslib.widget">
- <uses-sdk
- android:minSdkVersion="28"
- android:targetSdkVersion="31"/>
-
</manifest>
diff --git a/packages/SettingsLib/Utils/AndroidManifest.xml b/packages/SettingsLib/Utils/AndroidManifest.xml
index fd89676..e9957a9 100644
--- a/packages/SettingsLib/Utils/AndroidManifest.xml
+++ b/packages/SettingsLib/Utils/AndroidManifest.xml
@@ -18,6 +18,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.settingslib.utils">
- <uses-sdk android:minSdkVersion="21" />
-
</manifest>
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledLearnMoreButtonLauncher.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledLearnMoreButtonLauncher.java
index ba63a8e..0416b68 100644
--- a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledLearnMoreButtonLauncher.java
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledLearnMoreButtonLauncher.java
@@ -52,7 +52,6 @@
public final void setupLearnMoreButtonToShowAdminPolicies(Context context,
int enforcementAdminUserId, EnforcedAdmin enforcedAdmin) {
requireNonNull(context, "context cannot be null");
- requireNonNull(enforcedAdmin, "enforcedAdmin cannot be null");
// The "Learn more" button appears only if the restriction is enforced by an admin in the
// same profile group or by the device owner. Otherwise the admin package and its policies
@@ -132,7 +131,7 @@
}
private void showAdminPolicies(Context context, EnforcedAdmin enforcedAdmin) {
- if (enforcedAdmin.component != null) {
+ if (enforcedAdmin != null && enforcedAdmin.component != null) {
launchShowAdminPolicies(context, enforcedAdmin.user, enforcedAdmin.component);
} else {
launchShowAdminSettings(context);
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 713faaa..986fd45 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -303,9 +303,6 @@
<!-- This value is used for the default system capabilities used on LE device. -->
<string name="def_wearable_leSystemCapabilities" translatable="false">1</string>
- <!-- Brightness levels, on a 0-255 scale -->
- <string name="def_wearable_brightnessLevels" translatable="false">255,204,153,102,51</string>
-
<!-- Whether to allow mobile signal detector by default. -->
<bool name="def_wearable_mobileSignalDetectorAllowed">true</bool>
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 96f127b..fb6c248 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -42,6 +42,7 @@
Settings.Secure.AUTOFILL_SERVICE,
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+ Settings.Secure.ENABLED_ACCESSIBILITY_AUDIO_DESCRIPTION_BY_DEFAULT,
Settings.Secure.ENABLED_VR_LISTENERS,
Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
Settings.Secure.TOUCH_EXPLORATION_ENABLED,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index bf8b933..7859159 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -66,6 +66,8 @@
new InclusiveFloatRangeValidator(1.0f, Float.MAX_VALUE));
VALIDATORS.put(
Secure.ENABLED_ACCESSIBILITY_SERVICES, COLON_SEPARATED_COMPONENT_LIST_VALIDATOR);
+ VALIDATORS.put(
+ Secure.ENABLED_ACCESSIBILITY_AUDIO_DESCRIPTION_BY_DEFAULT, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ENABLED_VR_LISTENERS, COLON_SEPARATED_COMPONENT_LIST_VALIDATOR);
VALIDATORS.put(
Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index cd4047b..527fd88 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -5294,11 +5294,6 @@
Global.Wearable.WEAR_PLATFORM_MR_NUMBER,
SystemProperties.getInt("ro.cw_build.platform_mr", 0));
initGlobalSettingsDefaultValForWearLocked(
- Settings.Global.Wearable.SCREEN_BRIGHTNESS_LEVEL,
- getContext()
- .getResources()
- .getString(R.string.def_wearable_brightnessLevels));
- initGlobalSettingsDefaultValForWearLocked(
Settings.Global.Wearable.MOBILE_SIGNAL_DETECTOR,
getContext()
.getResources()
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index c05e01d..53df2e82 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -272,7 +272,6 @@
Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD,
Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS,
Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS,
- Settings.Global.ENABLE_ACCESSIBILITY_AUDIO_DESCRIPTION_BY_DEFAULT,
Settings.Global.ENABLE_ADB_INCREMENTAL_INSTALL_DEFAULT,
Settings.Global.ENABLE_MULTI_SLOT_TIMEOUT_MILLIS,
Settings.Global.ENHANCED_4G_MODE_ENABLED,
@@ -632,7 +631,6 @@
Settings.Global.Wearable.SYSTEM_CAPABILITIES,
Settings.Global.Wearable.SYSTEM_EDITION,
Settings.Global.Wearable.WEAR_PLATFORM_MR_NUMBER,
- Settings.Global.Wearable.SCREEN_BRIGHTNESS_LEVEL,
Settings.Global.Wearable.MOBILE_SIGNAL_DETECTOR,
Settings.Global.Wearable.AMBIENT_ENABLED,
Settings.Global.Wearable.AMBIENT_TILT_TO_WAKE,
@@ -710,6 +708,7 @@
Settings.Secure.DOCKED_CLOCK_FACE,
Settings.Secure.DOZE_PULSE_ON_LONG_PRESS,
Settings.Secure.EMERGENCY_ASSISTANCE_APPLICATION,
+ Settings.Secure.ENABLED_ACCESSIBILITY_AUDIO_DESCRIPTION_BY_DEFAULT,
Settings.Secure.ENABLED_INPUT_METHODS, // Intentionally removed in P
Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT,
Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index e1e9420..dde0a19 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -294,9 +294,6 @@
<uses-permission android:name="android.permission.READ_PEOPLE_DATA" />
- <!-- Permission for dream overlay. -->
- <uses-permission android:name="android.permission.BIND_DREAM_SERVICE" />
-
<uses-permission android:name="android.permission.BIND_APPWIDGET" />
<protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index ce23a8b..e1da744 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -11,6 +11,7 @@
awickham@google.com
beverlyt@google.com
brockman@google.com
+brycelee@google.com
ccassidy@google.com
cinek@google.com
cwren@google.com
@@ -71,4 +72,4 @@
hseog@google.com
#Android TV
-rgl@google.com
\ No newline at end of file
+rgl@google.com
diff --git a/packages/SystemUI/plugin/AndroidManifest.xml b/packages/SystemUI/plugin/AndroidManifest.xml
index 7c057dc..811595ad 100644
--- a/packages/SystemUI/plugin/AndroidManifest.xml
+++ b/packages/SystemUI/plugin/AndroidManifest.xml
@@ -18,7 +18,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.systemui.plugins">
- <uses-sdk
- android:minSdkVersion="21" />
-
</manifest>
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index a110413..07c2ac4 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -5,8 +5,8 @@
-keep class com.android.systemui.car.CarSystemUIFactory
-keep class com.android.systemui.SystemUIFactory
-keep class com.android.systemui.tv.TvSystemUIFactory
--keep class * extends com.android.systemui.SystemUI
--keep class * implements com.android.systemui.SystemUI$Injector
+-keep class * extends com.android.systemui.CoreStartable
+-keep class * implements com.android.systemui.CoreStartable$Injector
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
@@ -22,7 +22,7 @@
}
-keep class androidx.core.app.CoreComponentFactory
--keep public class * extends com.android.systemui.SystemUI {
+-keep public class * extends com.android.systemui.CoreStartable {
public <init>(android.content.Context);
}
diff --git a/packages/SystemUI/res/layout/notif_half_shelf.xml b/packages/SystemUI/res/layout/notif_half_shelf.xml
index 6bc0138..b2ff46e 100644
--- a/packages/SystemUI/res/layout/notif_half_shelf.xml
+++ b/packages/SystemUI/res/layout/notif_half_shelf.xml
@@ -78,12 +78,6 @@
android:layout_gravity="center_vertical"
android:padding="8dp" />
</com.android.systemui.statusbar.notification.row.AppControlView>
- <!-- divider view -->
- <View
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:background="@*android:color/background_device_default_light"
- />
<!-- ChannelRows get added dynamically -->
diff --git a/packages/SystemUI/res/layout/notif_half_shelf_row.xml b/packages/SystemUI/res/layout/notif_half_shelf_row.xml
index 245d157..d03cd7e 100644
--- a/packages/SystemUI/res/layout/notif_half_shelf_row.xml
+++ b/packages/SystemUI/res/layout/notif_half_shelf_row.xml
@@ -87,10 +87,4 @@
android:padding="8dp"
/>
</LinearLayout>
- <!-- divider view -->
- <View
- android:layout_width="match_parent"
- android:layout_height=".5dp"
- android:background="@*android:color/background_device_default_light"
- />
</com.android.systemui.statusbar.notification.row.ChannelRow>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index d2ea789..af5d85d 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -77,7 +77,7 @@
<!-- The color of the gear shown behind a notification -->
<color name="notification_gear_color">@color/GM2_grey_700</color>
- <color name="notification_guts_link_icon_tint">@color/GM2_grey_700</color>
+ <color name="notification_guts_link_icon_tint">@*android:color/accent_device_default</color>
<color name="notification_guts_sub_text_color">@color/GM2_grey_700</color>
<color name="notification_guts_header_text_color">@color/GM2_grey_900</color>
<color name="notification_guts_priority_button_content_color">@color/GM2_grey_700</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index fc6b99a..cbf3e83 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -638,9 +638,6 @@
screen on inactivity. -->
<bool name="config_enableIdleMode">false</bool>
- <!-- Timeout to idle mode duration in milliseconds. -->
- <integer name="config_idleModeTimeout">10000</integer>
-
<!-- This value is used when calculating whether the device is in ambient light mode. It is
light mode when the light sensor sample value exceeds above this value. -->
<integer name="config_ambientLightModeThreshold">5</integer>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 974eda7..d972b7fc 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -213,8 +213,6 @@
<item name="android:paddingTop">12dp</item>
<item name="android:paddingHorizontal">24dp</item>
<item name="android:textSize">24sp</item>
- <item name="android:singleLine">true</item>
- <item name="android:ellipsize">marquee</item>
</style>
<style name="TextAppearance.AuthCredential.Subtitle">
@@ -222,8 +220,6 @@
<item name="android:paddingTop">8dp</item>
<item name="android:paddingHorizontal">24dp</item>
<item name="android:textSize">16sp</item>
- <item name="android:singleLine">true</item>
- <item name="android:ellipsize">marquee</item>
</style>
<style name="TextAppearance.AuthCredential.Description">
@@ -231,8 +227,6 @@
<item name="android:paddingTop">8dp</item>
<item name="android:paddingHorizontal">24dp</item>
<item name="android:textSize">14sp</item>
- <item name="android:singleLine">true</item>
- <item name="android:ellipsize">marquee</item>
</style>
<style name="TextAppearance.AuthCredential.Error">
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt
index ee6dea5..91a3912 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt
@@ -20,6 +20,11 @@
*/
interface FlagReader {
/** Returns a boolean value for the given flag. */
+ fun isEnabled(flag: BooleanFlag): Boolean {
+ return flag.default
+ }
+
+ /** Returns a boolean value for the given flag. */
fun isEnabled(id: Int, def: Boolean): Boolean {
return def
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
index 3f2ff74..3128ffd 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
@@ -23,17 +23,14 @@
import android.app.TaskInfo;
import android.content.ComponentName;
import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.ViewDebug;
-import com.android.systemui.shared.recents.utilities.Utilities;
+import androidx.annotation.Nullable;
import java.io.PrintWriter;
-import java.util.ArrayList;
import java.util.Objects;
/**
@@ -202,8 +199,8 @@
* The icon is the task description icon (if provided), which falls back to the activity icon,
* which can then fall back to the application icon.
*/
- public Drawable icon;
- public ThumbnailData thumbnail;
+ @Nullable public Drawable icon;
+ @Nullable public ThumbnailData thumbnail;
@ViewDebug.ExportedProperty(category="recents")
@Deprecated
public String title;
diff --git a/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java b/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java
index ef04619..acfa3c8 100644
--- a/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java
+++ b/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java
@@ -26,13 +26,16 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.res.Resources;
import android.os.Bundle;
import android.util.Log;
+import androidx.annotation.BoolRes;
import androidx.annotation.NonNull;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.util.settings.SecureSettings;
@@ -62,14 +65,19 @@
private final FlagManager mFlagManager;
private final SecureSettings mSecureSettings;
+ private final Resources mResources;
private final Map<Integer, Boolean> mBooleanFlagCache = new HashMap<>();
@Inject
- public FeatureFlagManager(FlagManager flagManager,
- SecureSettings secureSettings, Context context,
+ public FeatureFlagManager(
+ FlagManager flagManager,
+ Context context,
+ SecureSettings secureSettings,
+ @Main Resources resources,
DumpManager dumpManager) {
mFlagManager = flagManager;
mSecureSettings = secureSettings;
+ mResources = resources;
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_SET_FLAG);
filter.addAction(ACTION_GET_FLAGS);
@@ -77,17 +85,32 @@
dumpManager.registerDumpable(TAG, this);
}
- /** Return a {@link BooleanFlag}'s value. */
@Override
- public boolean isEnabled(int id, boolean defaultValue) {
+ public boolean isEnabled(BooleanFlag flag) {
+ int id = flag.getId();
if (!mBooleanFlagCache.containsKey(id)) {
- Boolean result = isEnabledInternal(id);
- mBooleanFlagCache.put(id, result == null ? defaultValue : result);
+ boolean def = flag.getDefault();
+ if (flag.hasResourceOverride()) {
+ try {
+ def = isEnabledInOverlay(flag.getResourceOverride());
+ } catch (Resources.NotFoundException e) {
+ // no-op
+ }
+ }
+
+ mBooleanFlagCache.put(id, isEnabled(id, def));
}
return mBooleanFlagCache.get(id);
}
+ /** Return a {@link BooleanFlag}'s value. */
+ @Override
+ public boolean isEnabled(int id, boolean defaultValue) {
+ Boolean result = isEnabledInternal(id);
+ return result == null ? defaultValue : result;
+ }
+
/** Returns the stored value or null if not set. */
private Boolean isEnabledInternal(int id) {
try {
@@ -98,6 +121,10 @@
return null;
}
+ private boolean isEnabledInOverlay(@BoolRes int resId) {
+ return mResources.getBoolean(resId);
+ }
+
/** Set whether a given {@link BooleanFlag} is enabled or not. */
@Override
public void setEnabled(int id, boolean value) {
diff --git a/packages/SystemUI/src-release/com/android/systemui/flags/FeatureFlagManager.java b/packages/SystemUI/src-release/com/android/systemui/flags/FeatureFlagManager.java
index 6ff175f..0934b32 100644
--- a/packages/SystemUI/src-release/com/android/systemui/flags/FeatureFlagManager.java
+++ b/packages/SystemUI/src-release/com/android/systemui/flags/FeatureFlagManager.java
@@ -16,7 +16,6 @@
package com.android.systemui.flags;
-import android.content.Context;
import android.util.SparseBooleanArray;
import androidx.annotation.NonNull;
@@ -24,7 +23,6 @@
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.util.settings.SecureSettings;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -41,8 +39,7 @@
public class FeatureFlagManager implements FlagReader, FlagWriter, Dumpable {
SparseBooleanArray mAccessedFlags = new SparseBooleanArray();
@Inject
- public FeatureFlagManager(
- SecureSettings secureSettings, Context context, DumpManager dumpManager) {
+ public FeatureFlagManager(DumpManager dumpManager) {
dumpManager.registerDumpable("SysUIFlags", this);
}
@@ -53,6 +50,11 @@
public void removeListener(Listener run) {}
@Override
+ public boolean isEnabled(BooleanFlag flag) {
+ return isEnabled(flag.getId(), flag.getDefault());
+ }
+
+ @Override
public boolean isEnabled(int key, boolean defaultValue) {
mAccessedFlags.append(key, defaultValue);
return defaultValue;
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
index a383cab..ac463eb 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
@@ -31,7 +31,6 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.util.ViewController;
@@ -49,7 +48,6 @@
private final StatusBarStateController mStatusBarStateController;
private final BroadcastDispatcher mBroadcastDispatcher;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- private final KeyguardBypassController mBypassController;
private final BatteryController mBatteryController;
private final int mDozingColor = Color.WHITE;
private int mLockScreenColor;
@@ -71,14 +69,12 @@
BroadcastDispatcher broadcastDispatcher,
BatteryController batteryController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
- KeyguardBypassController bypassController,
@Main Resources resources
) {
super(view);
mStatusBarStateController = statusBarStateController;
mBroadcastDispatcher = broadcastDispatcher;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- mBypassController = bypassController;
mBatteryController = batteryController;
mBurmeseNumerals = mBurmeseNf.format(FORMAT_NUMBER);
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
index ef3104a..2a0c285 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
@@ -27,7 +27,6 @@
import android.widget.TextView;
import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
import java.util.Calendar;
import java.util.Locale;
@@ -111,6 +110,28 @@
super.onDetachedFromWindow();
}
+ int getDozingWeight() {
+ if (useBoldedVersion()) {
+ return mDozingWeight + 100;
+ }
+ return mDozingWeight;
+ }
+
+ int getLockScreenWeight() {
+ if (useBoldedVersion()) {
+ return mLockScreenWeight + 100;
+ }
+ return mLockScreenWeight;
+ }
+
+ /**
+ * Whether to use a bolded version based on the user specified fontWeightAdjustment.
+ */
+ boolean useBoldedVersion() {
+ // "Bold text" fontWeightAdjustment is 300.
+ return getResources().getConfiguration().fontWeightAdjustment > 100;
+ }
+
void refreshTime() {
mTime.setTimeInMillis(System.currentTimeMillis());
setText(DateFormat.format(mFormat, mTime));
@@ -162,7 +183,7 @@
}
setTextStyle(
- mDozingWeight,
+ getDozingWeight(),
-1 /* text size, no update */,
mLockScreenColor,
false /* animate */,
@@ -171,7 +192,7 @@
null /* onAnimationEnd */);
setTextStyle(
- mLockScreenWeight,
+ getLockScreenWeight(),
-1 /* text size, no update */,
mLockScreenColor,
true, /* animate */
@@ -180,35 +201,22 @@
null /* onAnimationEnd */);
}
- void animateDisappear() {
- if (mTextAnimator == null) {
- return;
- }
-
- setTextStyle(
- 0 /* weight */,
- -1 /* text size, no update */,
- null /* color, no update */,
- true /* animate */,
- KeyguardBypassController.BYPASS_FADE_DURATION /* duration */,
- 0 /* delay */,
- null /* onAnimationEnd */);
- }
-
void animateCharge(DozeStateGetter dozeStateGetter) {
if (mTextAnimator == null || mTextAnimator.isRunning()) {
// Skip charge animation if dozing animation is already playing.
return;
}
Runnable startAnimPhase2 = () -> setTextStyle(
- dozeStateGetter.isDozing() ? mDozingWeight : mLockScreenWeight/* weight */,
+ dozeStateGetter.isDozing() ? getDozingWeight() : getLockScreenWeight() /* weight */,
-1,
null,
true /* animate */,
CHARGE_ANIM_DURATION_PHASE_1,
0 /* delay */,
null /* onAnimationEnd */);
- setTextStyle(dozeStateGetter.isDozing() ? mLockScreenWeight : mDozingWeight/* weight */,
+ setTextStyle(dozeStateGetter.isDozing()
+ ? getLockScreenWeight()
+ : getDozingWeight()/* weight */,
-1,
null,
true /* animate */,
@@ -218,7 +226,7 @@
}
void animateDoze(boolean isDozing, boolean animate) {
- setTextStyle(isDozing ? mDozingWeight : mLockScreenWeight /* weight */,
+ setTextStyle(isDozing ? getDozingWeight() : getLockScreenWeight() /* weight */,
-1,
isDozing ? mDozingColor : mLockScreenColor,
animate,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 1931c0a..905495d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -167,7 +167,6 @@
mBroadcastDispatcher,
mBatteryController,
mKeyguardUpdateMonitor,
- mBypassController,
mResources);
mClockViewController.init();
@@ -178,7 +177,6 @@
mBroadcastDispatcher,
mBatteryController,
mKeyguardUpdateMonitor,
- mBypassController,
mResources);
mLargeClockViewController.init();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 85bc8f7..d27bc67 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -2044,17 +2044,17 @@
}
/**
- * @return true if there's at least one udfps enrolled
+ * @return true if there's at least one udfps enrolled for the current user.
*/
public boolean isUdfpsEnrolled() {
return mIsUdfpsEnrolled;
}
/**
- * @return if udfps is available on this device. will return true even if the user hasn't
- * enrolled udfps. This may be false if called before onAllAuthenticatorsRegistered.
+ * @return true if udfps HW is supported on this device. Can return true even if the user has
+ * not enrolled udfps. This may be false if called before onAllAuthenticatorsRegistered.
*/
- public boolean isUdfpsAvailable() {
+ public boolean isUdfpsSupported() {
return mAuthController.getUdfpsProps() != null
&& !mAuthController.getUdfpsProps().isEmpty();
}
@@ -2102,7 +2102,7 @@
}
updateUdfpsEnrolled(getCurrentUser());
- final boolean shouldListenForFingerprint = shouldListenForFingerprint(isUdfpsEnrolled());
+ final boolean shouldListenForFingerprint = shouldListenForFingerprint(isUdfpsSupported());
final boolean runningOrRestarting = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING
|| mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING;
if (runningOrRestarting && !shouldListenForFingerprint) {
@@ -2407,7 +2407,7 @@
} else {
mFpm.authenticate(null /* crypto */, mFingerprintCancelSignal,
mFingerprintAuthenticationCallback, null /* handler */,
- FingerprintManager.SENSOR_ID_ANY, userId);
+ FingerprintManager.SENSOR_ID_ANY, userId, 0 /* flags */);
}
setFingerprintRunningState(BIOMETRIC_STATE_RUNNING);
}
@@ -2990,7 +2990,7 @@
/**
* Register to receive notifications about general keyguard information
- * (see {@link InfoCallback}.
+ * (see {@link KeyguardUpdateMonitorCallback}.
*
* @param callback The callback to register
*/
@@ -3388,11 +3388,11 @@
+ " expected=" + (shouldListenForFingerprint(isUdfpsEnrolled()) ? 1 : 0));
pw.println(" strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
pw.println(" trustManaged=" + getUserTrustIsManaged(userId));
- pw.println(" udfpsEnrolled=" + isUdfpsEnrolled());
pw.println(" mFingerprintLockedOut=" + mFingerprintLockedOut);
pw.println(" mFingerprintLockedOutPermanent=" + mFingerprintLockedOutPermanent);
pw.println(" enabledByUser=" + mBiometricEnabledForUser.get(userId));
- if (isUdfpsEnrolled()) {
+ if (isUdfpsSupported()) {
+ pw.println(" udfpsEnrolled=" + isUdfpsEnrolled());
pw.println(" shouldListenForUdfps=" + shouldListenForFingerprint(true));
pw.println(" bouncerVisible=" + mBouncer);
pw.println(" mStatusBarState="
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
index 23438a9..a382b53 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
@@ -136,6 +136,13 @@
.setStartDelay(delay);
}
animator.start();
+ } else if (mUnlockedScreenOffAnimationController.shouldAnimateInKeyguard()) {
+ mKeyguardViewVisibilityAnimating = true;
+
+ // Ask the screen off animation controller to animate the keyguard visibility for us
+ // since it may need to be cancelled due to keyguard lifecycle events.
+ mUnlockedScreenOffAnimationController.animateInKeyguard(
+ mView, mAnimateKeyguardStatusViewVisibleEndRunnable);
} else if (mLastOccludedState && !isOccluded) {
// An activity was displayed over the lock screen, and has now gone away
mView.setVisibility(View.VISIBLE);
@@ -147,13 +154,6 @@
.alpha(1f)
.withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable)
.start();
- } else if (mUnlockedScreenOffAnimationController.shouldAnimateInKeyguard()) {
- mKeyguardViewVisibilityAnimating = true;
-
- // Ask the screen off animation controller to animate the keyguard visibility for us
- // since it may need to be cancelled due to keyguard lifecycle events.
- mUnlockedScreenOffAnimationController.animateInKeyguard(
- mView, mAnimateKeyguardStatusViewVisibleEndRunnable);
} else {
mView.setVisibility(View.VISIBLE);
mView.setAlpha(1f);
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 8a0b5b8..c7be3ce 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -435,7 +435,7 @@
boolean wasUdfpsSupported = mUdfpsSupported;
boolean wasUdfpsEnrolled = mUdfpsEnrolled;
- mUdfpsSupported = mAuthController.getUdfpsSensorLocation() != null;
+ mUdfpsSupported = mKeyguardUpdateMonitor.isUdfpsSupported();
mView.setUseBackground(mUdfpsSupported);
mUdfpsEnrolled = mKeyguardUpdateMonitor.isUdfpsEnrolled();
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUI.java b/packages/SystemUI/src/com/android/systemui/CoreStartable.java
similarity index 85%
rename from packages/SystemUI/src/com/android/systemui/SystemUI.java
rename to packages/SystemUI/src/com/android/systemui/CoreStartable.java
index c6a750a..0df8e02 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUI.java
+++ b/packages/SystemUI/src/com/android/systemui/CoreStartable.java
@@ -30,17 +30,18 @@
/**
* A top-level module of system UI code (sometimes called "system UI services" elsewhere in code).
- * Which SystemUI modules are loaded can be controlled via a config resource.
+ * Which CoreStartable modules are loaded can be controlled via a config resource.
*
* @see SystemUIApplication#startServicesIfNeeded()
*/
-public abstract class SystemUI implements Dumpable {
+public abstract class CoreStartable implements Dumpable {
protected final Context mContext;
- public SystemUI(Context context) {
+ public CoreStartable(Context context) {
mContext = context;
}
+ /** Main entry point for implementations. Called shortly after app startup. */
public abstract void start();
protected void onConfigurationChanged(Configuration newConfig) {
@@ -54,6 +55,7 @@
protected void onBootCompleted() {
}
+ /** TODO(b/205725937): Move this. SystemUIApplication? */
public static void overrideNotificationAppName(Context context, Notification.Builder n,
boolean system) {
final Bundle extras = new Bundle();
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index 744a77f..0b967b7 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -17,6 +17,8 @@
package com.android.systemui;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_ROW_EXPAND;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
@@ -32,6 +34,7 @@
import android.view.ViewConfiguration;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.policy.ScrollAdapter;
@@ -544,6 +547,7 @@
}
if (DEBUG) Log.d(TAG, "got mOldHeight: " + mOldHeight +
" mNaturalHeight: " + mNaturalHeight);
+ InteractionJankMonitor.getInstance().begin(v, CUJ_NOTIFICATION_SHADE_ROW_EXPAND);
return true;
}
@@ -608,6 +612,9 @@
}
mCallback.setUserLockedChild(scaledView, false);
mScaleAnimation.removeListener(this);
+ if (wasClosed) {
+ InteractionJankMonitor.getInstance().end(CUJ_NOTIFICATION_SHADE_ROW_EXPAND);
+ }
}
@Override
@@ -625,6 +632,9 @@
mCallback.setUserExpandedChild(mResizedView, nowExpanded);
mCallback.setUserLockedChild(mResizedView, false);
mScaler.setView(null);
+ if (wasClosed) {
+ InteractionJankMonitor.getInstance().end(CUJ_NOTIFICATION_SHADE_ROW_EXPAND);
+ }
}
mExpanding = false;
diff --git a/packages/SystemUI/src/com/android/systemui/LatencyTester.java b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
index d325b92..bc2a1ff 100644
--- a/packages/SystemUI/src/com/android/systemui/LatencyTester.java
+++ b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
@@ -35,7 +35,7 @@
* system that are used for testing the latency.
*/
@SysUISingleton
-public class LatencyTester extends SystemUI {
+public class LatencyTester extends CoreStartable {
private static final String
ACTION_FINGERPRINT_WAKE =
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index b2fae9d..e84024d 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -107,7 +107,7 @@
* for antialiasing and emulation purposes.
*/
@SysUISingleton
-public class ScreenDecorations extends SystemUI implements Tunable {
+public class ScreenDecorations extends CoreStartable implements Tunable {
private static final boolean DEBUG = false;
private static final String TAG = "ScreenDecorations";
diff --git a/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java b/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
index 0b997d0..d7da63b 100644
--- a/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
@@ -38,7 +38,7 @@
* @see SliceBroadcastRelay
*/
@SysUISingleton
-public class SliceBroadcastRelayHandler extends SystemUI {
+public class SliceBroadcastRelayHandler extends CoreStartable {
private static final String TAG = "SliceBroadcastRelay";
private static final boolean DEBUG = false;
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 3555e8d..4a0c30c 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -372,6 +372,11 @@
}
/**
+ * After dismissChild() and related animation finished, this function will be called.
+ */
+ protected void onDismissChildWithAnimationFinished() {}
+
+ /**
* @param view The view to be dismissed
* @param velocity The desired pixels/second speed at which the view should move
* @param useAccelerateInterpolator Should an accelerating Interpolator be used
@@ -436,6 +441,7 @@
Animator anim = getViewTranslationAnimator(animView, newPos, updateListener);
if (anim == null) {
+ onDismissChildWithAnimationFinished();
return;
}
if (useAccelerateInterpolator) {
@@ -481,6 +487,7 @@
if (!mDisableHwLayers) {
animView.setLayerType(View.LAYER_TYPE_NONE, null);
}
+ onDismissChildWithAnimationFinished();
}
});
@@ -505,6 +512,11 @@
// Do nothing
}
+ /**
+ * After snapChild() and related animation finished, this function will be called.
+ */
+ protected void onSnapChildWithAnimationFinished() {}
+
public void snapChild(final View animView, final float targetLeft, float velocity) {
final boolean canBeDismissed = mCallback.canChildBeDismissed(animView);
AnimatorUpdateListener updateListener = animation -> onTranslationUpdate(animView,
@@ -512,6 +524,7 @@
Animator anim = getViewTranslationAnimator(animView, targetLeft, updateListener);
if (anim == null) {
+ onSnapChildWithAnimationFinished();
return;
}
anim.addListener(new AnimatorListenerAdapter() {
@@ -529,6 +542,7 @@
updateSwipeProgressFromOffset(animView, canBeDismissed);
resetSwipeState();
}
+ onSnapChildWithAnimationFinished();
}
});
prepareSnapBackAnimation(animView, anim);
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 4fd2701..61d8c25 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -60,7 +60,7 @@
/**
* Hold a reference on the stuff we start.
*/
- private SystemUI[] mServices;
+ private CoreStartable[] mServices;
private boolean mServicesStarted;
private SystemUIAppComponentFactory.ContextAvailableCallback mContextAvailableCallback;
private GlobalRootComponent mRootComponent;
@@ -190,7 +190,7 @@
if (mServicesStarted) {
return;
}
- mServices = new SystemUI[services.length];
+ mServices = new CoreStartable[services.length];
if (!mBootCompleteCache.isBootComplete()) {
// check to see if maybe it was already completed long before we began
@@ -217,10 +217,10 @@
log.traceBegin(metricsPrefix + clsName);
long ti = System.currentTimeMillis();
try {
- SystemUI obj = mComponentHelper.resolveSystemUI(clsName);
+ CoreStartable obj = mComponentHelper.resolveCoreStartable(clsName);
if (obj == null) {
Constructor constructor = Class.forName(clsName).getConstructor(Context.class);
- obj = (SystemUI) constructor.newInstance(this);
+ obj = (CoreStartable) constructor.newInstance(this);
}
mServices[i] = obj;
} catch (ClassNotFoundException
@@ -265,7 +265,7 @@
}
}
- public SystemUI[] getServices() {
+ public CoreStartable[] getServices() {
return mServices;
}
diff --git a/packages/SystemUI/src/com/android/systemui/VendorServices.java b/packages/SystemUI/src/com/android/systemui/VendorServices.java
index 13d847b..139448c0 100644
--- a/packages/SystemUI/src/com/android/systemui/VendorServices.java
+++ b/packages/SystemUI/src/com/android/systemui/VendorServices.java
@@ -21,7 +21,7 @@
/**
* Placeholder for any vendor-specific services.
*/
-public class VendorServices extends SystemUI {
+public class VendorServices extends CoreStartable {
public VendorServices(Context context) {
super(context);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
index 294d1f4..20d6e32 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
@@ -49,7 +49,7 @@
import com.android.internal.R;
import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity;
import com.android.internal.util.ScreenshotHelper;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.recents.Recents;
import com.android.systemui.statusbar.CommandQueue;
@@ -69,7 +69,7 @@
* Class to register system actions with accessibility framework.
*/
@SysUISingleton
-public class SystemActions extends SystemUI {
+public class SystemActions extends CoreStartable {
private static final String TAG = "SystemActions";
/**
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index d34ac71..33ce206 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -35,7 +35,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.model.SysUiState;
@@ -54,7 +54,7 @@
* when {@code IStatusBar#requestWindowMagnificationConnection(boolean)} is called.
*/
@SysUISingleton
-public class WindowMagnification extends SystemUI implements WindowMagnifierCallback,
+public class WindowMagnification extends CoreStartable implements WindowMagnifierCallback,
CommandQueue.Callbacks {
private static final String TAG = "WindowMagnification";
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
index 36fef3e..8cb608f 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
@@ -106,6 +106,15 @@
void enableWindowMagnification(float scale, float centerX, float centerY,
@Nullable IRemoteMagnificationAnimationCallback animationCallback) {
sendAnimationCallback(false);
+ // Enable window magnification without animation immediately.
+ if (animationCallback == null) {
+ if (mState == STATE_ENABLING || mState == STATE_DISABLING) {
+ mValueAnimator.cancel();
+ }
+ mController.enableWindowMagnification(scale, centerX, centerY);
+ setState(STATE_ENABLED);
+ return;
+ }
mAnimationCallback = animationCallback;
setupEnableAnimationSpecs(scale, centerX, centerY);
if (mEndSpec.equals(mStartSpec)) {
@@ -173,6 +182,16 @@
void deleteWindowMagnification(
@Nullable IRemoteMagnificationAnimationCallback animationCallback) {
sendAnimationCallback(false);
+ // Delete window magnification without animation.
+ if (animationCallback == null) {
+ if (mState == STATE_ENABLING || mState == STATE_DISABLING) {
+ mValueAnimator.cancel();
+ }
+ mController.deleteWindowMagnification();
+ setState(STATE_DISABLED);
+ return;
+ }
+
mAnimationCallback = animationCallback;
if (mState == STATE_DISABLED || mState == STATE_DISABLING) {
if (mState == STATE_DISABLED) {
@@ -223,8 +242,7 @@
if (mEndAnimationCanceled) {
return;
}
- if (isReverse) {
- mController.deleteWindowMagnification();
+ if (Float.isNaN(mController.getScale())) {
setState(STATE_DISABLED);
} else {
setState(STATE_ENABLED);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 0893e89..b48def2 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -262,6 +262,9 @@
* Deletes the magnification window.
*/
void deleteWindowMagnification() {
+ if (!isWindowVisible()) {
+ return;
+ }
if (mMirrorSurface != null) {
mTransaction.remove(mMirrorSurface).apply();
mMirrorSurface = null;
@@ -690,7 +693,10 @@
}
/**
- * Enables window magnification with specified parameters.
+ * Enables window magnification with specified parameters. If the given scale is <strong>less
+ * than or equal to 1.0f<strong>, then
+ * {@link WindowMagnificationController#deleteWindowMagnification()} will be called instead to
+ * be consistent with the behavior of display magnification.
*
* @param scale the target scale, or {@link Float#NaN} to leave unchanged
* @param centerX the screen-relative X coordinate around which to center,
@@ -699,6 +705,11 @@
* or {@link Float#NaN} to leave unchanged.
*/
void enableWindowMagnification(float scale, float centerX, float centerY) {
+ if (Float.compare(scale, 1.0f) <= 0) {
+ deleteWindowMagnification();
+ return;
+ }
+
final float offsetX = Float.isNaN(centerX) ? 0
: centerX - mMagnificationFrame.exactCenterX();
final float offsetY = Float.isNaN(centerY) ? 0
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricUdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricUdfpsView.java
index 376368f..d80d9cc 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricUdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricUdfpsView.java
@@ -21,12 +21,19 @@
import android.content.Context;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.util.AttributeSet;
+import android.util.Log;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import com.android.systemui.R;
/**
* Manages the layout for under-display fingerprint sensors (UDFPS). Ensures that UI elements
* do not overlap with
*/
public class AuthBiometricUdfpsView extends AuthBiometricFingerprintView {
+ private static final String TAG = "AuthBiometricUdfpsView";
+
@Nullable private UdfpsDialogMeasureAdapter mMeasureAdapter;
public AuthBiometricUdfpsView(Context context) {
@@ -51,4 +58,23 @@
? mMeasureAdapter.onMeasureInternal(width, height, layoutParams)
: layoutParams;
}
+
+ @Override
+ void onLayoutInternal() {
+ super.onLayoutInternal();
+
+ // Move the UDFPS icon and indicator text if necessary. This probably only needs to happen
+ // for devices where the UDFPS sensor is too low.
+ // TODO(b/201510778): Update this logic to support cases where the sensor or text overlap
+ // the button bar area.
+ final int bottomSpacerHeight = mMeasureAdapter.getBottomSpacerHeight();
+ Log.w(TAG, "bottomSpacerHeight: " + bottomSpacerHeight);
+ if (bottomSpacerHeight < 0) {
+ FrameLayout iconFrame = findViewById(R.id.biometric_icon_frame);
+ iconFrame.setTranslationY(-bottomSpacerHeight);
+
+ TextView indicator = findViewById(R.id.indicator);
+ indicator.setTranslationY(-bottomSpacerHeight);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 7215736..29e5574 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -58,7 +58,7 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
import com.android.systemui.assist.ui.DisplayUtils;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
@@ -84,7 +84,7 @@
* {@link com.android.keyguard.KeyguardUpdateMonitor}
*/
@SysUISingleton
-public class AuthController extends SystemUI implements CommandQueue.Callbacks,
+public class AuthController extends CoreStartable implements CommandQueue.Callbacks,
AuthDialogCallback, DozeReceiver {
private static final String TAG = "AuthController";
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index ec17d4e..90a1e5e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -16,6 +16,8 @@
package com.android.systemui.biometrics
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.PointF
@@ -29,7 +31,9 @@
import com.android.systemui.R
import com.android.systemui.animation.Interpolators
import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.CircleReveal
+import com.android.systemui.statusbar.LiftReveal
import com.android.systemui.statusbar.LightRevealEffect
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.commandline.Command
@@ -41,13 +45,10 @@
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.ViewController
+import com.android.systemui.util.leak.RotationUtils
import java.io.PrintWriter
import javax.inject.Inject
import javax.inject.Provider
-import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.util.leak.RotationUtils
-
-private const val WAKE_AND_UNLOCK_FADE_DURATION = 180L
/***
* Controls the ripple effect that shows when authentication is successful.
@@ -141,11 +142,12 @@
private fun showUnlockedRipple() {
notificationShadeWindowController.setForcePluginOpen(true, this)
- val useCircleReveal = circleReveal != null && biometricUnlockController.isWakeAndUnlock
val lightRevealScrim = statusBar.lightRevealScrim
- if (useCircleReveal) {
- lightRevealScrim?.revealEffect = circleReveal!!
- startLightRevealScrimOnKeyguardFadingAway = true
+ if (statusBarStateController.isDozing || biometricUnlockController.isWakeAndUnlock) {
+ circleReveal?.let {
+ lightRevealScrim?.revealEffect = it
+ startLightRevealScrimOnKeyguardFadingAway = true
+ }
}
mView.startUnlockedRipple(
@@ -160,19 +162,29 @@
if (keyguardStateController.isKeyguardFadingAway) {
val lightRevealScrim = statusBar.lightRevealScrim
if (startLightRevealScrimOnKeyguardFadingAway && lightRevealScrim != null) {
- val revealAnimator = ValueAnimator.ofFloat(.1f, 1f).apply {
+ ValueAnimator.ofFloat(.1f, 1f).apply {
interpolator = Interpolators.LINEAR_OUT_SLOW_IN
duration = RIPPLE_ANIMATION_DURATION
startDelay = keyguardStateController.keyguardFadingAwayDelay
addUpdateListener { animator ->
if (lightRevealScrim.revealEffect != circleReveal) {
- // if the something else took over the reveal, let's do nothing.
+ // if something else took over the reveal, let's do nothing.
return@addUpdateListener
}
lightRevealScrim.revealAmount = animator.animatedValue as Float
}
+ addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator?) {
+ // Reset light reveal scrim to the default, so the StatusBar
+ // can handle any subsequent light reveal changes
+ // (ie: from dozing changes)
+ if (lightRevealScrim.revealEffect == circleReveal) {
+ lightRevealScrim.revealEffect = LiftReveal
+ }
+ }
+ })
+ start()
}
- revealAnimator.start()
startLightRevealScrimOnKeyguardFadingAway = false
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
index 6cc8acf..6afe420 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
@@ -23,6 +23,7 @@
import android.graphics.Rect;
import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.os.Build;
import android.util.Log;
import android.view.Surface;
import android.view.View;
@@ -30,6 +31,7 @@
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.WindowManager;
+import android.view.WindowMetrics;
import android.widget.FrameLayout;
import com.android.internal.annotations.VisibleForTesting;
@@ -41,16 +43,18 @@
*/
public class UdfpsDialogMeasureAdapter {
private static final String TAG = "UdfpsDialogMeasurementAdapter";
+ private static final boolean DEBUG = Build.IS_USERDEBUG || Build.IS_ENG;
@NonNull private final ViewGroup mView;
@NonNull private final FingerprintSensorPropertiesInternal mSensorProps;
-
@Nullable private WindowManager mWindowManager;
+ private int mBottomSpacerHeight;
public UdfpsDialogMeasureAdapter(
@NonNull ViewGroup view, @NonNull FingerprintSensorPropertiesInternal sensorProps) {
mView = view;
mSensorProps = sensorProps;
+ mWindowManager = mView.getContext().getSystemService(WindowManager.class);
}
@NonNull
@@ -75,19 +79,27 @@
}
}
+ /**
+ * @return the actual (and possibly negative) bottom spacer height. If negative, this indicates
+ * that the UDFPS sensor is too low. Our current xml and custom measurement logic is very hard
+ * too cleanly support this case. So, let's have the onLayout code translate the sensor location
+ * instead.
+ */
+ int getBottomSpacerHeight() {
+ return mBottomSpacerHeight;
+ }
+
@NonNull
private AuthDialog.LayoutParams onMeasureInternalPortrait(int width, int height) {
- // Get the height of the everything below the icon. Currently, that's the indicator and
- // button bar.
- final int textIndicatorHeight = getViewHeightPx(R.id.indicator);
- final int buttonBarHeight = getViewHeightPx(R.id.button_bar);
+ final WindowMetrics windowMetrics = mWindowManager.getMaximumWindowMetrics();
// Figure out where the bottom of the sensor anim should be.
- // Navbar + dialogMargin + buttonBar + textIndicator + spacerHeight = sensorDistFromBottom
+ final int textIndicatorHeight = getViewHeightPx(R.id.indicator);
+ final int buttonBarHeight = getViewHeightPx(R.id.button_bar);
final int dialogMargin = getDialogMarginPx();
- final int displayHeight = getWindowBounds().height();
- final Insets navbarInsets = getNavbarInsets();
- final int bottomSpacerHeight = calculateBottomSpacerHeightForPortrait(
+ final int displayHeight = getMaximumWindowBounds(windowMetrics).height();
+ final Insets navbarInsets = getNavbarInsets(windowMetrics);
+ mBottomSpacerHeight = calculateBottomSpacerHeightForPortrait(
mSensorProps, displayHeight, textIndicatorHeight, buttonBarHeight,
dialogMargin, navbarInsets.bottom);
@@ -123,9 +135,10 @@
MeasureSpec.EXACTLY));
} else if (child.getId() == R.id.space_below_icon) {
// Set the spacer height so the fingerprint icon is on the physical sensor area
+ final int clampedSpacerHeight = Math.max(mBottomSpacerHeight, 0);
child.measure(
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(bottomSpacerHeight, MeasureSpec.EXACTLY));
+ MeasureSpec.makeMeasureSpec(clampedSpacerHeight, MeasureSpec.EXACTLY));
} else {
child.measure(
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
@@ -142,6 +155,8 @@
@NonNull
private AuthDialog.LayoutParams onMeasureInternalLandscape(int width, int height) {
+ final WindowMetrics windowMetrics = mWindowManager.getMaximumWindowMetrics();
+
// Find the spacer height needed to vertically align the icon with the sensor.
final int titleHeight = getViewHeightPx(R.id.title);
final int subtitleHeight = getViewHeightPx(R.id.subtitle);
@@ -149,13 +164,14 @@
final int topSpacerHeight = getViewHeightPx(R.id.space_above_icon);
final int textIndicatorHeight = getViewHeightPx(R.id.indicator);
final int buttonBarHeight = getViewHeightPx(R.id.button_bar);
- final Insets navbarInsets = getNavbarInsets();
+
+ final Insets navbarInsets = getNavbarInsets(windowMetrics);
final int bottomSpacerHeight = calculateBottomSpacerHeightForLandscape(titleHeight,
subtitleHeight, descriptionHeight, topSpacerHeight, textIndicatorHeight,
buttonBarHeight, navbarInsets.bottom);
// Find the spacer width needed to horizontally align the icon with the sensor.
- final int displayWidth = getWindowBounds().width();
+ final int displayWidth = getMaximumWindowBounds(windowMetrics).width();
final int dialogMargin = getDialogMarginPx();
final int horizontalInset = navbarInsets.left + navbarInsets.right;
final int horizontalSpacerWidth = calculateHorizontalSpacerWidthForLandscape(
@@ -225,28 +241,15 @@
}
@NonNull
- private Insets getNavbarInsets() {
- final WindowManager windowManager = getWindowManager();
- return windowManager != null && windowManager.getCurrentWindowMetrics() != null
- ? windowManager.getCurrentWindowMetrics().getWindowInsets()
- .getInsets(WindowInsets.Type.navigationBars())
+ private static Insets getNavbarInsets(@Nullable WindowMetrics windowMetrics) {
+ return windowMetrics != null
+ ? windowMetrics.getWindowInsets().getInsets(WindowInsets.Type.navigationBars())
: Insets.NONE;
}
@NonNull
- private Rect getWindowBounds() {
- final WindowManager windowManager = getWindowManager();
- return windowManager != null && windowManager.getCurrentWindowMetrics() != null
- ? windowManager.getCurrentWindowMetrics().getBounds()
- : new Rect();
- }
-
- @Nullable
- private WindowManager getWindowManager() {
- if (mWindowManager == null) {
- mWindowManager = mView.getContext().getSystemService(WindowManager.class);
- }
- return mWindowManager;
+ private static Rect getMaximumWindowBounds(@Nullable WindowMetrics windowMetrics) {
+ return windowMetrics != null ? windowMetrics.getBounds() : new Rect();
}
/**
@@ -269,11 +272,13 @@
- dialogMarginPx
- navbarBottomInsetPx;
- Log.d(TAG, "Display height: " + displayHeightPx
- + ", Distance from bottom: " + sensorDistanceFromBottom
- + ", Bottom margin: " + dialogMarginPx
- + ", Navbar bottom inset: " + navbarBottomInsetPx
- + ", Bottom spacer height (portrait): " + spacerHeight);
+ if (DEBUG) {
+ Log.d(TAG, "Display height: " + displayHeightPx
+ + ", Distance from bottom: " + sensorDistanceFromBottom
+ + ", Bottom margin: " + dialogMarginPx
+ + ", Navbar bottom inset: " + navbarBottomInsetPx
+ + ", Bottom spacer height (portrait): " + spacerHeight);
+ }
return spacerHeight;
}
@@ -298,14 +303,16 @@
- dialogHeightBelowIcon
- navbarBottomInsetPx;
- Log.d(TAG, "Title height: " + titleHeightPx
- + ", Subtitle height: " + subtitleHeightPx
- + ", Description height: " + descriptionHeightPx
- + ", Top spacer height: " + topSpacerHeightPx
- + ", Text indicator height: " + textIndicatorHeightPx
- + ", Button bar height: " + buttonBarHeightPx
- + ", Navbar bottom inset: " + navbarBottomInsetPx
- + ", Bottom spacer height (landscape): " + bottomSpacerHeight);
+ if (DEBUG) {
+ Log.d(TAG, "Title height: " + titleHeightPx
+ + ", Subtitle height: " + subtitleHeightPx
+ + ", Description height: " + descriptionHeightPx
+ + ", Top spacer height: " + topSpacerHeightPx
+ + ", Text indicator height: " + textIndicatorHeightPx
+ + ", Button bar height: " + buttonBarHeightPx
+ + ", Navbar bottom inset: " + navbarBottomInsetPx
+ + ", Bottom spacer height (landscape): " + bottomSpacerHeight);
+ }
return bottomSpacerHeight;
}
@@ -328,11 +335,13 @@
- dialogMarginPx
- navbarHorizontalInsetPx;
- Log.d(TAG, "Display width: " + displayWidthPx
- + ", Distance from edge: " + sensorDistanceFromEdge
- + ", Dialog margin: " + dialogMarginPx
- + ", Navbar horizontal inset: " + navbarHorizontalInsetPx
- + ", Horizontal spacer width (landscape): " + horizontalPadding);
+ if (DEBUG) {
+ Log.d(TAG, "Display width: " + displayWidthPx
+ + ", Distance from edge: " + sensorDistanceFromEdge
+ + ", Dialog margin: " + dialogMarginPx
+ + ", Navbar horizontal inset: " + navbarHorizontalInsetPx
+ + ", Horizontal spacer width (landscape): " + horizontalPadding);
+ }
return horizontalPadding;
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentHelper.java b/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentHelper.java
index 1e7449c..f53221c 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentHelper.java
@@ -20,7 +20,7 @@
import android.app.Service;
import android.content.BroadcastReceiver;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
import com.android.systemui.recents.RecentsImplementation;
/**
@@ -37,7 +37,7 @@
Service resolveService(String className);
/** Turns a classname into an instance of the class or returns null. */
- SystemUI resolveSystemUI(String className);
+ CoreStartable resolveCoreStartable(String className);
/** Turns a classname into an instance of the class or returns null. */
BroadcastReceiver resolveBroadcastReceiver(String className);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentResolver.java b/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentResolver.java
index b41915b..fba8d35 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentResolver.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentResolver.java
@@ -20,7 +20,7 @@
import android.app.Service;
import android.content.BroadcastReceiver;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
import com.android.systemui.recents.RecentsImplementation;
import java.util.Map;
@@ -35,14 +35,14 @@
public class ContextComponentResolver implements ContextComponentHelper {
private final Map<Class<?>, Provider<Activity>> mActivityCreators;
private final Map<Class<?>, Provider<Service>> mServiceCreators;
- private final Map<Class<?>, Provider<SystemUI>> mSystemUICreators;
+ private final Map<Class<?>, Provider<CoreStartable>> mSystemUICreators;
private final Map<Class<?>, Provider<RecentsImplementation>> mRecentsCreators;
private final Map<Class<?>, Provider<BroadcastReceiver>> mBroadcastReceiverCreators;
@Inject
ContextComponentResolver(Map<Class<?>, Provider<Activity>> activityCreators,
Map<Class<?>, Provider<Service>> serviceCreators,
- Map<Class<?>, Provider<SystemUI>> systemUICreators,
+ Map<Class<?>, Provider<CoreStartable>> systemUICreators,
Map<Class<?>, Provider<RecentsImplementation>> recentsCreators,
Map<Class<?>, Provider<BroadcastReceiver>> broadcastReceiverCreators) {
mActivityCreators = activityCreators;
@@ -88,7 +88,7 @@
* Looks up the SystemUI class name to see if Dagger has an instance of it.
*/
@Override
- public SystemUI resolveSystemUI(String className) {
+ public CoreStartable resolveCoreStartable(String className) {
return resolve(className, mSystemUICreators);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index a5d4d80..e5c6ab5 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -16,10 +16,10 @@
package com.android.systemui.dagger;
+import com.android.systemui.CoreStartable;
import com.android.systemui.LatencyTester;
import com.android.systemui.ScreenDecorations;
import com.android.systemui.SliceBroadcastRelayHandler;
-import com.android.systemui.SystemUI;
import com.android.systemui.accessibility.SystemActions;
import com.android.systemui.accessibility.WindowMagnification;
import com.android.systemui.biometrics.AuthController;
@@ -63,145 +63,145 @@
@Binds
@IntoMap
@ClassKey(AuthController.class)
- public abstract SystemUI bindAuthController(AuthController service);
+ public abstract CoreStartable bindAuthController(AuthController service);
/** Inject into GarbageMonitor.Service. */
@Binds
@IntoMap
@ClassKey(GarbageMonitor.Service.class)
- public abstract SystemUI bindGarbageMonitorService(GarbageMonitor.Service sysui);
+ public abstract CoreStartable bindGarbageMonitorService(GarbageMonitor.Service sysui);
/** Inject into GlobalActionsComponent. */
@Binds
@IntoMap
@ClassKey(GlobalActionsComponent.class)
- public abstract SystemUI bindGlobalActionsComponent(GlobalActionsComponent sysui);
+ public abstract CoreStartable bindGlobalActionsComponent(GlobalActionsComponent sysui);
/** Inject into InstantAppNotifier. */
@Binds
@IntoMap
@ClassKey(InstantAppNotifier.class)
- public abstract SystemUI bindInstantAppNotifier(InstantAppNotifier sysui);
+ public abstract CoreStartable bindInstantAppNotifier(InstantAppNotifier sysui);
/** Inject into KeyguardViewMediator. */
@Binds
@IntoMap
@ClassKey(KeyguardViewMediator.class)
- public abstract SystemUI bindKeyguardViewMediator(KeyguardViewMediator sysui);
+ public abstract CoreStartable bindKeyguardViewMediator(KeyguardViewMediator sysui);
/** Inject into LatencyTests. */
@Binds
@IntoMap
@ClassKey(LatencyTester.class)
- public abstract SystemUI bindLatencyTester(LatencyTester sysui);
+ public abstract CoreStartable bindLatencyTester(LatencyTester sysui);
/** Inject into PowerUI. */
@Binds
@IntoMap
@ClassKey(PowerUI.class)
- public abstract SystemUI bindPowerUI(PowerUI sysui);
+ public abstract CoreStartable bindPowerUI(PowerUI sysui);
/** Inject into Recents. */
@Binds
@IntoMap
@ClassKey(Recents.class)
- public abstract SystemUI bindRecents(Recents sysui);
+ public abstract CoreStartable bindRecents(Recents sysui);
/** Inject into ScreenDecorations. */
@Binds
@IntoMap
@ClassKey(ScreenDecorations.class)
- public abstract SystemUI bindScreenDecorations(ScreenDecorations sysui);
+ public abstract CoreStartable bindScreenDecorations(ScreenDecorations sysui);
/** Inject into ShortcutKeyDispatcher. */
@Binds
@IntoMap
@ClassKey(ShortcutKeyDispatcher.class)
- public abstract SystemUI bindsShortcutKeyDispatcher(ShortcutKeyDispatcher sysui);
+ public abstract CoreStartable bindsShortcutKeyDispatcher(ShortcutKeyDispatcher sysui);
/** Inject into SliceBroadcastRelayHandler. */
@Binds
@IntoMap
@ClassKey(SliceBroadcastRelayHandler.class)
- public abstract SystemUI bindSliceBroadcastRelayHandler(SliceBroadcastRelayHandler sysui);
+ public abstract CoreStartable bindSliceBroadcastRelayHandler(SliceBroadcastRelayHandler sysui);
/** Inject into StatusBar. */
@Binds
@IntoMap
@ClassKey(StatusBar.class)
- public abstract SystemUI bindsStatusBar(StatusBar sysui);
+ public abstract CoreStartable bindsStatusBar(StatusBar sysui);
/** Inject into SystemActions. */
@Binds
@IntoMap
@ClassKey(SystemActions.class)
- public abstract SystemUI bindSystemActions(SystemActions sysui);
+ public abstract CoreStartable bindSystemActions(SystemActions sysui);
/** Inject into ThemeOverlayController. */
@Binds
@IntoMap
@ClassKey(ThemeOverlayController.class)
- public abstract SystemUI bindThemeOverlayController(ThemeOverlayController sysui);
+ public abstract CoreStartable bindThemeOverlayController(ThemeOverlayController sysui);
/** Inject into ToastUI. */
@Binds
@IntoMap
@ClassKey(ToastUI.class)
- public abstract SystemUI bindToastUI(ToastUI service);
+ public abstract CoreStartable bindToastUI(ToastUI service);
/** Inject into TvStatusBar. */
@Binds
@IntoMap
@ClassKey(TvStatusBar.class)
- public abstract SystemUI bindsTvStatusBar(TvStatusBar sysui);
+ public abstract CoreStartable bindsTvStatusBar(TvStatusBar sysui);
/** Inject into TvNotificationPanel. */
@Binds
@IntoMap
@ClassKey(TvNotificationPanel.class)
- public abstract SystemUI bindsTvNotificationPanel(TvNotificationPanel sysui);
+ public abstract CoreStartable bindsTvNotificationPanel(TvNotificationPanel sysui);
/** Inject into TvOngoingPrivacyChip. */
@Binds
@IntoMap
@ClassKey(TvOngoingPrivacyChip.class)
- public abstract SystemUI bindsTvOngoingPrivacyChip(TvOngoingPrivacyChip sysui);
+ public abstract CoreStartable bindsTvOngoingPrivacyChip(TvOngoingPrivacyChip sysui);
/** Inject into VolumeUI. */
@Binds
@IntoMap
@ClassKey(VolumeUI.class)
- public abstract SystemUI bindVolumeUI(VolumeUI sysui);
+ public abstract CoreStartable bindVolumeUI(VolumeUI sysui);
/** Inject into WindowMagnification. */
@Binds
@IntoMap
@ClassKey(WindowMagnification.class)
- public abstract SystemUI bindWindowMagnification(WindowMagnification sysui);
+ public abstract CoreStartable bindWindowMagnification(WindowMagnification sysui);
/** Inject into WMShell. */
@Binds
@IntoMap
@ClassKey(WMShell.class)
- public abstract SystemUI bindWMShell(WMShell sysui);
+ public abstract CoreStartable bindWMShell(WMShell sysui);
/** Inject into HomeSoundEffectController. */
@Binds
@IntoMap
@ClassKey(HomeSoundEffectController.class)
- public abstract SystemUI bindHomeSoundEffectController(HomeSoundEffectController sysui);
+ public abstract CoreStartable bindHomeSoundEffectController(HomeSoundEffectController sysui);
/** Inject into DreamOverlay. */
@Binds
@IntoMap
@ClassKey(DreamOverlayRegistrant.class)
- public abstract SystemUI bindDreamOverlayRegistrant(
+ public abstract CoreStartable bindDreamOverlayRegistrant(
DreamOverlayRegistrant dreamOverlayRegistrant);
/** Inject into AppWidgetOverlayPrimer. */
@Binds
@IntoMap
@ClassKey(AppWidgetOverlayPrimer.class)
- public abstract SystemUI bindAppWidgetOverlayPrimer(
+ public abstract CoreStartable bindAppWidgetOverlayPrimer(
AppWidgetOverlayPrimer appWidgetOverlayPrimer);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java
index 20c46da..994c630 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java
@@ -30,8 +30,8 @@
import android.service.dreams.IDreamManager;
import android.util.Log;
+import com.android.systemui.CoreStartable;
import com.android.systemui.R;
-import com.android.systemui.SystemUI;
import com.android.systemui.dagger.qualifiers.Main;
import javax.inject.Inject;
@@ -40,7 +40,7 @@
* {@link DreamOverlayRegistrant} is responsible for telling system server that SystemUI should be
* the designated dream overlay component.
*/
-public class DreamOverlayRegistrant extends SystemUI {
+public class DreamOverlayRegistrant extends CoreStartable {
private static final String TAG = "DreamOverlayRegistrant";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final IDreamManager mDreamManager;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetOverlayPrimer.java b/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetOverlayPrimer.java
index a0c7c29..563f707 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetOverlayPrimer.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/appwidgets/AppWidgetOverlayPrimer.java
@@ -23,8 +23,8 @@
import androidx.constraintlayout.widget.ConstraintSet;
+import com.android.systemui.CoreStartable;
import com.android.systemui.R;
-import com.android.systemui.SystemUI;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dreams.OverlayHostView;
@@ -36,7 +36,7 @@
* {@link AppWidgetOverlayPrimer} reads the configured App Widget Overlay from resources on start
* and populates them into the {@link DreamOverlayStateController}.
*/
-public class AppWidgetOverlayPrimer extends SystemUI {
+public class AppWidgetOverlayPrimer extends CoreStartable {
private final Resources mResources;
private final DreamOverlayStateController mDreamOverlayStateController;
private final AppWidgetOverlayComponent.Factory mComponentFactory;
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
index 6880674..5f7ad58 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
@@ -17,22 +17,11 @@
package com.android.systemui.flags;
import android.content.Context;
-import android.content.res.Resources;
import android.util.FeatureFlagUtils;
import android.util.Log;
-import android.util.SparseArray;
import android.widget.Toast;
-import androidx.annotation.BoolRes;
-
-import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
import javax.inject.Inject;
@@ -43,31 +32,13 @@
*/
@SysUISingleton
public class FeatureFlags {
- private final Resources mResources;
private final FlagReader mFlagReader;
private final Context mContext;
- private final Map<Integer, Flag<?>> mFlagMap = new HashMap<>();
- private final Map<Integer, List<Listener>> mListeners = new HashMap<>();
- private final SparseArray<Boolean> mCachedFlags = new SparseArray<>();
@Inject
- public FeatureFlags(@Main Resources resources, FlagReader flagReader, Context context) {
- mResources = resources;
+ public FeatureFlags(FlagReader flagReader, Context context) {
mFlagReader = flagReader;
mContext = context;
-
- flagReader.addListener(mListener);
- }
-
- private final FlagReader.Listener mListener = id -> {
- if (mListeners.containsKey(id) && mFlagMap.containsKey(id)) {
- mListeners.get(id).forEach(listener -> listener.onFlagChanged(mFlagMap.get(id)));
- }
- };
-
- @VisibleForTesting
- void addFlag(Flag<?> flag) {
- mFlagMap.put(flag.getId(), flag);
}
/**
@@ -75,32 +46,7 @@
* @return The value of the flag.
*/
public boolean isEnabled(BooleanFlag flag) {
- boolean def = flag.getDefault();
- if (flag.hasResourceOverride()) {
- try {
- def = isEnabledInOverlay(flag.getResourceOverride());
- } catch (Resources.NotFoundException e) {
- // no-op
- }
- }
- return mFlagReader.isEnabled(flag.getId(), def);
- }
-
- /**
- * @param flag The {@link IntFlag} of interest.
-
- /** Add a listener for a specific flag. */
- public void addFlagListener(Flag<?> flag, Listener listener) {
- mListeners.putIfAbsent(flag.getId(), new ArrayList<>());
- mListeners.get(flag.getId()).add(listener);
- mFlagMap.putIfAbsent(flag.getId(), flag);
- }
-
- /** Remove a listener for a specific flag. */
- public void removeFlagListener(Flag<?> flag, Listener listener) {
- if (mListeners.containsKey(flag.getId())) {
- mListeners.get(flag.getId()).remove(listener);
- }
+ return mFlagReader.isEnabled(flag);
}
public void assertLegacyPipelineEnabled() {
@@ -119,10 +65,6 @@
return false;
}
- public boolean isNewNotifPipelineEnabled() {
- return isEnabled(Flags.NEW_NOTIFICATION_PIPELINE);
- }
-
public boolean isNewNotifPipelineRenderingEnabled() {
return isEnabled(Flags.NEW_NOTIFICATION_PIPELINE_RENDERING);
}
@@ -209,20 +151,4 @@
public static boolean isProviderModelSettingEnabled(Context context) {
return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
}
-
- private boolean isEnabledInOverlay(@BoolRes int resId) {
- synchronized (mCachedFlags) {
- if (!mCachedFlags.contains(resId)) {
- mCachedFlags.put(resId, mResources.getBoolean(resId));
- }
-
- return mCachedFlags.get(resId);
- }
- }
-
- /** Simple interface for beinga alerted when a specific flag changes value. */
- public interface Listener {
- /** */
- void onFlagChanged(Flag<?> flag);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 5be1cdf..ee24f45 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -40,9 +40,6 @@
/***************************************/
// 100 - notification
- public static final BooleanFlag NEW_NOTIFICATION_PIPELINE =
- new BooleanFlag(100, true);
-
public static final BooleanFlag NEW_NOTIFICATION_PIPELINE_RENDERING =
new BooleanFlag(101, false);
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
index 86c8565..e746caf 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
@@ -19,7 +19,7 @@
import android.os.ServiceManager;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.GlobalActions;
import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
@@ -36,7 +36,8 @@
* Manages power menu plugins and communicates power menu actions to the StatusBar.
*/
@SysUISingleton
-public class GlobalActionsComponent extends SystemUI implements Callbacks, GlobalActionsManager {
+public class GlobalActionsComponent extends CoreStartable
+ implements Callbacks, GlobalActionsManager {
private final CommandQueue mCommandQueue;
private final ExtensionController mExtensionController;
diff --git a/packages/SystemUI/src/com/android/systemui/idle/DreamHelper.java b/packages/SystemUI/src/com/android/systemui/idle/DreamHelper.java
deleted file mode 100644
index fba1067..0000000
--- a/packages/SystemUI/src/com/android/systemui/idle/DreamHelper.java
+++ /dev/null
@@ -1,41 +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.systemui.idle;
-
-import android.content.Context;
-import android.service.dreams.Sandman;
-
-import javax.inject.Inject;
-
-/**
- * A helper class to the idle mode for requests related to the
- * {@link DreamService}.
- */
-public class DreamHelper {
- @Inject
- protected DreamHelper() {
- }
-
- /**
- * Requests the system to start dreaming.
- *
- * @param context The context within which the dream request is sent.
- */
- public void startDreaming(Context context) {
- Sandman.startDreamByUserRequest(context);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/idle/IdleHostViewController.java b/packages/SystemUI/src/com/android/systemui/idle/IdleHostViewController.java
index 6b212b7..624d01f 100644
--- a/packages/SystemUI/src/com/android/systemui/idle/IdleHostViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/idle/IdleHostViewController.java
@@ -24,22 +24,17 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
-import android.os.Looper;
import android.os.PowerManager;
import android.os.SystemClock;
import android.util.Log;
-import android.view.Choreographer;
import android.view.View;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shared.system.InputChannelCompat;
-import com.android.systemui.shared.system.InputMonitorCompat;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.ViewController;
-import com.android.systemui.util.concurrency.DelayableExecutor;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -52,13 +47,12 @@
* {@link IdleHostViewController} processes signals to control the lifecycle of the idle screen.
*/
public class IdleHostViewController extends ViewController<IdleHostView> {
- private static final String INPUT_MONITOR_IDENTIFIER = "IdleHostViewController";
private static final String TAG = "IdleHostViewController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@Retention(RetentionPolicy.RUNTIME)
@IntDef({STATE_IDLE_MODE_ENABLED, STATE_KEYGUARD_SHOWING, STATE_DOZING, STATE_DREAMING,
- STATE_LOW_LIGHT, STATE_IDLING, STATE_SHOULD_START_IDLING})
+ STATE_LOW_LIGHT})
public @interface State {}
// Set at construction to indicate idle mode is available.
@@ -76,76 +70,27 @@
// Set when the device is in a low light environment.
private static final int STATE_LOW_LIGHT = 1 << 4;
- // Set when the device is idling, which is either dozing or dreaming.
- private static final int STATE_IDLING = 1 << 5;
-
- // Set when the controller decides that the device should start idling (either dozing or
- // dreaming).
- private static final int STATE_SHOULD_START_IDLING = 1 << 6;
-
// The aggregate current state.
private int mState;
- private boolean mIdleModeActive;
- private boolean mLowLightModeActive;
private boolean mIsMonitoringLowLight;
private boolean mIsMonitoringDream;
- // Whether in a state waiting for dozing to complete before starting dreaming.
- private boolean mDozeToDreamLock = false;
-
- private final Context mContext;
-
- // Timeout to idle in milliseconds.
- private final int mIdleTimeout;
-
- // Factory for generating input listeners.
- private final InputMonitorFactory mInputMonitorFactory;
-
- // Delayable executor.
- private final DelayableExecutor mDelayableExecutor;
-
private final BroadcastDispatcher mBroadcastDispatcher;
private final PowerManager mPowerManager;
- // Runnable for canceling enabling idle.
- private Runnable mCancelEnableIdling;
-
// Keyguard state controller for monitoring keyguard show state.
private final KeyguardStateController mKeyguardStateController;
// Status bar state controller for monitoring when the device is dozing.
private final StatusBarStateController mStatusBarStateController;
- // Looper to use for monitoring input.
- private final Looper mLooper;
-
- // Choreographer to use for monitoring input.
- private final Choreographer mChoreographer;
-
- // Helper class for DreamService related requests.
- private final DreamHelper mDreamHelper;
-
- // Monitor for tracking touches for activity.
- private InputMonitorCompat mInputMonitor;
-
- // Input receiver of touch activities.
- private InputChannelCompat.InputEventReceiver mInputEventReceiver;
-
// Intent filter for receiving dream broadcasts.
private IntentFilter mDreamIntentFilter;
// Monitor for the current ambient light mode. Used to trigger / exit low-light mode.
private final AmbientLightModeMonitor mAmbientLightModeMonitor;
- // Delayed callback for starting idling.
- private final Runnable mEnableIdlingCallback = () -> {
- if (DEBUG) {
- Log.d(TAG, "time out, should start idling");
- }
- setState(STATE_SHOULD_START_IDLING, true);
- };
-
private final KeyguardStateController.Callback mKeyguardCallback =
new KeyguardStateController.Callback() {
@Override
@@ -202,29 +147,20 @@
@Inject
protected IdleHostViewController(
- Context context,
BroadcastDispatcher broadcastDispatcher,
PowerManager powerManager,
- IdleHostView view, InputMonitorFactory factory,
- @Main DelayableExecutor delayableExecutor,
+ IdleHostView view,
@Main Resources resources,
- @Main Looper looper,
@Named(IDLE_VIEW) Provider<View> idleViewProvider,
- Choreographer choreographer,
KeyguardStateController keyguardStateController,
StatusBarStateController statusBarStateController,
- DreamHelper dreamHelper,
AmbientLightModeMonitor ambientLightModeMonitor) {
super(view);
- mContext = context;
mBroadcastDispatcher = broadcastDispatcher;
mPowerManager = powerManager;
mIdleViewProvider = idleViewProvider;
mKeyguardStateController = keyguardStateController;
mStatusBarStateController = statusBarStateController;
- mLooper = looper;
- mChoreographer = choreographer;
- mDreamHelper = dreamHelper;
mAmbientLightModeMonitor = ambientLightModeMonitor;
mState = STATE_KEYGUARD_SHOWING;
@@ -236,13 +172,8 @@
setState(mState, true);
- mIdleTimeout = resources.getInteger(R.integer.config_idleModeTimeout);
- mInputMonitorFactory = factory;
- mDelayableExecutor = delayableExecutor;
-
if (DEBUG) {
- Log.d(TAG, "initial state:" + mState + " enabled:" + enabled
- + " timeout:" + mIdleTimeout);
+ Log.d(TAG, "initial state:" + mState + " enabled:" + enabled);
}
}
@@ -255,20 +186,6 @@
}
private void setState(@State int state, boolean active) {
- // If waiting for dozing to stop, ignore any state update until dozing is stopped.
- if (mDozeToDreamLock) {
- if (state == STATE_DOZING && !active) {
- if (DEBUG) {
- Log.d(TAG, "dozing stopped, now start dreaming");
- }
-
- mDozeToDreamLock = false;
- enableIdleMode(true);
- }
-
- return;
- }
-
final int oldState = mState;
if (active) {
@@ -281,53 +198,20 @@
return;
}
- // Updates STATE_IDLING.
- final boolean isIdling = getState(STATE_DOZING) || getState(STATE_DREAMING);
- if (isIdling) {
- mState |= STATE_IDLING;
- } else {
- mState &= ~STATE_IDLING;
- }
-
- // Updates STATE_SHOULD_START_IDLING.
- final boolean stoppedIdling = stoppedIdling(oldState);
- if (stoppedIdling) {
- mState &= ~STATE_SHOULD_START_IDLING;
- } else if (shouldStartIdling(oldState)) {
- mState |= STATE_SHOULD_START_IDLING;
- }
-
if (DEBUG) {
Log.d(TAG, "set " + getStateName(state) + " to " + active);
logCurrentState();
}
- final boolean wasLowLight = getState(STATE_LOW_LIGHT, oldState);
- final boolean isLowLight = getState(STATE_LOW_LIGHT);
- final boolean wasIdling = getState(STATE_IDLING, oldState);
-
- // When the device is idling and no longer in low light, wake up from dozing, wait till
- // done, and start dreaming.
- if (wasLowLight && !isLowLight && wasIdling && isIdling) {
- if (DEBUG) {
- Log.d(TAG, "idling and no longer in low light, stop dozing");
- }
-
- mDozeToDreamLock = true;
-
- enableLowLightMode(false);
- return;
- }
-
final boolean inCommunalMode = getState(STATE_IDLE_MODE_ENABLED)
&& getState(STATE_KEYGUARD_SHOWING);
enableDreamMonitoring(inCommunalMode);
enableLowLightMonitoring(inCommunalMode);
- enableIdleMonitoring(inCommunalMode && !getState(STATE_IDLING));
- enableIdleMode(inCommunalMode && !getState(STATE_LOW_LIGHT)
- && getState(STATE_SHOULD_START_IDLING));
- enableLowLightMode(inCommunalMode && !stoppedIdling && getState(STATE_LOW_LIGHT));
+
+ if (state == STATE_LOW_LIGHT) {
+ enableLowLightMode(inCommunalMode && active);
+ }
}
private void enableDreamMonitoring(boolean enable) {
@@ -354,64 +238,6 @@
}
}
- private void enableIdleMonitoring(boolean enable) {
- if (enable && mInputMonitor == null && mInputEventReceiver == null) {
- if (DEBUG) {
- Log.d(TAG, "enable idle monitoring");
- }
- // Set initial timeout to idle.
- mCancelEnableIdling = mDelayableExecutor.executeDelayed(mEnableIdlingCallback,
- mIdleTimeout);
-
- // Monitor - any input should reset timer
- mInputMonitor = mInputMonitorFactory.getInputMonitor(INPUT_MONITOR_IDENTIFIER);
- mInputEventReceiver = mInputMonitor.getInputReceiver(mLooper, mChoreographer,
- v -> {
- if (DEBUG) {
- Log.d(TAG, "touch detected, resetting timeout");
- }
- // When input is received, reset timeout.
- if (mCancelEnableIdling != null) {
- mCancelEnableIdling.run();
- mCancelEnableIdling = null;
- }
- mCancelEnableIdling = mDelayableExecutor.executeDelayed(
- mEnableIdlingCallback, mIdleTimeout);
- });
- } else if (!enable && mInputMonitor != null && mInputEventReceiver != null) {
- if (DEBUG) {
- Log.d(TAG, "disable idle monitoring");
- }
- // Clean up idle callback and touch monitoring.
- if (mCancelEnableIdling != null) {
- mCancelEnableIdling.run();
- mCancelEnableIdling = null;
- }
-
- mInputEventReceiver.dispose();
- mInputMonitor.dispose();
- mInputEventReceiver = null;
- mInputMonitor = null;
- }
- }
-
- private void enableIdleMode(boolean enable) {
- if (mIdleModeActive == enable) {
- return;
- }
-
- if (DEBUG) {
- Log.d(TAG, (enable ? "enable" : "disable") + " idle mode");
- }
-
- mIdleModeActive = enable;
-
- if (mIdleModeActive) {
- // Start dream.
- mDreamHelper.startDreaming(mContext);
- }
- }
-
private void enableLowLightMonitoring(boolean enable) {
if (enable == mIsMonitoringLowLight) {
return;
@@ -429,13 +255,11 @@
}
private void enableLowLightMode(boolean enable) {
- if (mLowLightModeActive == enable) {
+ if (enable == getState(STATE_DOZING)) {
return;
}
- mLowLightModeActive = enable;
-
- if (mLowLightModeActive) {
+ if (enable) {
if (DEBUG) Log.d(TAG, "enter low light, start dozing");
mPowerManager.goToSleep(
@@ -464,19 +288,6 @@
mStatusBarStateController.removeCallback(mStatusBarCallback);
}
- // Returns whether the device just stopped idling by comparing the previous state with the
- // current one.
- private boolean stoppedIdling(int oldState) {
- // The device stopped idling if it's no longer dreaming or dozing.
- return !getState(STATE_DOZING) && !getState(STATE_DREAMING)
- && (getState(STATE_DOZING, oldState) || getState(STATE_DREAMING, oldState));
- }
-
- private boolean shouldStartIdling(int oldState) {
- // Should start idling immediately if the device went in low light environment.
- return !getState(STATE_LOW_LIGHT, oldState) && getState(STATE_LOW_LIGHT);
- }
-
private String getStateName(@State int state) {
switch (state) {
case STATE_IDLE_MODE_ENABLED:
@@ -489,10 +300,6 @@
return "STATE_DREAMING";
case STATE_LOW_LIGHT:
return "STATE_LOW_LIGHT";
- case STATE_IDLING:
- return "STATE_IDLING";
- case STATE_SHOULD_START_IDLING:
- return "STATE_SHOULD_START_IDLING";
default:
return "STATE_UNKNOWN";
}
@@ -517,8 +324,6 @@
+ "\t" + getStateLog(STATE_DOZING) + "\n"
+ "\t" + getStateLog(STATE_DREAMING) + "\n"
+ "\t" + getStateLog(STATE_LOW_LIGHT) + "\n"
- + "\t" + getStateLog(STATE_IDLING) + "\n"
- + "\t" + getStateLog(STATE_SHOULD_START_IDLING) + "\n"
+ "}");
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
index 42f455a..1c0b104 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
@@ -49,9 +49,9 @@
import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.systemui.CoreStartable;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.SystemUI;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -60,7 +60,7 @@
import java.util.List;
import java.util.Set;
-public class KeyboardUI extends SystemUI implements InputManager.OnTabletModeChangedListener {
+public class KeyboardUI extends CoreStartable implements InputManager.OnTabletModeChangedListener {
private static final String TAG = "KeyboardUI";
private static final boolean DEBUG = false;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index 2cc564b..a6455e6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -30,8 +30,8 @@
import com.android.keyguard.KeyguardViewController
import com.android.systemui.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController
import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController
import com.android.systemui.statusbar.policy.KeyguardStateController
import dagger.Lazy
import javax.inject.Inject
@@ -88,7 +88,8 @@
class KeyguardUnlockAnimationController @Inject constructor(
context: Context,
private val keyguardStateController: KeyguardStateController,
- private val keyguardViewMediator: Lazy<KeyguardViewMediator>,
+ private val
+ keyguardViewMediator: Lazy<KeyguardViewMediator>,
private val keyguardViewController: KeyguardViewController,
private val smartspaceTransitionController: SmartspaceTransitionController,
private val featureFlags: FeatureFlags
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 7bb7cc4..e97e762 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -99,8 +99,8 @@
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.KeyguardViewController;
import com.android.keyguard.ViewMediatorCallback;
+import com.android.systemui.CoreStartable;
import com.android.systemui.Dumpable;
-import com.android.systemui.SystemUI;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollector;
@@ -176,7 +176,7 @@
* directly to the keyguard UI is posted to a {@link android.os.Handler} to ensure it is taken on the UI
* thread of the keyguard.
*/
-public class KeyguardViewMediator extends SystemUI implements Dumpable,
+public class KeyguardViewMediator extends CoreStartable implements Dumpable,
StatusBarStateController.StateListener {
private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000;
private static final long KEYGUARD_DONE_PENDING_TIMEOUT_MS = 3000;
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index df950d7..02beff9 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -147,7 +147,7 @@
public static LogcatEchoTracker provideLogcatEchoTracker(
ContentResolver contentResolver,
@Main Looper looper) {
- if (Build.IS_DEBUGGABLE) {
+ if (Build.isDebuggable()) {
return LogcatEchoTrackerDebug.create(contentResolver, looper);
} else {
return new LogcatEchoTrackerProd();
diff --git a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
index 553b6d8..ae5f9b6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
@@ -37,7 +37,7 @@
import android.provider.MediaStore;
import android.util.Log;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -48,7 +48,7 @@
* Service that offers to play ringtones by {@link Uri}, since our process has
* {@link android.Manifest.permission#READ_EXTERNAL_STORAGE}.
*/
-public class RingtonePlayer extends SystemUI {
+public class RingtonePlayer extends CoreStartable {
private static final String TAG = "RingtonePlayer";
private static final boolean LOGD = false;
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 42dd886..1981269 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -79,6 +79,7 @@
private final DialogLaunchAnimator mDialogLaunchAnimator;
private final List<MediaDevice> mGroupMediaDevices = new CopyOnWriteArrayList<>();
private final boolean mAboveStatusbar;
+ private final boolean mVolumeAdjustmentForRemoteGroupSessions;
private final NotificationEntryManager mNotificationEntryManager;
@VisibleForTesting
final List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>();
@@ -111,6 +112,8 @@
mMetricLogger = new MediaOutputMetricLogger(mContext, mPackageName);
mUiEventLogger = uiEventLogger;
mDialogLaunchAnimator = dialogLaunchAnimator;
+ mVolumeAdjustmentForRemoteGroupSessions = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_volumeAdjustmentForRemoteGroupSessions);
}
void start(@NonNull Callback cb) {
@@ -477,7 +480,9 @@
}
boolean isVolumeControlEnabled(@NonNull MediaDevice device) {
- return !isActiveRemoteDevice(device);
+ // TODO(b/202500642): Also enable volume control for remote non-group sessions.
+ return !isActiveRemoteDevice(device)
+ || mVolumeAdjustmentForRemoteGroupSessions;
}
private final MediaController.Callback mCb = new MediaController.Callback() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/systemsounds/HomeSoundEffectController.java b/packages/SystemUI/src/com/android/systemui/media/systemsounds/HomeSoundEffectController.java
index 31e4939..d60172a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/systemsounds/HomeSoundEffectController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/systemsounds/HomeSoundEffectController.java
@@ -25,8 +25,8 @@
import android.media.AudioManager;
import android.util.Slog;
+import com.android.systemui.CoreStartable;
import com.android.systemui.R;
-import com.android.systemui.SystemUI;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
@@ -40,7 +40,7 @@
* documented at {@link #handleTaskStackChanged} apply.
*/
@SysUISingleton
-public class HomeSoundEffectController extends SystemUI {
+public class HomeSoundEffectController extends CoreStartable {
private static final String TAG = "HomeSoundEffectController";
private final AudioManager mAudioManager;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index c351d13..0e6e8a4 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -24,9 +24,6 @@
import static android.app.StatusBarManager.WindowVisibleState;
import static android.app.StatusBarManager.windowStateToString;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
-import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
-import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE;
-import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.containsType;
@@ -613,8 +610,6 @@
mDeviceProvisionedController.addCallback(mUserSetupListener);
mNotificationShadeDepthController.addListener(mDepthListener);
- updateAccessibilityButtonModeIfNeeded();
-
return barView;
}
@@ -1405,34 +1400,6 @@
updateSystemUiStateFlags(a11yFlags);
}
- private void updateAccessibilityButtonModeIfNeeded() {
- final int mode = Settings.Secure.getIntForUser(mContentResolver,
- Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
- ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
-
- // ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU is compatible under gestural or non-gestural
- // mode, so we don't need to update it.
- if (mode == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
- return;
- }
-
- // ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR is incompatible under gestural mode. Need to
- // force update to ACCESSIBILITY_BUTTON_MODE_GESTURE.
- if (QuickStepContract.isGesturalMode(mNavBarMode)
- && mode == ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR) {
- Settings.Secure.putIntForUser(mContentResolver,
- Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_GESTURE,
- UserHandle.USER_CURRENT);
- // ACCESSIBILITY_BUTTON_MODE_GESTURE is incompatible under non gestural mode. Need to
- // force update to ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR.
- } else if (!QuickStepContract.isGesturalMode(mNavBarMode)
- && mode == ACCESSIBILITY_BUTTON_MODE_GESTURE) {
- Settings.Secure.putIntForUser(mContentResolver,
- Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
- ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
- }
- }
-
public void updateSystemUiStateFlags(int a11yFlags) {
if (a11yFlags < 0) {
a11yFlags = mNavigationBarA11yHelper.getA11yButtonState();
@@ -1550,6 +1517,9 @@
@Override
public void onNavigationModeChanged(int mode) {
mNavBarMode = mode;
+ // update assistant entry points on system navigation radio button click
+ updateAssistantEntrypoints();
+
if (!QuickStepContract.isGesturalMode(mode)) {
// Reset the override alpha
if (getBarTransitions() != null) {
@@ -1557,7 +1527,6 @@
}
}
updateScreenPinningGestures();
- updateAccessibilityButtonModeIfNeeded();
if (!canShowSecondaryHandle()) {
resetSecondaryHandle();
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 4959c7d..3dc79c4 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -16,10 +16,14 @@
package com.android.systemui.navigationbar;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.systemui.shared.recents.utilities.Utilities.isTablet;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
@@ -27,6 +31,8 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
import android.util.Log;
import android.util.SparseArray;
import android.view.Display;
@@ -46,6 +52,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.model.SysUiState;
import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
import com.android.systemui.statusbar.phone.AutoHideController;
@@ -142,6 +149,8 @@
}
final int oldMode = mNavMode;
mNavMode = mode;
+ updateAccessibilityButtonModeIfNeeded();
+
mHandler.post(() -> {
// create/destroy nav bar based on nav mode only in unfolded state
if (oldMode != mNavMode) {
@@ -157,6 +166,35 @@
});
}
+ private void updateAccessibilityButtonModeIfNeeded() {
+ ContentResolver contentResolver = mContext.getContentResolver();
+ final int mode = Settings.Secure.getIntForUser(contentResolver,
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
+
+ // ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU is compatible under gestural or non-gestural
+ // mode, so we don't need to update it.
+ if (mode == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
+ return;
+ }
+
+ // ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR is incompatible under gestural mode. Need to
+ // force update to ACCESSIBILITY_BUTTON_MODE_GESTURE.
+ if (QuickStepContract.isGesturalMode(mNavMode)
+ && mode == ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR) {
+ Settings.Secure.putIntForUser(contentResolver,
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_GESTURE,
+ UserHandle.USER_CURRENT);
+ // ACCESSIBILITY_BUTTON_MODE_GESTURE is incompatible under non gestural mode. Need to
+ // force update to ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR.
+ } else if (!QuickStepContract.isGesturalMode(mNavMode)
+ && mode == ACCESSIBILITY_BUTTON_MODE_GESTURE) {
+ Settings.Secure.putIntForUser(contentResolver,
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
+ }
+ }
+
/** @see #initializeTaskbarIfNecessary() */
private boolean updateNavbarForTaskbar() {
boolean taskbarShown = initializeTaskbarIfNecessary();
@@ -222,6 +260,8 @@
*/
public void createNavigationBars(final boolean includeDefaultDisplay,
RegisterStatusBarResult result) {
+ updateAccessibilityButtonModeIfNeeded();
+
// Don't need to create nav bar on the default display if we initialize TaskBar.
final boolean shouldCreateDefaultNavbar = includeDefaultDisplay
&& !initializeTaskbarIfNecessary();
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index 4e1e1cd..b483e59 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -58,9 +58,9 @@
import com.android.settingslib.Utils;
import com.android.settingslib.fuelgauge.BatterySaverUtils;
import com.android.settingslib.utils.PowerUtil;
+import com.android.systemui.CoreStartable;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.SystemUI;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.phone.SystemUIDialog;
@@ -247,7 +247,7 @@
.setContentText(mContext.getString(R.string.invalid_charger_text))
.setColor(mContext.getColor(
com.android.internal.R.color.system_notification_accent_color));
- SystemUI.overrideNotificationAppName(mContext, nb, false);
+ CoreStartable.overrideNotificationAppName(mContext, nb, false);
final Notification n = nb.build();
mNoMan.cancelAsUser(TAG_BATTERY, SystemMessage.NOTE_POWER_LOW, UserHandle.ALL);
mNoMan.notifyAsUser(TAG_BATTERY, SystemMessage.NOTE_BAD_CHARGER, n, UserHandle.ALL);
@@ -298,7 +298,7 @@
}
nb.setOnlyAlertOnce(!mPlaySound);
mPlaySound = false;
- SystemUI.overrideNotificationAppName(mContext, nb, false);
+ CoreStartable.overrideNotificationAppName(mContext, nb, false);
final Notification n = nb.build();
mNoMan.cancelAsUser(TAG_BATTERY, SystemMessage.NOTE_BAD_CHARGER, UserHandle.ALL);
mNoMan.notifyAsUser(TAG_BATTERY, SystemMessage.NOTE_POWER_LOW, n, UserHandle.ALL);
@@ -320,7 +320,7 @@
mContext.getString(R.string.no_auto_saver_action),
pendingBroadcast(ACTION_AUTO_SAVER_NO_THANKS));
- SystemUI.overrideNotificationAppName(mContext, nb, false);
+ CoreStartable.overrideNotificationAppName(mContext, nb, false);
final Notification n = nb.build();
mNoMan.notifyAsUser(
@@ -397,7 +397,7 @@
.setDeleteIntent(pendingBroadcast(ACTION_DISMISSED_TEMP_WARNING))
.setColor(Utils.getColorAttrDefaultColor(mContext,
android.R.attr.colorError));
- SystemUI.overrideNotificationAppName(mContext, nb, false);
+ CoreStartable.overrideNotificationAppName(mContext, nb, false);
final Notification n = nb.build();
mNoMan.notifyAsUser(TAG_TEMPERATURE, SystemMessage.NOTE_HIGH_TEMP, n, UserHandle.ALL);
}
@@ -484,7 +484,7 @@
pendingBroadcast(ACTION_DISMISSED_THERMAL_SHUTDOWN_WARNING))
.setColor(Utils.getColorAttrDefaultColor(mContext,
android.R.attr.colorError));
- SystemUI.overrideNotificationAppName(mContext, nb, false);
+ CoreStartable.overrideNotificationAppName(mContext, nb, false);
final Notification n = nb.build();
mNoMan.notifyAsUser(
TAG_TEMPERATURE, SystemMessage.NOTE_THERMAL_SHUTDOWN, n, UserHandle.ALL);
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 6252654..37a0f59 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -42,9 +42,9 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.fuelgauge.Estimate;
import com.android.settingslib.utils.ThreadUtils;
+import com.android.systemui.CoreStartable;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.SystemUI;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.CommandQueue;
@@ -62,7 +62,7 @@
import dagger.Lazy;
@SysUISingleton
-public class PowerUI extends SystemUI implements CommandQueue.Callbacks {
+public class PowerUI extends CoreStartable implements CommandQueue.Callbacks {
static final String TAG = "PowerUI";
static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java b/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java
index 8750976..5510eb1 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java
+++ b/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java
@@ -45,8 +45,8 @@
import androidx.annotation.NonNull;
+import com.android.systemui.CoreStartable;
import com.android.systemui.R;
-import com.android.systemui.SystemUI;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.privacy.PrivacyChipBuilder;
import com.android.systemui.privacy.PrivacyItem;
@@ -67,7 +67,7 @@
* recording audio, accessing the camera or accessing the location.
*/
@SysUISingleton
-public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemController.Callback,
+public class TvOngoingPrivacyChip extends CoreStartable implements PrivacyItemController.Callback,
PrivacyChipDrawable.PrivacyChipDrawableListener {
private static final String TAG = "TvOngoingPrivacyChip";
private static final boolean DEBUG = false;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 6f6dd9c..b7a44a4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -21,7 +21,7 @@
import android.content.res.Configuration;
import android.provider.Settings;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
import com.android.systemui.statusbar.CommandQueue;
import java.io.FileDescriptor;
@@ -30,7 +30,7 @@
/**
* A proxy to a Recents implementation.
*/
-public class Recents extends SystemUI implements CommandQueue.Callbacks {
+public class Recents extends CoreStartable implements CommandQueue.Callbacks {
private final RecentsImplementation mImpl;
private final CommandQueue mCommandQueue;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
index 58a54f6..28bdd53 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
@@ -30,8 +30,8 @@
import android.view.WindowManager;
import com.android.internal.messages.nano.SystemMessageProto;
+import com.android.systemui.CoreStartable;
import com.android.systemui.R;
-import com.android.systemui.SystemUI;
import com.android.systemui.util.NotificationChannels;
import javax.inject.Inject;
@@ -86,7 +86,7 @@
b.setContentIntent(pendingIntent);
}
- SystemUI.overrideNotificationAppName(mContext, b, true);
+ CoreStartable.overrideNotificationAppName(mContext, b, true);
Notification n = new Notification.BigTextStyle(b)
.bigText(errorMsg)
diff --git a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
index 9933438..10aa12b 100644
--- a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
@@ -24,7 +24,7 @@
import android.view.WindowManagerGlobal;
import com.android.internal.policy.DividerSnapAlgorithm;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.wm.shell.legacysplitscreen.DividerView;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
@@ -37,7 +37,7 @@
* Dispatches shortcut to System UI components
*/
@SysUISingleton
-public class ShortcutKeyDispatcher extends SystemUI
+public class ShortcutKeyDispatcher extends CoreStartable
implements ShortcutKeyServiceProxy.Callbacks {
private static final String TAG = "ShortcutKeyDispatcher";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index e189b26..75b3592 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -596,7 +596,7 @@
@Override
public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
- boolean showImeSwitcher, boolean isMultiClientImeEnabled) {
+ boolean showImeSwitcher) {
synchronized (mLock) {
mHandler.removeMessages(MSG_SHOW_IME_BUTTON);
SomeArgs args = SomeArgs.obtain();
@@ -604,7 +604,6 @@
args.argi2 = vis;
args.argi3 = backDisposition;
args.argi4 = showImeSwitcher ? 1 : 0;
- args.argi5 = isMultiClientImeEnabled ? 1 : 0;
args.arg1 = token;
Message m = mHandler.obtainMessage(MSG_SHOW_IME_BUTTON, args);
m.sendToTarget();
@@ -993,10 +992,10 @@
}
private void handleShowImeButton(int displayId, IBinder token, int vis, int backDisposition,
- boolean showImeSwitcher, boolean isMultiClientImeEnabled) {
+ boolean showImeSwitcher) {
if (displayId == INVALID_DISPLAY) return;
- if (!isMultiClientImeEnabled && mLastUpdatedImeDisplayId != displayId
+ if (mLastUpdatedImeDisplayId != displayId
&& mLastUpdatedImeDisplayId != INVALID_DISPLAY) {
// Set previous NavBar's IME window status as invisible when IME
// window switched to another display for single-session IME case.
@@ -1206,8 +1205,7 @@
args = (SomeArgs) msg.obj;
handleShowImeButton(args.argi1 /* displayId */, (IBinder) args.arg1 /* token */,
args.argi2 /* vis */, args.argi3 /* backDisposition */,
- args.argi4 != 0 /* showImeSwitcher */,
- args.argi5 != 0 /* isMultiClientImeEnabled */);
+ args.argi4 != 0 /* showImeSwitcher */);
break;
case MSG_SHOW_RECENT_APPS:
for (int i = 0; i < mCallbacks.size(); i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 74ebfe5..1c00887 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -818,7 +818,7 @@
}
private void showTryFingerprintMsg(int msgId, String a11yString) {
- if (mKeyguardUpdateMonitor.isUdfpsAvailable()) {
+ if (mKeyguardUpdateMonitor.isUdfpsSupported()) {
// if udfps available, there will always be a tappable affordance to unlock
// For example, the lock icon
if (mKeyguardBypassController.getUserHasDeviceEntryIntent()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 464b2b6..ff3e97a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -35,7 +35,7 @@
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.inflation.LowPriorityInflationHelper;
+import com.android.systemui.statusbar.notification.collection.legacy.LowPriorityInflationHelper;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index cbb3aba..da2b85e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -173,7 +173,7 @@
}
// Record the to-be mState and mLastState
- recordHistoricalState(state, mState);
+ recordHistoricalState(state /* newState */, mState /* lastState */, false);
// b/139259891
if (mState == StatusBarState.SHADE && state == StatusBarState.SHADE_LOCKED) {
@@ -206,6 +206,7 @@
@Override
public void setUpcomingState(int nextState) {
mUpcomingState = nextState;
+ recordHistoricalState(mUpcomingState /* newState */, mState /* lastState */, true);
}
@Override
@@ -505,31 +506,36 @@
}
}
- private void recordHistoricalState(int currentState, int lastState) {
+ private void recordHistoricalState(int newState, int lastState, boolean upcoming) {
mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE;
HistoricalState state = mHistoricalRecords[mHistoryIndex];
- state.mState = currentState;
+ state.mNewState = newState;
state.mLastState = lastState;
state.mTimestamp = System.currentTimeMillis();
+ state.mUpcoming = upcoming;
}
/**
* For keeping track of our previous state to help with debugging
*/
private static class HistoricalState {
- int mState;
+ int mNewState;
int mLastState;
long mTimestamp;
+ boolean mUpcoming;
@Override
public String toString() {
if (mTimestamp != 0) {
StringBuilder sb = new StringBuilder();
- sb.append("state=").append(mState)
- .append(" (").append(describe(mState)).append(")");
- sb.append("lastState=").append(mLastState).append(" (").append(describe(mLastState))
+ if (mUpcoming) {
+ sb.append("upcoming-");
+ }
+ sb.append("newState=").append(mNewState)
+ .append("(").append(describe(mNewState)).append(")");
+ sb.append(" lastState=").append(mLastState).append("(").append(describe(mLastState))
.append(")");
- sb.append("timestamp=")
+ sb.append(" timestamp=")
.append(DateFormat.format("MM-dd HH:mm:ss", mTimestamp));
return sb.toString();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index aa86daa..d5cba72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -55,7 +55,7 @@
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
-import com.android.systemui.statusbar.notification.collection.inflation.LowPriorityInflationHelper;
+import com.android.systemui.statusbar.notification.collection.legacy.LowPriorityInflationHelper;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
@@ -73,8 +73,6 @@
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.statusbar.window.StatusBarWindowController;
-import com.android.systemui.statusbar.window.StatusBarWindowModule;
-import com.android.systemui.statusbar.window.StatusBarWindowView;
import com.android.systemui.tracing.ProtoTracer;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.time.SystemClock;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
index e58ea7b..60d1317 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
@@ -51,9 +51,9 @@
import com.android.internal.messages.nano.SystemMessageProto;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.systemui.CoreStartable;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.SystemUI;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.statusbar.CommandQueue;
@@ -71,7 +71,7 @@
* splitted screen.
*/
@SysUISingleton
-public class InstantAppNotifier extends SystemUI
+public class InstantAppNotifier extends CoreStartable
implements CommandQueue.Callbacks, KeyguardStateController.Callback {
private static final String TAG = "InstantAppNotifier";
public static final int NUM_TASKS_FOR_INSTANT_APP_INFO = 5;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 82f35a8..2437415 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -638,7 +638,7 @@
// Construct the expanded view.
if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
- mNotificationRowBinderLazy.get().inflateViews(entry, mInflationCallback);
+ mNotificationRowBinderLazy.get().inflateViews(entry, null, mInflationCallback);
}
mPendingNotifications.put(key, entry);
@@ -695,7 +695,7 @@
}
if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
- mNotificationRowBinderLazy.get().inflateViews(entry, mInflationCallback);
+ mNotificationRowBinderLazy.get().inflateViews(entry, null, mInflationCallback);
}
updateNotifications("updateNotificationInternal");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
index 8562a2e..4f3c287 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
@@ -16,7 +16,8 @@
package com.android.systemui.statusbar.notification.collection;
-import com.android.internal.statusbar.IStatusBarService;
+import androidx.annotation.NonNull;
+
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
@@ -34,23 +35,13 @@
@SysUISingleton
public class NotifInflaterImpl implements NotifInflater {
- private final IStatusBarService mStatusBarService;
- private final NotifCollection mNotifCollection;
private final NotifInflationErrorManager mNotifErrorManager;
- private final NotifPipeline mNotifPipeline;
private NotificationRowBinderImpl mNotificationRowBinder;
@Inject
- public NotifInflaterImpl(
- IStatusBarService statusBarService,
- NotifCollection notifCollection,
- NotifInflationErrorManager errorManager,
- NotifPipeline notifPipeline) {
- mStatusBarService = statusBarService;
- mNotifCollection = notifCollection;
+ public NotifInflaterImpl(NotifInflationErrorManager errorManager) {
mNotifErrorManager = errorManager;
- mNotifPipeline = notifPipeline;
}
/**
@@ -61,8 +52,9 @@
}
@Override
- public void rebindViews(NotificationEntry entry, InflationCallback callback) {
- inflateViews(entry, callback);
+ public void rebindViews(@NonNull NotificationEntry entry, @NonNull Params params,
+ @NonNull InflationCallback callback) {
+ inflateViews(entry, params, callback);
}
/**
@@ -70,10 +62,12 @@
* views are bound.
*/
@Override
- public void inflateViews(NotificationEntry entry, InflationCallback callback) {
+ public void inflateViews(@NonNull NotificationEntry entry, @NonNull Params params,
+ @NonNull InflationCallback callback) {
try {
requireBinder().inflateViews(
entry,
+ params,
wrapInflationCallback(callback));
} catch (InflationException e) {
mNotifErrorManager.setInflationError(entry, e);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt
index 50d7324..9ae9fe5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt
@@ -58,12 +58,13 @@
* appropriately).
* 3. OnBeforeTransformGroupListeners are fired ([.addOnBeforeTransformGroupsListener])
* 4. NotifPromoters are called on each notification with a parent ([.addPromoter])
- * 5. Finalize filters are fired on each notification ([.addFinalizeFilter])
- * 6. OnBeforeSortListeners are fired ([.addOnBeforeSortListener])
- * 7. Top-level entries are assigned sections by NotifSections ([.setSections])
- * 8. Top-level entries within the same section are sorted by NotifComparators ([.setComparators])
- * 9. OnBeforeRenderListListeners are fired ([.addOnBeforeRenderListListener])
- * 10. The list is handed off to the view layer to be rendered
+ * 5. OnBeforeSortListeners are fired ([.addOnBeforeSortListener])
+ * 6. Top-level entries are assigned sections by NotifSections ([.setSections])
+ * 7. Top-level entries within the same section are sorted by NotifComparators ([.setComparators])
+ * 8. OnBeforeFinalizeFilterListeners are fired ([.addOnBeforeFinalizeFilterListener])
+ * 9. Finalize filters are fired on each notification ([.addFinalizeFilter])
+ * 10. OnBeforeRenderListListeners are fired ([.addOnBeforeRenderListListener])
+ * 11. The list is handed off to the view layer to be rendered
*/
@SysUISingleton
class NotifPipeline @Inject constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 15872da..72cd951 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -28,12 +28,15 @@
import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_SORTING;
import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_TRANSFORMING;
+import static java.util.Objects.requireNonNull;
+
import android.annotation.MainThread;
import android.annotation.Nullable;
import android.os.Trace;
import android.util.ArrayMap;
import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
@@ -64,6 +67,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -363,22 +367,24 @@
mPipelineState.incrementTo(STATE_GROUP_STABILIZING);
stabilizeGroupingNotifs(mNotifList);
+ // Step 5: Section & Sort
+ // Assign each top-level entry a section, and copy to all of its children
+ dispatchOnBeforeSort(mReadOnlyNotifList);
+ mPipelineState.incrementTo(STATE_SORTING);
+ assignSections();
+ notifySectionEntriesUpdated();
+ // Sort the list by section and then within section by our list of custom comparators
+ sortListAndGroups();
- // Step 5: Filter out entries after pre-group filtering, grouping and promoting
- // Now filters can see grouping information to determine whether to filter or not.
+ // Step 6: Filter out entries after pre-group filtering, grouping, promoting, and sorting
+ // Now filters can see grouping, sectioning, and order information to determine whether
+ // to filter or not.
dispatchOnBeforeFinalizeFilter(mReadOnlyNotifList);
mPipelineState.incrementTo(STATE_FINALIZE_FILTERING);
filterNotifs(mNotifList, mNewNotifList, mNotifFinalizeFilters);
applyNewNotifList();
pruneIncompleteGroups(mNotifList);
- // Step 6: Sort
- // Assign each top-level entry a section, then sort the list by section and then within
- // section by our list of custom comparators
- dispatchOnBeforeSort(mReadOnlyNotifList);
- mPipelineState.incrementTo(STATE_SORTING);
- sortListAndNotifySections();
-
// Step 7: Lock in our group structure and log anything that's changed since the last run
mPipelineState.incrementTo(STATE_FINALIZING);
logChanges();
@@ -408,18 +414,15 @@
private void notifySectionEntriesUpdated() {
Trace.beginSection("ShadeListBuilder.notifySectionEntriesUpdated");
- NotifSection currentSection = null;
mTempSectionMembers.clear();
- for (int i = 0; i < mNotifList.size(); i++) {
- ListEntry currentEntry = mNotifList.get(i);
- if (currentSection != currentEntry.getSection()) {
- if (currentSection != null) {
- currentSection.getSectioner().onEntriesUpdated(mTempSectionMembers);
- mTempSectionMembers.clear();
+ for (NotifSection section : mNotifSections) {
+ for (ListEntry entry : mNotifList) {
+ if (section == entry.getSection()) {
+ mTempSectionMembers.add(entry);
}
- currentSection = currentEntry.getSection();
}
- mTempSectionMembers.add(currentEntry);
+ section.getSectioner().onEntriesUpdated(mTempSectionMembers);
+ mTempSectionMembers.clear();
}
Trace.endSection();
}
@@ -653,16 +656,15 @@
final List<NotificationEntry> children = group.getRawChildren();
if (group.getSummary() != null && children.size() == 0) {
- shadeList.remove(i);
- i--;
-
NotificationEntry summary = group.getSummary();
summary.setParent(ROOT_ENTRY);
- shadeList.add(summary);
+ // The list may be sorted; replace the group with the summary, in its place
+ shadeList.set(i, summary);
group.setSummary(null);
annulAddition(group, shadeList);
+ i--; // The node we visited is gone, so be sure to visit this index again.
} else if (group.getSummary() == null
|| children.size() < MIN_CHILDREN_FOR_GROUP) {
@@ -680,7 +682,6 @@
// its children (if any) directly to top-level.
shadeList.remove(i);
- i--;
if (group.getSummary() != null) {
final NotificationEntry summary = group.getSummary();
@@ -691,11 +692,14 @@
for (int j = 0; j < children.size(); j++) {
final NotificationEntry child = children.get(j);
child.setParent(ROOT_ENTRY);
- shadeList.add(child);
+ // The list may be sorted, so add the children in order where the group was.
+ shadeList.add(i + j, child);
}
children.clear();
annulAddition(group, shadeList);
+
+ i--; // The node we visited is gone, so be sure to visit this index again.
}
}
}
@@ -765,9 +769,9 @@
}
}
- private void sortListAndNotifySections() {
- Trace.beginSection("ShadeListBuilder.sortListAndNotifySections");
- // Assign sections to top-level elements and sort their children
+ private void assignSections() {
+ Trace.beginSection("ShadeListBuilder.assignSections");
+ // Assign sections to top-level elements and their children
for (ListEntry entry : mNotifList) {
NotifSection section = applySections(entry);
if (entry instanceof GroupEntry) {
@@ -775,27 +779,64 @@
for (NotificationEntry child : parent.getChildren()) {
setEntrySection(child, section);
}
+ }
+ }
+ Trace.endSection();
+ }
+
+ private void sortListAndGroups() {
+ Trace.beginSection("ShadeListBuilder.sortListAndGroups");
+ // Assign sections to top-level elements and sort their children
+ for (ListEntry entry : mNotifList) {
+ if (entry instanceof GroupEntry) {
+ GroupEntry parent = (GroupEntry) entry;
parent.sortChildren(mGroupChildrenComparator);
}
}
mNotifList.sort(mTopLevelComparator);
assignIndexes(mNotifList);
- notifySectionEntriesUpdated();
+ // Check for suppressed order changes
+ if (!mNotifStabilityManager.isEveryChangeAllowed()) {
+ mForceReorderable = true;
+ boolean isSorted = isSorted(mNotifList, mTopLevelComparator);
+ mForceReorderable = false;
+ if (!isSorted) {
+ mNotifStabilityManager.onEntryReorderSuppressed();
+ }
+ }
Trace.endSection();
}
+ /** Determine whether the items in the list are sorted according to the comparator */
+ @VisibleForTesting
+ public static <T> boolean isSorted(List<T> items, Comparator<T> comparator) {
+ if (items.size() <= 1) {
+ return true;
+ }
+ Iterator<T> iterator = items.iterator();
+ T previous = iterator.next();
+ T current;
+ while (iterator.hasNext()) {
+ current = iterator.next();
+ if (comparator.compare(previous, current) > 0) {
+ return false;
+ }
+ previous = current;
+ }
+ return true;
+ }
+
/**
* Assign the index of each notification relative to the total order
- * @param notifList
*/
- private void assignIndexes(List<ListEntry> notifList) {
+ private static void assignIndexes(List<ListEntry> notifList) {
if (notifList.size() == 0) return;
- NotifSection currentSection = notifList.get(0).getSection();
+ NotifSection currentSection = requireNonNull(notifList.get(0).getSection());
int sectionMemberIndex = 0;
for (int i = 0; i < notifList.size(); i++) {
ListEntry entry = notifList.get(i);
- NotifSection section = entry.getSection();
+ NotifSection section = requireNonNull(entry.getSection());
if (section.getIndex() != currentSection.getIndex()) {
sectionMemberIndex = 0;
currentSection = section;
@@ -960,8 +1001,14 @@
return cmp;
};
+ /**
+ * A flag that is set to true when we want to run the comparators as if all reordering is
+ * allowed. This is used to check if the list is "out of order" after the sort is complete.
+ */
+ private boolean mForceReorderable = false;
+
private boolean canReorder(ListEntry entry) {
- return mNotifStabilityManager.isEntryReorderingAllowed(entry);
+ return mForceReorderable || mNotifStabilityManager.isEntryReorderingAllowed(entry);
}
private boolean applyFilters(NotificationEntry entry, long now, List<NotifFilter> filters) {
@@ -1031,8 +1078,11 @@
private void setEntrySection(ListEntry entry, NotifSection finalSection) {
entry.getAttachState().setSection(finalSection);
NotificationEntry representativeEntry = entry.getRepresentativeEntry();
- if (representativeEntry != null && finalSection != null) {
- representativeEntry.setBucket(finalSection.getBucket());
+ if (representativeEntry != null) {
+ representativeEntry.getAttachState().setSection(finalSection);
+ if (finalSection != null) {
+ representativeEntry.setBucket(finalSection.getBucket());
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
index afdfb3b..644f248 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -26,6 +26,9 @@
import android.util.ArrayMap;
import android.util.ArraySet;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.dagger.SysUISingleton;
@@ -35,6 +38,8 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
+import com.android.systemui.statusbar.notification.collection.inflation.NotifUiAdjustment;
+import com.android.systemui.statusbar.notification.collection.inflation.NotifUiAdjustmentProvider;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
@@ -46,7 +51,6 @@
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import javax.inject.Inject;
@@ -66,14 +70,24 @@
private final NotifInflater mNotifInflater;
private final NotifInflationErrorManager mNotifErrorManager;
private final NotifViewBarn mViewBarn;
- private final Map<NotificationEntry, Integer> mInflationStates = new ArrayMap<>();
+ private final NotifUiAdjustmentProvider mAdjustmentProvider;
+ private final ArrayMap<NotificationEntry, Integer> mInflationStates = new ArrayMap<>();
+
+ /**
+ * The map of notifications to the NotifUiAdjustment (i.e. parameters) that were calculated
+ * when the inflation started. If an update of any kind results in the adjustment changing,
+ * then the row must be reinflated. If the row is being inflated, then the inflation must be
+ * aborted and restarted.
+ */
+ private final ArrayMap<NotificationEntry, NotifUiAdjustment> mInflationAdjustments =
+ new ArrayMap<>();
/**
* The set of notifications that are currently inflating something. Note that this is
* separate from inflation state as a view could either be uninflated or inflated and still be
* inflating something.
*/
- private final Set<NotificationEntry> mInflatingNotifs = new ArraySet<>();
+ private final ArraySet<NotificationEntry> mInflatingNotifs = new ArraySet<>();
private final IStatusBarService mStatusBarService;
@@ -92,12 +106,14 @@
NotifInflater notifInflater,
NotifInflationErrorManager errorManager,
NotifViewBarn viewBarn,
+ NotifUiAdjustmentProvider adjustmentProvider,
IStatusBarService service) {
this(
logger,
notifInflater,
errorManager,
viewBarn,
+ adjustmentProvider,
service,
CHILD_BIND_CUTOFF,
MAX_GROUP_INFLATION_DELAY);
@@ -109,6 +125,7 @@
NotifInflater notifInflater,
NotifInflationErrorManager errorManager,
NotifViewBarn viewBarn,
+ NotifUiAdjustmentProvider adjustmentProvider,
IStatusBarService service,
int childBindCutoff,
long maxGroupInflationDelay) {
@@ -116,6 +133,7 @@
mNotifInflater = notifInflater;
mNotifErrorManager = errorManager;
mViewBarn = viewBarn;
+ mAdjustmentProvider = adjustmentProvider;
mStatusBarService = service;
mChildBindCutoff = childBindCutoff;
mMaxGroupInflationDelay = maxGroupInflationDelay;
@@ -160,6 +178,7 @@
public void onEntryCleanUp(NotificationEntry entry) {
mInflationStates.remove(entry);
mViewBarn.removeViewForEntry(entry);
+ mInflationAdjustments.remove(entry);
}
};
@@ -269,39 +288,78 @@
}
private void inflateRequiredNotifViews(NotificationEntry entry) {
+ NotifUiAdjustment newAdjustment = mAdjustmentProvider.calculateAdjustment(entry);
if (mInflatingNotifs.contains(entry)) {
// Already inflating this entry
+ String errorIfNoOldAdjustment = "Inflating notification has no adjustments";
+ if (needToReinflate(entry, newAdjustment, errorIfNoOldAdjustment)) {
+ inflateEntry(entry, newAdjustment, "adjustment changed while inflating");
+ }
return;
}
@InflationState int state = mInflationStates.get(entry);
switch (state) {
case STATE_UNINFLATED:
- inflateEntry(entry, "entryAdded");
+ inflateEntry(entry, newAdjustment, "entryAdded");
break;
case STATE_INFLATED_INVALID:
- rebind(entry, "entryUpdated");
+ rebind(entry, newAdjustment, "entryUpdated");
break;
case STATE_INFLATED:
+ String errorIfNoOldAdjustment = "Fully inflated notification has no adjustments";
+ if (needToReinflate(entry, newAdjustment, errorIfNoOldAdjustment)) {
+ rebind(entry, newAdjustment, "adjustment changed after inflated");
+ }
+ break;
case STATE_ERROR:
+ if (needToReinflate(entry, newAdjustment, null)) {
+ inflateEntry(entry, newAdjustment, "adjustment changed after error");
+ }
+ break;
default:
// Nothing to do.
}
}
- private void inflateEntry(NotificationEntry entry, String reason) {
- abortInflation(entry, reason);
- mInflatingNotifs.add(entry);
- mNotifInflater.inflateViews(entry, this::onInflationFinished);
+ private boolean needToReinflate(@NonNull NotificationEntry entry,
+ @NonNull NotifUiAdjustment newAdjustment, @Nullable String oldAdjustmentMissingError) {
+ NotifUiAdjustment oldAdjustment = mInflationAdjustments.get(entry);
+ if (oldAdjustment == null) {
+ if (oldAdjustmentMissingError == null) {
+ return true;
+ } else {
+ throw new IllegalStateException(oldAdjustmentMissingError);
+ }
+ }
+ return NotifUiAdjustment.needReinflate(oldAdjustment, newAdjustment);
}
- private void rebind(NotificationEntry entry, String reason) {
+ private void inflateEntry(NotificationEntry entry,
+ NotifUiAdjustment newAdjustment,
+ String reason) {
+ abortInflation(entry, reason);
+ mInflationAdjustments.put(entry, newAdjustment);
mInflatingNotifs.add(entry);
- mNotifInflater.rebindViews(entry, this::onInflationFinished);
+ NotifInflater.Params params = getInflaterParams(newAdjustment, reason);
+ mNotifInflater.inflateViews(entry, params, this::onInflationFinished);
+ }
+
+ private void rebind(NotificationEntry entry,
+ NotifUiAdjustment newAdjustment,
+ String reason) {
+ mInflationAdjustments.put(entry, newAdjustment);
+ mInflatingNotifs.add(entry);
+ NotifInflater.Params params = getInflaterParams(newAdjustment, reason);
+ mNotifInflater.rebindViews(entry, params, this::onInflationFinished);
+ }
+
+ NotifInflater.Params getInflaterParams(NotifUiAdjustment adjustment, String reason) {
+ return new NotifInflater.Params(adjustment.isMinimized(), reason);
}
private void abortInflation(NotificationEntry entry, String reason) {
mLogger.logInflationAborted(entry.getKey(), reason);
- entry.abortTask();
+ mNotifInflater.abortInflation(entry);
mInflatingNotifs.remove(entry);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
index 79eb089..c60ebcd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
@@ -19,12 +19,12 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
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.coordinator.dagger.CoordinatorScope;
+import com.android.systemui.statusbar.notification.collection.inflation.NotifUiAdjustmentProvider;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
@@ -34,6 +34,7 @@
import com.android.systemui.statusbar.notification.dagger.SilentHeader;
import com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt;
+import java.util.Collections;
import java.util.List;
import javax.inject.Inject;
@@ -50,6 +51,7 @@
public static final boolean SHOW_ALL_SECTIONS = false;
private final StatusBarStateController mStatusBarStateController;
private final HighPriorityProvider mHighPriorityProvider;
+ private final NotifUiAdjustmentProvider mAdjustmentProvider;
private final NodeController mSilentNodeController;
private final SectionHeaderController mSilentHeaderController;
private final NodeController mAlertingHeaderController;
@@ -60,11 +62,13 @@
public RankingCoordinator(
StatusBarStateController statusBarStateController,
HighPriorityProvider highPriorityProvider,
+ NotifUiAdjustmentProvider adjustmentProvider,
@AlertingHeader NodeController alertingHeaderController,
@SilentHeader SectionHeaderController silentHeaderController,
@SilentHeader NodeController silentNodeController) {
mStatusBarStateController = statusBarStateController;
mHighPriorityProvider = highPriorityProvider;
+ mAdjustmentProvider = adjustmentProvider;
mAlertingHeaderController = alertingHeaderController;
mSilentNodeController = silentNodeController;
mSilentHeaderController = silentHeaderController;
@@ -73,10 +77,10 @@
@Override
public void attach(NotifPipeline pipeline) {
mStatusBarStateController.addCallback(mStatusBarStateCallback);
+ mAdjustmentProvider.setLowPrioritySections(Collections.singleton(mMinimizedNotifSectioner));
pipeline.addPreGroupFilter(mSuspendedFilter);
pipeline.addPreGroupFilter(mDndVisualEffectsFilter);
- pipeline.addOnBeforeSortListener(entries -> resetClearAllFlags());
}
public NotifSectioner getAlertingSectioner() {
@@ -126,6 +130,7 @@
@Nullable
@Override
public void onEntriesUpdated(@NonNull List<ListEntry> entries) {
+ mHasSilentEntries = false;
for (int i = 0; i < entries.size(); i++) {
if (entries.get(i).getRepresentativeEntry().getSbn().isClearable()) {
mHasSilentEntries = true;
@@ -154,6 +159,7 @@
@Nullable
@Override
public void onEntriesUpdated(@NonNull List<ListEntry> entries) {
+ mHasMinimizedEntries = false;
for (int i = 0; i < entries.size(); i++) {
if (entries.get(i).getRepresentativeEntry().getSbn().isClearable()) {
mHasMinimizedEntries = true;
@@ -189,12 +195,6 @@
}
};
- @VisibleForTesting
- protected void resetClearAllFlags() {
- mHasSilentEntries = false;
- mHasMinimizedEntries = false;
- }
-
private final StatusBarStateController.StateListener mStatusBarStateCallback =
new StatusBarStateController.StateListener() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
index 32b1cf6..75489b1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
@@ -19,11 +19,12 @@
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
-import android.annotation.NonNull;
-
+import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
+import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
@@ -34,6 +35,8 @@
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -53,7 +56,7 @@
*/
// TODO(b/204468557): Move to @CoordinatorScope
@SysUISingleton
-public class VisualStabilityCoordinator implements Coordinator {
+public class VisualStabilityCoordinator implements Coordinator, Dumpable {
private final DelayableExecutor mDelayableExecutor;
private final WakefulnessLifecycle mWakefulnessLifecycle;
private final StatusBarStateController mStatusBarStateController;
@@ -66,6 +69,7 @@
private boolean mReorderingAllowed;
private boolean mIsSuppressingGroupChange = false;
private final Set<String> mEntriesWithSuppressedSectionChange = new HashSet<>();
+ private boolean mIsSuppressingEntryReorder = false;
// key: notification key that can temporarily change its section
// value: runnable that when run removes its associated RemoveOverrideSuppressionRunnable
@@ -77,6 +81,7 @@
@Inject
public VisualStabilityCoordinator(
+ DumpManager dumpManager,
HeadsUpManager headsUpManager,
WakefulnessLifecycle wakefulnessLifecycle,
StatusBarStateController statusBarStateController,
@@ -86,6 +91,8 @@
mWakefulnessLifecycle = wakefulnessLifecycle;
mStatusBarStateController = statusBarStateController;
mDelayableExecutor = delayableExecutor;
+
+ dumpManager.registerDumpable(this);
}
@Override
@@ -99,7 +106,6 @@
pipeline.setVisualStabilityManager(mNotifStabilityManager);
}
- // TODO(b/203828145): Ensure stability manager handles minimized state changes
// TODO(b/203826051): Ensure stability manager can allow reordering off-screen
// HUNs to the top of the shade
private final NotifStabilityManager mNotifStabilityManager =
@@ -108,6 +114,7 @@
public void onBeginRun() {
mIsSuppressingGroupChange = false;
mEntriesWithSuppressedSectionChange.clear();
+ mIsSuppressingEntryReorder = false;
}
@Override
@@ -124,7 +131,7 @@
mReorderingAllowed
|| mHeadsUpManager.isAlerting(entry.getKey())
|| mEntriesThatCanChangeSection.containsKey(entry.getKey());
- if (isSectionChangeAllowedForEntry) {
+ if (!isSectionChangeAllowedForEntry) {
mEntriesWithSuppressedSectionChange.add(entry.getKey());
}
return isSectionChangeAllowedForEntry;
@@ -134,11 +141,22 @@
public boolean isEntryReorderingAllowed(ListEntry section) {
return mReorderingAllowed;
}
+
+ @Override
+ public boolean isEveryChangeAllowed() {
+ return mReorderingAllowed;
+ }
+
+ @Override
+ public void onEntryReorderSuppressed() {
+ mIsSuppressingEntryReorder = true;
+ }
};
private void updateAllowedStates() {
mReorderingAllowed = isReorderingAllowed();
- if (mReorderingAllowed && (mIsSuppressingGroupChange || isSuppressingSectionChange())) {
+ if (mReorderingAllowed && (mIsSuppressingGroupChange || isSuppressingSectionChange()
+ || mIsSuppressingEntryReorder)) {
mNotifStabilityManager.invalidateList();
}
}
@@ -211,4 +229,23 @@
updateAllowedStates();
}
};
+
+ @Override
+ public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+ pw.println("reorderingAllowed: " + mReorderingAllowed);
+ pw.println(" screenOn: " + mScreenOn);
+ pw.println(" panelExpanded: " + mPanelExpanded);
+ pw.println(" pulsing: " + mPulsing);
+ pw.println("isSuppressingGroupChange: " + mIsSuppressingGroupChange);
+ pw.println("isSuppressingEntryReorder: " + mIsSuppressingEntryReorder);
+ pw.println("entriesWithSuppressedSectionChange: "
+ + mEntriesWithSuppressedSectionChange.size());
+ for (String key : mEntriesWithSuppressedSectionChange) {
+ pw.println(" " + key);
+ }
+ pw.println("entriesThatCanChangeSection: " + mEntriesThatCanChangeSection.size());
+ for (String key : mEntriesThatCanChangeSection.keySet()) {
+ pw.println(" " + key);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt
similarity index 73%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt
index e3d76113..c59f184 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt
@@ -14,22 +14,22 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.collection.inflation;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.coordinator.PreparationCoordinator;
+package com.android.systemui.statusbar.notification.collection.inflation
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
/**
- * Used by the {@link PreparationCoordinator}. When notifications are added or updated, the
+ * Used by the [PreparationCoordinator]. When notifications are added or updated, the
* NotifInflater is asked to (re)inflated and prepare their views. This inflation occurs off the
* main thread. When the inflation is finished, NotifInflater will trigger its InflationCallback.
*/
-public interface NotifInflater {
+interface NotifInflater {
/**
* Called to rebind the entry's views.
*
* @param callback callback called after inflation finishes
*/
- void rebindViews(NotificationEntry entry, InflationCallback callback);
+ fun rebindViews(entry: NotificationEntry, params: Params, callback: InflationCallback)
/**
* Called to inflate the views of an entry. Views are not considered inflated until all of its
@@ -37,18 +37,23 @@
*
* @param callback callback called after inflation finishes
*/
- void inflateViews(NotificationEntry entry, InflationCallback callback);
+ fun inflateViews(entry: NotificationEntry, params: Params, callback: InflationCallback)
/**
* Request to stop the inflation of an entry. For example, called when a notification is
* removed and no longer needs to be inflated.
*/
- void abortInflation(NotificationEntry entry);
+ fun abortInflation(entry: NotificationEntry)
/**
* Callback once all the views are inflated and bound for a given NotificationEntry.
*/
interface InflationCallback {
- void onInflationFinished(NotificationEntry entry);
+ fun onInflationFinished(entry: NotificationEntry)
}
-}
+
+ /**
+ * A class holding parameters used when inflating the notification row
+ */
+ class Params(val isLowPriority: Boolean, val reason: String)
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt
new file mode 100644
index 0000000..9d86b78
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.inflation
+
+import android.app.Notification
+import android.app.RemoteInput
+import android.graphics.drawable.Icon
+import android.text.TextUtils
+
+/**
+ * An immutable object which contains minimal state extracted from an entry that represents state
+ * which can change without a direct app update (e.g. with a ranking update).
+ * Diffing two entries determines if view re-inflation is needed.
+ */
+class NotifUiAdjustment internal constructor(
+ val key: String,
+ val smartActions: List<Notification.Action>,
+ val smartReplies: List<CharSequence>,
+ val isConversation: Boolean,
+ val isMinimized: Boolean
+) {
+ companion object {
+ @JvmStatic
+ fun needReinflate(
+ oldAdjustment: NotifUiAdjustment,
+ newAdjustment: NotifUiAdjustment
+ ): Boolean = when {
+ oldAdjustment === newAdjustment -> false
+ oldAdjustment.isConversation != newAdjustment.isConversation -> true
+ oldAdjustment.isMinimized != newAdjustment.isMinimized -> true
+ areDifferent(oldAdjustment.smartActions, newAdjustment.smartActions) -> true
+ newAdjustment.smartReplies != oldAdjustment.smartReplies -> true
+ else -> false
+ }
+
+ private fun areDifferent(
+ first: List<Notification.Action>,
+ second: List<Notification.Action>
+ ): Boolean = when {
+ first === second -> false
+ first.size != second.size -> true
+ else -> first.asSequence().zip(second.asSequence()).any {
+ (!TextUtils.equals(it.first.title, it.second.title)) ||
+ (areDifferent(it.first.getIcon(), it.second.getIcon())) ||
+ (it.first.actionIntent != it.second.actionIntent) ||
+ (areDifferent(it.first.remoteInputs, it.second.remoteInputs))
+ }
+ }
+
+ private fun areDifferent(first: Icon?, second: Icon?): Boolean = when {
+ first === second -> false
+ first == null || second == null -> true
+ else -> !first.sameAs(second)
+ }
+
+ private fun areDifferent(
+ first: Array<RemoteInput>?,
+ second: Array<RemoteInput>?
+ ): Boolean = when {
+ first === second -> false
+ first == null || second == null -> true
+ first.size != second.size -> true
+ else -> first.asSequence().zip(second.asSequence()).any {
+ (!TextUtils.equals(it.first.label, it.second.label)) ||
+ (areDifferent(it.first.choices, it.second.choices))
+ }
+ }
+
+ private fun areDifferent(
+ first: Array<CharSequence>?,
+ second: Array<CharSequence>?
+ ): Boolean = when {
+ first === second -> false
+ first == null || second == null -> true
+ first.size != second.size -> true
+ else -> first.asSequence().zip(second.asSequence()).any {
+ !TextUtils.equals(it.first, it.second)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
new file mode 100644
index 0000000..3290cdf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
@@ -0,0 +1,63 @@
+/*
+ * 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.inflation
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.collection.GroupEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
+import javax.inject.Inject
+
+/**
+ * A class which provides an adjustment object to the preparation coordinator which is uses
+ * to ensure that notifications are reinflated when ranking-derived information changes.
+ */
+@SysUISingleton
+open class NotifUiAdjustmentProvider @Inject constructor() {
+
+ private lateinit var lowPrioritySections: Set<NotifSectioner>
+
+ /**
+ * Feed the provider the information it needs about which sections should have minimized top
+ * level views, so that it can calculate the correct minimized value in the adjustment.
+ */
+ fun setLowPrioritySections(sections: Collection<NotifSectioner>) {
+ lowPrioritySections = sections.toSet()
+ }
+
+ private fun isEntryMinimized(entry: NotificationEntry): Boolean {
+ val section = entry.section ?: error("Entry must have a section to determine if minimized")
+ val parent = entry.parent ?: error("Entry must have a parent to determine if minimized")
+ val isLowPrioritySection = lowPrioritySections.contains(section.sectioner)
+ val isTopLevelEntry = parent == GroupEntry.ROOT_ENTRY
+ val isGroupSummary = parent.summary == entry
+ return isLowPrioritySection && (isTopLevelEntry || isGroupSummary)
+ }
+
+ /**
+ * Returns a adjustment object for the given entry. This can be compared to a previous instance
+ * from the same notification using [NotifUiAdjustment.needReinflate] to determine if it
+ * should be reinflated.
+ */
+ fun calculateAdjustment(entry: NotificationEntry) = NotifUiAdjustment(
+ key = entry.key,
+ smartActions = entry.ranking.smartActions,
+ smartReplies = entry.ranking.smartReplies,
+ isConversation = entry.ranking.isConversation,
+ isMinimized = isEntryMinimized(entry)
+ )
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java
index 1215ade..3a4701c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java
@@ -32,12 +32,10 @@
/**
* Called when a notification has been added or updated. The binder must asynchronously inflate
* and bind the views associated with the notification.
- *
- * TODO: The caller is notified when the inflation completes, but this is currently a very
- * roundabout business. Add an explicit completion/failure callback to this method.
*/
void inflateViews(
NotificationEntry entry,
+ NotifInflater.Params params,
NotificationRowContentBinder.InflationCallback callback)
throws InflationException;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index e5425cf..5c8e8b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -25,6 +25,7 @@
import com.android.internal.util.NotificationMessagingUtil;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -32,6 +33,7 @@
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.NotificationClicker;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.legacy.LowPriorityInflationHelper;
import com.android.systemui.statusbar.notification.icon.IconManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
@@ -53,6 +55,7 @@
private static final String TAG = "NotificationViewManager";
private final Context mContext;
+ private final FeatureFlags mFeatureFlags;
private final NotificationMessagingUtil mMessagingUtil;
private final NotificationRemoteInputManager mNotificationRemoteInputManager;
private final NotificationLockscreenUserManager mNotificationLockscreenUserManager;
@@ -72,6 +75,7 @@
@Inject
public NotificationRowBinderImpl(
Context context,
+ FeatureFlags featureFlags,
NotificationMessagingUtil notificationMessagingUtil,
NotificationRemoteInputManager notificationRemoteInputManager,
NotificationLockscreenUserManager notificationLockscreenUserManager,
@@ -82,6 +86,7 @@
IconManager iconManager,
LowPriorityInflationHelper lowPriorityInflationHelper) {
mContext = context;
+ mFeatureFlags = featureFlags;
mNotifBindPipeline = notifBindPipeline;
mRowContentBindStage = rowContentBindStage;
mMessagingUtil = notificationMessagingUtil;
@@ -116,8 +121,13 @@
@Override
public void inflateViews(
NotificationEntry entry,
+ NotifInflater.Params params,
NotificationRowContentBinder.InflationCallback callback)
throws InflationException {
+ if (params == null) {
+ // weak assert that the params should always be passed in the new pipeline
+ mFeatureFlags.checkLegacyPipelineEnabled();
+ }
ViewGroup parent = mListContainer.getViewParentForNotification(entry);
if (entry.rowExists()) {
@@ -125,7 +135,7 @@
ExpandableNotificationRow row = entry.getRow();
row.reset();
updateRow(entry, row);
- inflateContentViews(entry, row, callback);
+ inflateContentViews(entry, params, row, callback);
} else {
mIconManager.createIcons(entry);
mRowInflaterTaskProvider.get().inflate(mContext, parent, entry,
@@ -144,7 +154,7 @@
entry.setRowController(rowController);
bindRow(entry, row);
updateRow(entry, row);
- inflateContentViews(entry, row, callback);
+ inflateContentViews(entry, params, row, callback);
});
}
}
@@ -177,12 +187,13 @@
NotificationUiAdjustment oldAdjustment,
NotificationUiAdjustment newAdjustment,
NotificationRowContentBinder.InflationCallback callback) {
+ mFeatureFlags.checkLegacyPipelineEnabled();
if (NotificationUiAdjustment.needReinflate(oldAdjustment, newAdjustment)) {
if (entry.rowExists()) {
ExpandableNotificationRow row = entry.getRow();
row.reset();
updateRow(entry, row);
- inflateContentViews(entry, row, callback);
+ inflateContentViews(entry, null, row, callback);
} else {
// Once the RowInflaterTask is done, it will pick up the updated entry, so
// no-op here.
@@ -216,15 +227,24 @@
*/
private void inflateContentViews(
NotificationEntry entry,
+ NotifInflater.Params inflaterParams,
ExpandableNotificationRow row,
@Nullable NotificationRowContentBinder.InflationCallback inflationCallback) {
final boolean useIncreasedCollapsedHeight =
mMessagingUtil.isImportantMessaging(entry.getSbn(), entry.getImportance());
- // If this is our first time inflating, we don't actually know the groupings for real
- // yet, so we might actually inflate a low priority content view incorrectly here and have
- // to correct it later in the pipeline. On subsequent inflations (i.e. updates), this
- // should inflate the correct view.
- final boolean isLowPriority = mLowPriorityInflationHelper.shouldUseLowPriorityView(entry);
+ final boolean isLowPriority;
+ if (inflaterParams != null) {
+ // NEW pipeline
+ isLowPriority = inflaterParams.isLowPriority();
+ } else {
+ // LEGACY pipeline
+ mFeatureFlags.checkLegacyPipelineEnabled();
+ // If this is our first time inflating, we don't actually know the groupings for real
+ // yet, so we might actually inflate a low priority content view incorrectly here and
+ // have to correct it later in the pipeline. On subsequent inflations (i.e. updates),
+ // this should inflate the correct view.
+ isLowPriority = mLowPriorityInflationHelper.shouldUseLowPriorityView(entry);
+ }
RowContentBindParams params = mRowContentBindStage.getStageParams(entry);
params.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/LowPriorityInflationHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LowPriorityInflationHelper.java
similarity index 84%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/LowPriorityInflationHelper.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LowPriorityInflationHelper.java
index 518c3f1..dd1f948 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/LowPriorityInflationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LowPriorityInflationHelper.java
@@ -14,13 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.collection.inflation;
+package com.android.systemui.statusbar.notification.collection.legacy;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.RowContentBindParams;
import com.android.systemui.statusbar.notification.row.RowContentBindStage;
@@ -61,6 +59,7 @@
public void recheckLowPriorityViewAndInflate(
NotificationEntry entry,
ExpandableNotificationRow row) {
+ mFeatureFlags.checkLegacyPipelineEnabled();
RowContentBindParams params = mRowContentBindStage.getStageParams(entry);
final boolean shouldBeLowPriority = shouldUseLowPriorityView(entry);
if (!row.isRemoved() && row.isLowPriority() != shouldBeLowPriority) {
@@ -74,12 +73,7 @@
* Whether the notification should inflate a low priority version of its content views.
*/
public boolean shouldUseLowPriorityView(NotificationEntry entry) {
- boolean isGroupChild;
- if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
- isGroupChild = (entry.getParent() != GroupEntry.ROOT_ENTRY);
- } else {
- isGroupChild = mGroupManager.isChildInGroup(entry);
- }
- return entry.isAmbient() && !isGroupChild;
+ mFeatureFlags.checkLegacyPipelineEnabled();
+ return entry.isAmbient() && !mGroupManager.isChildInGroup(entry);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt
index 6424e37..8444287 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt
@@ -27,8 +27,7 @@
val label: String
get() = "Section($index, $bucket, \"${sectioner.name}\")"
- val headerController: NodeController?
- get() = sectioner.headerNodeController
+ val headerController: NodeController? = sectioner.headerNodeController
@PriorityBucket val bucket: Int = sectioner.bucket
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java
index 027ac0f..798bfe7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java
@@ -82,8 +82,8 @@
public static final int STATE_GROUPING = 4;
public static final int STATE_TRANSFORMING = 5;
public static final int STATE_GROUP_STABILIZING = 6;
- public static final int STATE_FINALIZE_FILTERING = 7;
- public static final int STATE_SORTING = 8;
+ public static final int STATE_SORTING = 7;
+ public static final int STATE_FINALIZE_FILTERING = 8;
public static final int STATE_FINALIZING = 9;
@IntDef(prefix = { "STATE_" }, value = {
@@ -94,8 +94,8 @@
STATE_GROUPING,
STATE_TRANSFORMING,
STATE_GROUP_STABILIZING,
- STATE_FINALIZE_FILTERING,
STATE_SORTING,
+ STATE_FINALIZE_FILTERING,
STATE_FINALIZING,
})
@Retention(RetentionPolicy.SOURCE)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.java
index 520791c..cb2d3cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.java
@@ -60,4 +60,19 @@
* @return if can re-order
*/
public abstract boolean isEntryReorderingAllowed(ListEntry entry);
+
+ /**
+ * Called by the pipeline to determine if every call to the other stability methods would
+ * return true, regardless of parameters. This allows the pipeline to skip any pieces of
+ * work related to stability.
+ *
+ * @return true if all other methods will return true for any parameters.
+ */
+ public abstract boolean isEveryChangeAllowed();
+
+ /**
+ * Called by the pipeline to inform the stability manager that an entry reordering was indeed
+ * suppressed as the result of a previous call to {@link #isEntryReorderingAllowed(ListEntry)}.
+ */
+ public abstract void onEntryReorderSuppressed();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index 11b0429..757f68a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -108,7 +108,7 @@
notifBindPipelineInitializer.initialize()
animatedImageNotificationManager.bind()
- if (featureFlags.isNewNotifPipelineEnabled) {
+ if (INITIALIZE_NEW_PIPELINE) {
newNotifPipeline.get().initialize(
notificationListener,
notificationRowBinder,
@@ -170,4 +170,9 @@
override fun getActiveNotificationsCount(): Int {
return entryManager.activeNotificationsCount
}
+
+ companion object {
+ // NOTE: The new pipeline is always active, even if the old pipeline is *rendering*.
+ private const val INITIALIZE_NEW_PIPELINE = true
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index 5f157a7..408c457 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -22,6 +22,7 @@
import android.view.View
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.R
+import com.android.systemui.flags.FeatureFlags
import com.android.systemui.media.KeyguardMediaController
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.StatusBarState
@@ -53,6 +54,7 @@
* TODO: Move remaining sections logic from NSSL into this class.
*/
class NotificationSectionsManager @Inject internal constructor(
+ private val featureFlags: FeatureFlags,
private val statusBarStateController: StatusBarStateController,
private val configurationController: ConfigurationController,
private val keyguardMediaController: KeyguardMediaController,
@@ -199,6 +201,7 @@
override var targetPosition: Int? = null
override fun adjustViewPosition() {
+ featureFlags.checkLegacyPipelineEnabled()
val target = targetPosition
val current = currentPosition
if (target == null) {
@@ -225,6 +228,7 @@
private fun <T : StackScrollerDecorView> decorViewHeaderState(
header: T
): SectionUpdateState<T> {
+ featureFlags.checkLegacyPipelineEnabled()
val inner = expandableViewHeaderState(header)
return object : SectionUpdateState<T> by inner {
override fun adjustViewPosition() {
@@ -241,6 +245,7 @@
* bookkeeping and adds/moves/removes section headers if appropriate.
*/
fun updateSectionBoundaries(reason: String) {
+ featureFlags.checkLegacyPipelineEnabled()
if (!isUsingMultipleSections) {
return
}
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 bc79db3..a97a54a 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
@@ -4631,6 +4631,7 @@
public void addContainerViewAt(View v, int index) {
Assert.isMainThread();
+ ensureRemovedFromTransientContainer(v);
addView(v, index);
if (v instanceof ExpandableNotificationRow && mController.isShowingEmptyShadeView()) {
mController.updateShowEmptyShadeView();
@@ -4640,6 +4641,22 @@
updateSpeedBumpIndex();
}
+ private void ensureRemovedFromTransientContainer(View v) {
+ if (v.getParent() == this && v instanceof SectionHeaderView) {
+ ExpandableView expandableView = (ExpandableView) v;
+ ViewGroup transientContainer = expandableView.getTransientContainer();
+ // If the child is animating away, it will still have a parent, so
+ // detach it first
+ // TODO: We should really cancel the active animations here. This will
+ // happen automatically when the view's intro animation starts, but
+ // it's a fragile link.
+ if (transientContainer != null) {
+ transientContainer.removeTransientView(v);
+ expandableView.setTransientContainer(null);
+ }
+ }
+ }
+
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void runAfterAnimationFinished(Runnable runnable) {
mAnimationFinishedRunnables.add(runnable);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
index 6647769..eb7410c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -17,6 +17,8 @@
package com.android.systemui.statusbar.notification.stack;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_ROW_SWIPE;
+
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.res.Resources;
@@ -28,6 +30,7 @@
import android.view.ViewConfiguration;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.systemui.SwipeHelper;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.FalsingManager;
@@ -264,6 +267,22 @@
}
@Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ final boolean previousIsSwiping = isSwiping();
+ boolean ret = super.onInterceptTouchEvent(ev);
+ final View swipedView = getSwipedView();
+ if (!previousIsSwiping && swipedView != null) {
+ InteractionJankMonitor.getInstance().begin(swipedView,
+ CUJ_NOTIFICATION_SHADE_ROW_SWIPE);
+ }
+ return ret;
+ }
+
+ protected void onDismissChildWithAnimationFinished() {
+ InteractionJankMonitor.getInstance().end(CUJ_NOTIFICATION_SHADE_ROW_SWIPE);
+ }
+
+ @Override
public void dismissChild(final View view, float velocity,
boolean useAccelerateInterpolator) {
superDismissChild(view, velocity, useAccelerateInterpolator);
@@ -281,6 +300,11 @@
super.dismissChild(view, velocity, useAccelerateInterpolator);
}
+ @Override
+ protected void onSnapChildWithAnimationFinished() {
+ InteractionJankMonitor.getInstance().end(CUJ_NOTIFICATION_SHADE_ROW_SWIPE);
+ }
+
@VisibleForTesting
protected void superSnapChild(final View animView, final float targetLeft, float velocity) {
super.snapChild(animView, targetLeft, velocity);
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 e26e69f..3953698 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -2342,7 +2342,7 @@
mQs.setExpanded(mQsExpanded);
}
- private void setQsExpansion(float height) {
+ void setQsExpansion(float height) {
height = Math.min(Math.max(height, mQsMinExpansionHeight), mQsMaxExpansionHeight);
mQsFullyExpanded = height == mQsMaxExpansionHeight && mQsMaxExpansionHeight != 0;
if (height > mQsMinExpansionHeight && !mQsExpanded && !mStackScrollerOverscrolling
@@ -2386,7 +2386,13 @@
int qsPanelBottomY = calculateQsBottomPosition(qsExpansionFraction);
mScrimController.setQsPosition(qsExpansionFraction, qsPanelBottomY);
setQSClippingBounds();
- mNotificationStackScrollLayoutController.setQsExpansionFraction(qsExpansionFraction);
+
+ // Only need to notify the notification stack when we're not in split screen mode. If we
+ // do, then the notification panel starts scrolling along with the QS.
+ if (!mShouldUseSplitNotificationShade) {
+ mNotificationStackScrollLayoutController.setQsExpansionFraction(qsExpansionFraction);
+ }
+
mDepthController.setQsPanelExpansion(qsExpansionFraction);
if (mCommunalViewController != null) {
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 afa333a..b47e71a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -128,12 +128,12 @@
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.ActivityIntentHelper;
import com.android.systemui.AutoReinflateContainer;
+import com.android.systemui.CoreStartable;
import com.android.systemui.DejankUtils;
import com.android.systemui.EventLogTags;
import com.android.systemui.InitController;
import com.android.systemui.Prefs;
import com.android.systemui.R;
-import com.android.systemui.SystemUI;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.DelegateLaunchAnimatorController;
import com.android.systemui.assist.AssistManager;
@@ -255,7 +255,7 @@
import dagger.Lazy;
/** */
-public class StatusBar extends SystemUI implements
+public class StatusBar extends CoreStartable implements
ActivityStarter,
LifecycleOwner {
public static final boolean MULTIUSER_DEBUG = false;
@@ -2385,6 +2385,8 @@
if (mLightRevealScrim != null) {
pw.println(
+ "mLightRevealScrim.getRevealEffect(): " + mLightRevealScrim.getRevealEffect());
+ pw.println(
"mLightRevealScrim.getRevealAmount(): " + mLightRevealScrim.getRevealAmount());
}
@@ -3370,17 +3372,24 @@
return;
}
- if (wakingUp && mWakefulnessLifecycle.getLastWakeReason()
- == PowerManager.WAKE_REASON_POWER_BUTTON
- || !wakingUp && mWakefulnessLifecycle.getLastSleepReason()
- == PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON) {
+ final boolean wakingUpFromPowerButton = wakingUp
+ && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)
+ && mWakefulnessLifecycle.getLastWakeReason()
+ == PowerManager.WAKE_REASON_POWER_BUTTON;
+ final boolean sleepingFromPowerButton = !wakingUp
+ && mWakefulnessLifecycle.getLastSleepReason()
+ == PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON;
+
+ if (wakingUpFromPowerButton || sleepingFromPowerButton) {
mLightRevealScrim.setRevealEffect(mPowerButtonReveal);
+ mLightRevealScrim.setRevealAmount(1f - mStatusBarStateController.getDozeAmount());
} else if (!wakingUp || !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
// If we're going to sleep, but it's not from the power button, use the default reveal.
// If we're waking up, only use the default reveal if the biometric controller didn't
// already set it to the circular reveal because we're waking up from a fingerprint/face
// auth.
mLightRevealScrim.setRevealEffect(LiftReveal.INSTANCE);
+ mLightRevealScrim.setRevealAmount(1f - mStatusBarStateController.getDozeAmount());
}
}
@@ -4278,7 +4287,9 @@
Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
}
- mViewHierarchyManager.updateRowStates();
+ if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
+ mViewHierarchyManager.updateRowStates();
+ }
mScreenPinningRequest.onConfigurationChanged();
}
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 ecd5c98..7c0f923 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -305,6 +305,9 @@
@Override
public void updateNotificationViews(final String reason) {
+ if (!mFeatureFlags.checkLegacyPipelineEnabled()) {
+ return;
+ }
// The function updateRowStates depends on both of these being non-null, so check them here.
// We may be called before they are set from DeviceProvisionedController's callback.
if (mScrimController == null) return;
@@ -325,15 +328,17 @@
// End old BaseStatusBar.userSwitched
if (MULTIUSER_DEBUG) mNotificationPanel.setHeaderDebugInfo("USER " + newUserId);
mCommandQueue.animateCollapsePanels();
- if (mReinflateNotificationsOnUserSwitched) {
- updateNotificationsOnDensityOrFontScaleChanged();
- mReinflateNotificationsOnUserSwitched = false;
+ if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
+ if (mReinflateNotificationsOnUserSwitched) {
+ updateNotificationsOnDensityOrFontScaleChanged();
+ mReinflateNotificationsOnUserSwitched = false;
+ }
+ if (mDispatchUiModeChangeOnUserSwitched) {
+ updateNotificationsOnUiModeChanged();
+ mDispatchUiModeChangeOnUserSwitched = false;
+ }
+ updateNotificationViews("user switched");
}
- if (mDispatchUiModeChangeOnUserSwitched) {
- updateNotificationsOnUiModeChanged();
- mDispatchUiModeChangeOnUserSwitched = false;
- }
- updateNotificationViews("user switched");
mMediaManager.clearCurrentMediaNotification();
mStatusBar.setLockscreenUser(newUserId);
updateMediaMetaData(true, false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 37a763b..8ea7d62 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -22,7 +22,7 @@
import android.os.ServiceManager;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.CommandQueue;
@@ -36,7 +36,7 @@
* Serves as a collection of UI components, rather than showing its own UI.
*/
@SysUISingleton
-public class TvStatusBar extends SystemUI implements CommandQueue.Callbacks {
+public class TvStatusBar extends CoreStartable implements CommandQueue.Callbacks {
private final CommandQueue mCommandQueue;
private final Lazy<AssistManager> mAssistManagerLazy;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/VpnStatusObserver.kt b/packages/SystemUI/src/com/android/systemui/statusbar/tv/VpnStatusObserver.kt
index 7c6f86a..e25a105 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/VpnStatusObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/VpnStatusObserver.kt
@@ -24,9 +24,9 @@
import android.content.Context
import com.android.internal.messages.nano.SystemMessageProto
import com.android.internal.net.VpnConfig
+import com.android.systemui.CoreStartable
import com.android.systemui.Dependency
import com.android.systemui.R
-import com.android.systemui.SystemUI
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.policy.SecurityController
import javax.inject.Inject
@@ -35,7 +35,7 @@
* Observes if a vpn connection is active and displays a notification to the user
*/
@SysUISingleton
-class VpnStatusObserver @Inject constructor(context: Context) : SystemUI(context),
+class VpnStatusObserver @Inject constructor(context: Context) : CoreStartable(context),
SecurityController.SecurityControllerCallback {
private var vpnConnected = false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java
index d985803..8026ba5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java
@@ -24,7 +24,7 @@
import android.util.Log;
import android.util.SparseArray;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
import com.android.systemui.statusbar.NotificationListener;
import javax.inject.Inject;
@@ -32,7 +32,7 @@
/**
* Keeps track of the notifications on TV.
*/
-public class TvNotificationHandler extends SystemUI implements
+public class TvNotificationHandler extends CoreStartable implements
NotificationListener.NotificationHandler {
private static final String TAG = "TvNotificationHandler";
private final NotificationListener mNotificationListener;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java
index c9bc7e6..892fedc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java
@@ -25,7 +25,7 @@
import android.os.UserHandle;
import android.util.Log;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.CommandQueue;
@@ -35,7 +35,7 @@
* Offers control methods for the notification panel handler on TV devices.
*/
@SysUISingleton
-public class TvNotificationPanel extends SystemUI implements CommandQueue.Callbacks {
+public class TvNotificationPanel extends CoreStartable implements CommandQueue.Callbacks {
private static final String TAG = "TvNotificationPanel";
private final CommandQueue mCommandQueue;
private final String mNotificationHandlerPackage;
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index c5f463f..6d176a7 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -53,8 +53,8 @@
import androidx.annotation.NonNull;
import com.android.internal.graphics.ColorUtils;
+import com.android.systemui.CoreStartable;
import com.android.systemui.Dumpable;
-import com.android.systemui.SystemUI;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
@@ -93,7 +93,7 @@
* associated work profiles
*/
@SysUISingleton
-public class ThemeOverlayController extends SystemUI implements Dumpable {
+public class ThemeOverlayController extends CoreStartable implements Dumpable {
protected static final String TAG = "ThemeOverlayController";
private static final boolean DEBUG = true;
@@ -259,8 +259,13 @@
if (DEBUG) Log.d(TAG, "Updating overlays for user switch / profile added.");
reevaluateSystemTheme(true /* forceReload */);
} else if (Intent.ACTION_WALLPAPER_CHANGED.equals(intent.getAction())) {
- mAcceptColorEvents = true;
- Log.i(TAG, "Allowing color events again");
+ if (intent.getBooleanExtra(WallpaperManager.EXTRA_FROM_FOREGROUND_APP, false)) {
+ mAcceptColorEvents = true;
+ Log.i(TAG, "Wallpaper changed, allowing color events again");
+ } else {
+ Log.i(TAG, "Wallpaper changed from background app, "
+ + "keep deferring color events. Accepting: " + mAcceptColorEvents);
+ }
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
index 42f6687..0758f8f 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
@@ -37,7 +37,7 @@
import androidx.annotation.VisibleForTesting;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.CommandQueue;
@@ -49,7 +49,7 @@
* Controls display of text toasts.
*/
@SysUISingleton
-public class ToastUI extends SystemUI implements CommandQueue.Callbacks {
+public class ToastUI extends CoreStartable implements CommandQueue.Callbacks {
// values from NotificationManagerService#LONG_DELAY and NotificationManagerService#SHORT_DELAY
private static final int TOAST_LONG_TIME = 3500; // 3.5 seconds
private static final int TOAST_SHORT_TIME = 2000; // 2 seconds
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
index 353333f..e59d2f23 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
@@ -16,7 +16,7 @@
package com.android.systemui.tv;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.GlobalRootComponent;
import com.android.systemui.statusbar.tv.notifications.TvNotificationHandler;
@@ -33,5 +33,5 @@
@Binds
@IntoMap
@ClassKey(TvNotificationHandler.class)
- SystemUI bindTvNotificationHandler(TvNotificationHandler systemui);
+ CoreStartable bindTvNotificationHandler(TvNotificationHandler systemui);
}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
index 755372b..881ac08 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
@@ -44,12 +44,12 @@
import com.android.internal.R;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.systemui.SystemUI;
+import com.android.systemui.CoreStartable;
import com.android.systemui.util.NotificationChannels;
import java.util.List;
-public class StorageNotification extends SystemUI {
+public class StorageNotification extends CoreStartable {
private static final String TAG = "StorageNotification";
private static final String ACTION_SNOOZE_VOLUME = "com.android.systemui.action.SNOOZE_VOLUME";
@@ -224,7 +224,7 @@
.setCategory(Notification.CATEGORY_SYSTEM)
.setDeleteIntent(buildSnoozeIntent(fsUuid))
.extend(new Notification.TvExtender());
- SystemUI.overrideNotificationAppName(mContext, builder, false);
+ CoreStartable.overrideNotificationAppName(mContext, builder, false);
mNotificationManager.notifyAsUser(fsUuid, SystemMessage.NOTE_STORAGE_PRIVATE,
builder.build(), UserHandle.ALL);
@@ -252,7 +252,7 @@
.setLocalOnly(true)
.setCategory(Notification.CATEGORY_ERROR)
.extend(new Notification.TvExtender());
- SystemUI.overrideNotificationAppName(mContext, builder, false);
+ CoreStartable.overrideNotificationAppName(mContext, builder, false);
mNotificationManager.notifyAsUser(disk.getId(), SystemMessage.NOTE_STORAGE_DISK,
builder.build(), UserHandle.ALL);
@@ -527,7 +527,7 @@
.setCategory(Notification.CATEGORY_PROGRESS)
.setProgress(100, status, false)
.setOngoing(true);
- SystemUI.overrideNotificationAppName(mContext, builder, false);
+ CoreStartable.overrideNotificationAppName(mContext, builder, false);
mNotificationManager.notifyAsUser(move.packageName, SystemMessage.NOTE_STORAGE_MOVE,
builder.build(), UserHandle.ALL);
@@ -577,7 +577,7 @@
.setLocalOnly(true)
.setCategory(Notification.CATEGORY_SYSTEM)
.setAutoCancel(true);
- SystemUI.overrideNotificationAppName(mContext, builder, false);
+ CoreStartable.overrideNotificationAppName(mContext, builder, false);
mNotificationManager.notifyAsUser(move.packageName, SystemMessage.NOTE_STORAGE_MOVE,
builder.build(), UserHandle.ALL);
diff --git a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
index f97f4d0..ce7e4cf 100644
--- a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
+++ b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
@@ -23,13 +23,13 @@
import android.provider.Settings;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.CoreStartable;
import com.android.systemui.R;
-import com.android.systemui.SystemUI;
import com.android.wm.shell.pip.tv.TvPipNotificationController;
import java.util.Arrays;
-public class NotificationChannels extends SystemUI {
+public class NotificationChannels extends CoreStartable {
public static String ALERTS = "ALR";
public static String SCREENSHOTS_HEADSUP = "SCN_HEADSUP";
public static String GENERAL = "GEN";
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
index 4318994..612b7cb 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
@@ -45,9 +45,9 @@
import android.view.View;
import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.CoreStartable;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
-import com.android.systemui.SystemUI;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
@@ -565,7 +565,7 @@
/** */
@SysUISingleton
- public static class Service extends SystemUI implements Dumpable {
+ public static class Service extends CoreStartable implements Dumpable {
private final GarbageMonitor mGarbageMonitor;
@Inject
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
index c378e3b..8007ed3 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
@@ -21,8 +21,8 @@
import android.os.Handler;
import android.util.Log;
+import com.android.systemui.CoreStartable;
import com.android.systemui.R;
-import com.android.systemui.SystemUI;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.qs.tiles.DndTile;
@@ -32,7 +32,7 @@
import javax.inject.Inject;
@SysUISingleton
-public class VolumeUI extends SystemUI {
+public class VolumeUI extends CoreStartable {
private static final String TAG = "VolumeUI";
private static boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 74611cc..66c70ed 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -40,8 +40,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.CoreStartable;
import com.android.systemui.Dependency;
-import com.android.systemui.SystemUI;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.WMComponent;
import com.android.systemui.dagger.qualifiers.Main;
@@ -92,7 +92,7 @@
* -> WMShell starts and binds SysUI with Shell components via exported Shell interfaces
*/
@SysUISingleton
-public final class WMShell extends SystemUI
+public final class WMShell extends CoreStartable
implements CommandQueue.Callbacks, ProtoTraceable<SystemUiTraceProto> {
private static final String TAG = WMShell.class.getName();
private static final int INVALID_SYSUI_STATE_MASK =
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 1ee6f70..ff5960b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -472,7 +472,8 @@
mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
mTestableLooper.processAllMessages();
- verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt());
+ verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(),
+ anyInt());
verify(mFingerprintManager, never()).detectFingerprint(any(), any(), anyInt());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
index 045fb57f..148c6ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
@@ -48,6 +48,7 @@
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
@@ -61,11 +62,11 @@
@RunWith(AndroidTestingRunner.class)
public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
- private static final float DEFAULT_SCALE = 3.0f;
+ private static final float DEFAULT_SCALE = 4.0f;
private static final float DEFAULT_CENTER_X = 400.0f;
private static final float DEFAULT_CENTER_Y = 500.0f;
// The duration couldn't too short, otherwise the ValueAnimator won't work in expectation.
- private static final long ANIMATION_DURATION_MS = 200;
+ private static final long ANIMATION_DURATION_MS = 300;
private AtomicReference<Float> mCurrentScale = new AtomicReference<>((float) 0);
private AtomicReference<Float> mCurrentCenterX = new AtomicReference<>((float) 0);
@@ -84,7 +85,7 @@
IRemoteMagnificationAnimationCallback mAnimationCallback;
@Mock
IRemoteMagnificationAnimationCallback mAnimationCallback2;
- @Mock
+ @Mock(answer = Answers.RETURNS_SELF)
SysUiState mSysUiState;
private SpyWindowMagnificationController mController;
private WindowMagnificationController mSpyController;
@@ -127,6 +128,28 @@
verify(mAnimationCallback).onResult(true);
}
+ @Test
+ public void enableWindowMagnificationWithoutCallback_disabled_expectedValues() {
+ enableWindowMagnificationWithoutAnimation();
+
+ verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y);
+ }
+
+ @Test
+ public void enableWindowMagnificationWithoutCallback_enabled_expectedValues() {
+ enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback);
+ final float targetScale = DEFAULT_SCALE + 1.0f;
+ final float targetCenterX = DEFAULT_CENTER_X + 100;
+ final float targetCenterY = DEFAULT_CENTER_Y + 100;
+
+ mInstrumentation.runOnMainSync(
+ () -> {
+ mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
+ targetCenterX, targetCenterY, null);
+ });
+
+ verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
+ }
@Test
public void enableWindowMagnificationWithScaleOne_disabled_NoAnimationAndInvokeCallback()
@@ -174,6 +197,52 @@
}
@Test
+ public void enableWindowMagnificationWithScaleOne_enabled_AnimationAndInvokeCallback()
+ throws RemoteException {
+ enableWindowMagnificationWithoutAnimation();
+
+ mInstrumentation.runOnMainSync(() -> {
+ Mockito.reset(mSpyController);
+ mWindowMagnificationAnimationController.enableWindowMagnification(1.0f,
+ DEFAULT_CENTER_X + 100, DEFAULT_CENTER_Y + 100, mAnimationCallback);
+ mCurrentScale.set(mController.getScale());
+ mCurrentCenterX.set(mController.getCenterX());
+ mCurrentCenterY.set(mController.getCenterY());
+ });
+
+ SystemClock.sleep(mWaitingAnimationPeriod);
+
+ verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
+ mCenterXCaptor.capture(), mCenterYCaptor.capture());
+ verifyStartValue(mScaleCaptor, mCurrentScale.get());
+ verifyStartValue(mCenterXCaptor, mCurrentCenterX.get());
+ verifyStartValue(mCenterYCaptor, mCurrentCenterY.get());
+ // It presents the window magnification is disabled.
+ verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
+
+ verify(mAnimationCallback).onResult(true);
+ }
+
+ @Test
+ public void
+ enableMagnificationWithoutCallback_enabling_expectedValuesAndInvokeFormerCallback()
+ throws RemoteException {
+ enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
+ mAnimationCallback);
+ final float targetScale = DEFAULT_SCALE - 1.0f;
+ final float targetCenterX = DEFAULT_CENTER_X + 100;
+ final float targetCenterY = DEFAULT_CENTER_Y + 100;
+
+ mInstrumentation.runOnMainSync(() -> {
+ Mockito.reset(mSpyController);
+ mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
+ targetCenterX, targetCenterY, null);
+ });
+ verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
+ verify(mAnimationCallback).onResult(false);
+ }
+
+ @Test
public void enableWindowMagnificationWithSameSpec_enabling_NoAnimationAndInvokeCallback()
throws RemoteException {
enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
@@ -231,6 +300,28 @@
}
@Test
+ public void
+ enableMagnificationWithoutCallback_disabling_expectedValuesAndInvokeFormerCallback()
+ throws RemoteException {
+ enableWindowMagnificationWithoutAnimation();
+ deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
+ mAnimationCallback);
+ final float targetScale = DEFAULT_SCALE + 1.0f;
+ final float targetCenterX = DEFAULT_CENTER_X + 100;
+ final float targetCenterY = DEFAULT_CENTER_Y + 100;
+
+ mInstrumentation.runOnMainSync(
+ () -> {
+ Mockito.reset(mSpyController);
+ mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
+ targetCenterX, targetCenterY, null);
+ });
+
+ verify(mAnimationCallback).onResult(false);
+ verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
+ }
+
+ @Test
public void enableWindowMagnificationWithSameSpec_disabling_NoAnimationAndInvokeCallback()
throws RemoteException {
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
@@ -254,8 +345,7 @@
@Test
public void enableWindowMagnification_enabled_expectedValuesAndInvokeCallback()
throws RemoteException {
- enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
- mAnimationCallback);
+ enableWindowMagnificationWithoutAnimation();
final float targetScale = DEFAULT_SCALE + 1.0f;
final float targetCenterX = DEFAULT_CENTER_X + 100;
final float targetCenterY = DEFAULT_CENTER_Y + 100;
@@ -277,7 +367,6 @@
verifyStartValue(mCenterXCaptor, mCurrentCenterX.get());
verifyStartValue(mCenterYCaptor, mCurrentCenterY.get());
verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
- verify(mAnimationCallback).onResult(false);
verify(mAnimationCallback2).onResult(true);
}
@@ -295,7 +384,7 @@
@Test
public void setScale_enabled_expectedScale() {
- enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
+ enableWindowMagnificationWithoutAnimation();
mInstrumentation.runOnMainSync(
() -> mWindowMagnificationAnimationController.setScale(DEFAULT_SCALE + 1));
@@ -307,13 +396,12 @@
@Test
public void deleteWindowMagnification_enabled_expectedValuesAndInvokeCallback()
throws RemoteException {
- enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
+ enableWindowMagnificationWithoutAnimation();
deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback);
verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
mCenterXCaptor.capture(), mCenterYCaptor.capture());
- verify(mSpyController).deleteWindowMagnification();
verifyStartValue(mScaleCaptor, DEFAULT_SCALE);
verifyStartValue(mCenterXCaptor, Float.NaN);
verifyStartValue(mCenterYCaptor, Float.NaN);
@@ -322,6 +410,15 @@
}
@Test
+ public void deleteWindowMagnificationWithoutCallback_enabled_expectedValues() {
+ enableWindowMagnificationWithoutAnimation();
+
+ deleteWindowMagnificationAndWaitAnimating(0, null);
+
+ verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
+ }
+
+ @Test
public void deleteWindowMagnification_disabled_doNothingAndInvokeCallback()
throws RemoteException {
deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, mAnimationCallback);
@@ -346,10 +443,8 @@
mCurrentCenterY.set(mController.getCenterY());
});
SystemClock.sleep(mWaitingAnimationPeriod);
-
verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
mCenterXCaptor.capture(), mCenterYCaptor.capture());
- verify(mSpyController).deleteWindowMagnification();
//The animation is in verse, so we only check the start values should no be greater than
// the current one.
@@ -363,6 +458,22 @@
}
@Test
+ public void deleteWindowMagnificationWithoutCallback_enabling_expectedValuesAndInvokeCallback()
+ throws RemoteException {
+ enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
+ mAnimationCallback);
+
+ mInstrumentation.runOnMainSync(
+ () -> {
+ Mockito.reset(mSpyController);
+ mWindowMagnificationAnimationController.deleteWindowMagnification(null);
+ });
+
+ verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
+ verify(mAnimationCallback).onResult(false);
+ }
+
+ @Test
public void deleteWindowMagnification_disabling_checkStartAndValues() throws RemoteException {
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
@@ -372,7 +483,6 @@
verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
mCenterXCaptor.capture(), mCenterYCaptor.capture());
- verify(mSpyController).deleteWindowMagnification();
assertEquals(1.0f, mScaleCaptor.getValue(), 0f);
verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
verify(mAnimationCallback).onResult(false);
@@ -380,8 +490,22 @@
}
@Test
- public void moveWindowMagnifier_enabled() {
+ public void deleteWindowMagnificationWithoutCallback_disabling_checkStartAndValues()
+ throws RemoteException {
enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod, null);
+ deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod,
+ mAnimationCallback);
+
+ deleteWindowMagnificationAndWaitAnimating(0, null);
+
+ verify(mSpyController).deleteWindowMagnification();
+ verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
+ verify(mAnimationCallback).onResult(false);
+ }
+
+ @Test
+ public void moveWindowMagnifier_enabled() {
+ enableWindowMagnificationWithoutAnimation();
mInstrumentation.runOnMainSync(
() -> mWindowMagnificationAnimationController.moveWindowMagnifier(100f, 200f));
@@ -411,6 +535,15 @@
assertEquals(expectedCenterY, mController.getCenterY(), 0f);
}
+ private void enableWindowMagnificationWithoutAnimation() {
+ mInstrumentation.runOnMainSync(
+ () -> {
+ Mockito.reset(mSpyController);
+ mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE,
+ DEFAULT_CENTER_X, DEFAULT_CENTER_Y, null);
+ });
+ }
+
private void enableWindowMagnificationAndWaitAnimating(long duration,
@Nullable IRemoteMagnificationAnimationCallback callback) {
mInstrumentation.runOnMainSync(
@@ -485,11 +618,16 @@
}
@Override
+ public void updateSysUIStateFlag() {
+ super.updateSysUIStateFlag();
+ mSpyController.updateSysUIStateFlag();
+ }
+
+ @Override
void onConfigurationChanged(int configDiff) {
super.onConfigurationChanged(configDiff);
mSpyController.onConfigurationChanged(configDiff);
}
-
}
private static ValueAnimator newValueAnimator() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 14a60be..70457cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -469,6 +469,21 @@
}
@Test
+ public void enableWindowMagnificationWithScaleLessThanOne_enabled_disabled() {
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ Float.NaN);
+ });
+
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnification(0.9f, Float.NaN,
+ Float.NaN);
+ });
+
+ assertEquals(Float.NaN, mWindowMagnificationController.getScale(), 0);
+ }
+
+ @Test
public void onLocaleChanged_enabled_updateA11yWindowTitle() {
final String newA11yWindowTitle = "new a11y window title";
mInstrumentation.runOnMainSync(() -> {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagManagerTest.java
index 2fa32ba..6347638 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagManagerTest.java
@@ -53,8 +53,6 @@
public class FeatureFlagManagerTest extends SysuiTestCase {
FeatureFlagManager mFeatureFlagManager;
- @Mock private FlagManager mFlagManager;
- @Mock private SecureSettings mSecureSettings;
@Mock private Context mContext;
@Mock private DumpManager mDumpManager;
@@ -62,14 +60,11 @@
public void setup() {
MockitoAnnotations.initMocks(this);
- mFeatureFlagManager = new FeatureFlagManager(mSecureSettings, mContext, mDumpManager);
+ mFeatureFlagManager = new FeatureFlagManager(mDumpManager);
}
@After
public void onFinished() {
- // SecureSettings and Context are provided for constructor consistency with the
- // debug version of the FeatureFlagManager, but should never be used.
- verifyZeroInteractions(mSecureSettings, mContext);
// The dump manager should be registered with even for the release version, but that's it.
verify(mDumpManager).registerDumpable(anyString(), any());
verifyNoMoreInteractions(mDumpManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsTest.java b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsTest.java
deleted file mode 100644
index 30e9b51..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsTest.java
+++ /dev/null
@@ -1,138 +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.systemui.flags;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.res.Resources;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.stubbing.Answer;
-
-@SmallTest
-public class FeatureFlagsTest extends SysuiTestCase {
-
- @Mock Resources mResources;
- @Mock FlagReader mFeatureFlagReader;
-
- private FeatureFlags mFeatureFlags;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
-
- when(mFeatureFlagReader.isEnabled(anyInt(), anyBoolean())).thenAnswer(
- (Answer<Boolean>) invocation -> invocation.getArgument(1));
-
- mFeatureFlags = new FeatureFlags(mResources, mFeatureFlagReader, getContext());
- }
-
- @Test
- public void testAddListener() {
- Flag<?> flag = new BooleanFlag(1);
- mFeatureFlags.addFlag(flag);
-
- // Assert and capture that a plugin listener was added.
- ArgumentCaptor<FlagReader.Listener> pluginListenerCaptor =
- ArgumentCaptor.forClass(FlagReader.Listener.class);
- verify(mFeatureFlagReader).addListener(pluginListenerCaptor.capture());
- FlagReader.Listener pluginListener = pluginListenerCaptor.getValue();
-
- // Signal a change. No listeners, so no real effect.
- pluginListener.onFlagChanged(flag.getId());
-
- // Add a listener for the flag
- final Flag<?>[] changedFlag = {null};
- FeatureFlags.Listener listener = f -> changedFlag[0] = f;
- mFeatureFlags.addFlagListener(flag, listener);
-
- // No changes seen yet.
- assertThat(changedFlag[0]).isNull();
-
- // Signal a change.
- pluginListener.onFlagChanged(flag.getId());
-
- // Assert that the change was for the correct flag.
- assertThat(changedFlag[0]).isEqualTo(flag);
- }
-
- @Test
- public void testRemoveListener() {
- Flag<?> flag = new BooleanFlag(1);
- mFeatureFlags.addFlag(flag);
-
- // Assert and capture that a plugin listener was added.
- ArgumentCaptor<FlagReader.Listener> pluginListenerCaptor =
- ArgumentCaptor.forClass(FlagReader.Listener.class);
- verify(mFeatureFlagReader).addListener(pluginListenerCaptor.capture());
- FlagReader.Listener pluginListener = pluginListenerCaptor.getValue();
-
- // Add a listener for the flag
- final Flag<?>[] changedFlag = {null};
- FeatureFlags.Listener listener = f -> changedFlag[0] = f;
- mFeatureFlags.addFlagListener(flag, listener);
-
- // Signal a change.
- pluginListener.onFlagChanged(flag.getId());
-
- // Assert that the change was for the correct flag.
- assertThat(changedFlag[0]).isEqualTo(flag);
-
- changedFlag[0] = null;
-
- // Now remove the listener.
- mFeatureFlags.removeFlagListener(flag, listener);
- // Signal a change.
- pluginListener.onFlagChanged(flag.getId());
- // Assert that the change was not triggered
- assertThat(changedFlag[0]).isNull();
- }
-
- @Test
- public void testBooleanDefault() {
- BooleanFlag flag = new BooleanFlag(1, true);
-
- mFeatureFlags.addFlag(flag);
-
- assertThat(mFeatureFlags.isEnabled(flag)).isTrue();
- }
-
- @Test
- public void testBooleanResourceOverlay() {
- int resourceId = 12;
- BooleanFlag flag = new BooleanFlag(1, false, resourceId);
- when(mResources.getBoolean(resourceId)).thenReturn(true);
- when(mResources.getResourceEntryName(resourceId)).thenReturn("flag");
-
- mFeatureFlags.addFlag(flag);
-
- assertThat(mFeatureFlags.isEnabled(flag)).isTrue();
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/idle/IdleHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/idle/IdleHostViewControllerTest.java
index 8ff33a0..3c24a3a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/idle/IdleHostViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/idle/IdleHostViewControllerTest.java
@@ -16,22 +16,16 @@
package com.android.systemui.idle;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.content.BroadcastReceiver;
-import android.content.Intent;
import android.content.res.Resources;
-import android.os.Looper;
import android.os.PowerManager;
import android.testing.AndroidTestingRunner;
-import android.view.Choreographer;
import android.view.View;
import androidx.test.filters.SmallTest;
@@ -40,10 +34,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shared.system.InputChannelCompat;
-import com.android.systemui.shared.system.InputMonitorCompat;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.concurrency.DelayableExecutor;
import org.junit.Before;
import org.junit.Test;
@@ -60,22 +51,14 @@
@Mock private BroadcastDispatcher mBroadcastDispatcher;
@Mock private PowerManager mPowerManager;
@Mock private IdleHostView mIdleHostView;
- @Mock private InputMonitorFactory mInputMonitorFactory;
- @Mock private DelayableExecutor mDelayableExecutor;
@Mock private Resources mResources;
- @Mock private Looper mLooper;
@Mock private Provider<View> mViewProvider;
- @Mock private Choreographer mChoreographer;
@Mock private KeyguardStateController mKeyguardStateController;
@Mock private StatusBarStateController mStatusBarStateController;
- @Mock private DreamHelper mDreamHelper;
- @Mock private InputMonitorCompat mInputMonitor;
- @Mock private InputChannelCompat.InputEventReceiver mInputEventReceiver;
@Mock private AmbientLightModeMonitor mAmbientLightModeMonitor;
private KeyguardStateController.Callback mKeyguardStateCallback;
private StatusBarStateController.StateListener mStatusBarStateListener;
- private IdleHostViewController mController;
@Before
public void setup() {
@@ -83,17 +66,12 @@
when(mResources.getBoolean(R.bool.config_enableIdleMode)).thenReturn(true);
when(mStatusBarStateController.isDozing()).thenReturn(false);
- when(mInputMonitorFactory.getInputMonitor("IdleHostViewController"))
- .thenReturn(mInputMonitor);
- when(mInputMonitor.getInputReceiver(any(), any(), any())).thenReturn(mInputEventReceiver);
- mController = new IdleHostViewController(mContext,
- mBroadcastDispatcher, mPowerManager, mIdleHostView,
- mInputMonitorFactory, mDelayableExecutor, mResources, mLooper, mViewProvider,
- mChoreographer, mKeyguardStateController, mStatusBarStateController, mDreamHelper,
- mAmbientLightModeMonitor);
- mController.init();
- mController.onViewAttached();
+ final IdleHostViewController controller = new IdleHostViewController(mBroadcastDispatcher,
+ mPowerManager, mIdleHostView, mResources, mViewProvider, mKeyguardStateController,
+ mStatusBarStateController, mAmbientLightModeMonitor);
+ controller.init();
+ controller.onViewAttached();
// Captures keyguard state controller callback.
ArgumentCaptor<KeyguardStateController.Callback> keyguardStateCallbackCaptor =
@@ -109,101 +87,6 @@
}
@Test
- public void testTimeoutToIdleMode() {
- // Keyguard showing.
- when(mKeyguardStateController.isShowing()).thenReturn(true);
- mKeyguardStateCallback.onKeyguardShowingChanged();
-
- // Regular ambient lighting.
- final AmbientLightModeMonitor.Callback lightMonitorCallback =
- captureAmbientLightModeMonitorCallback();
- lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT);
-
- // Times out.
- ArgumentCaptor<Runnable> callbackCapture = ArgumentCaptor.forClass(Runnable.class);
- verify(mDelayableExecutor).executeDelayed(callbackCapture.capture(), anyLong());
- callbackCapture.getValue().run();
-
- // Verifies start dreaming (idle mode).
- verify(mDreamHelper).startDreaming(any());
- }
-
- @Test
- public void testTimeoutToLowLightMode() {
- // Keyguard showing.
- when(mKeyguardStateController.isShowing()).thenReturn(true);
- mKeyguardStateCallback.onKeyguardShowingChanged();
-
- // Captures dream broadcast receiver;
- ArgumentCaptor<BroadcastReceiver> dreamBroadcastReceiverCaptor =
- ArgumentCaptor.forClass(BroadcastReceiver.class);
- verify(mBroadcastDispatcher)
- .registerReceiver(dreamBroadcastReceiverCaptor.capture(), any());
- final BroadcastReceiver dreamBroadcastReceiver = dreamBroadcastReceiverCaptor.getValue();
-
- // Low ambient lighting.
- final AmbientLightModeMonitor.Callback lightMonitorCallback =
- captureAmbientLightModeMonitorCallback();
- lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
-
- // Verifies it goes to sleep because of low light.
- verify(mPowerManager).goToSleep(anyLong(), anyInt(), anyInt());
-
- mStatusBarStateListener.onDozingChanged(true /*isDozing*/);
- dreamBroadcastReceiver.onReceive(mContext, new Intent(Intent.ACTION_DREAMING_STARTED));
-
- // User wakes up the device.
- mStatusBarStateListener.onDozingChanged(false /*isDozing*/);
- dreamBroadcastReceiver.onReceive(mContext, new Intent(Intent.ACTION_DREAMING_STOPPED));
-
- // Clears power manager invocations to make sure the below dozing was triggered by the
- // timeout.
- clearInvocations(mPowerManager);
-
- // Times out.
- ArgumentCaptor<Runnable> callbackCapture = ArgumentCaptor.forClass(Runnable.class);
- verify(mDelayableExecutor, atLeastOnce()).executeDelayed(callbackCapture.capture(),
- anyLong());
- callbackCapture.getValue().run();
-
- // Verifies go to sleep (low light mode).
- verify(mPowerManager).goToSleep(anyLong(), anyInt(), anyInt());
- }
-
- @Test
- public void testTransitionBetweenIdleAndLowLightMode() {
- // Keyguard showing.
- when(mKeyguardStateController.isShowing()).thenReturn(true);
- mKeyguardStateCallback.onKeyguardShowingChanged();
-
- // Regular ambient lighting.
- final AmbientLightModeMonitor.Callback lightMonitorCallback =
- captureAmbientLightModeMonitorCallback();
- lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT);
-
- // Times out.
- ArgumentCaptor<Runnable> callbackCapture = ArgumentCaptor.forClass(Runnable.class);
- verify(mDelayableExecutor).executeDelayed(callbackCapture.capture(), anyLong());
- callbackCapture.getValue().run();
-
- // Verifies in idle mode (dreaming).
- verify(mDreamHelper).startDreaming(any());
- clearInvocations(mDreamHelper);
-
- // Ambient lighting becomes dim.
- lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
-
- // Verifies in low light mode (dozing).
- verify(mPowerManager).goToSleep(anyLong(), anyInt(), anyInt());
-
- // Ambient lighting becomes bright again.
- lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT);
-
- // Verifies in idle mode (dreaming).
- verify(mDreamHelper).startDreaming(any());
- }
-
- @Test
public void testStartDozingWhenLowLight() {
// Keyguard showing.
when(mKeyguardStateController.isShowing()).thenReturn(true);
@@ -225,20 +108,22 @@
}
@Test
- public void testInputEventReceiverLifecycle() {
+ public void testWakeUpWhenRegularLight() {
// Keyguard showing.
when(mKeyguardStateController.isShowing()).thenReturn(true);
mKeyguardStateCallback.onKeyguardShowingChanged();
- // Should register input event receiver.
- verify(mInputMonitor).getInputReceiver(any(), any(), any());
+ // In low light / dozing.
+ final AmbientLightModeMonitor.Callback lightMonitorCallback =
+ captureAmbientLightModeMonitorCallback();
+ lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK);
+ mStatusBarStateListener.onDozingChanged(true /*isDozing*/);
- // Keyguard dismissed.
- when(mKeyguardStateController.isShowing()).thenReturn(false);
- mKeyguardStateCallback.onKeyguardShowingChanged();
+ // Regular ambient lighting.
+ lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT);
- // Should dispose input event receiver.
- verify(mInputEventReceiver).dispose();
+ // Verifies it wakes up from sleep.
+ verify(mPowerManager).wakeUp(anyLong(), anyInt(), anyString());
}
// Captures [AmbientLightModeMonitor.Callback] assuming that the ambient light mode monitor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/AnimatableClockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/AnimatableClockControllerTest.java
index df11284..5a4bb86 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/AnimatableClockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/AnimatableClockControllerTest.java
@@ -40,7 +40,6 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.BatteryController;
import org.junit.After;
@@ -69,8 +68,6 @@
@Mock
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
- private KeyguardBypassController mBypassController;
- @Mock
private Resources mResources;
private MockitoSession mStaticMockSession;
@@ -99,7 +96,6 @@
mBroadcastDispatcher,
mBatteryController,
mKeyguardUpdateMonitor,
- mBypassController,
mResources
);
mAnimatableClockController.init();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
index 5e73dbc..d64319b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
@@ -212,6 +212,7 @@
@Test
public void testUpdateFingerprintLocationOnAuthenticatorsRegistered() {
// GIVEN fp sensor location is not available pre-init
+ when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false);
when(mAuthController.getFingerprintSensorLocation()).thenReturn(null);
when(mAuthController.getUdfpsProps()).thenReturn(null);
mLockIconViewController.init();
@@ -232,7 +233,7 @@
}
@Test
- public void testLockIconViewBackgroundEnabledWhenUdfpsIsAvailable() {
+ public void testLockIconViewBackgroundEnabledWhenUdfpsIsSupported() {
// GIVEN Udpfs sensor location is available
setupUdfps();
@@ -247,9 +248,9 @@
}
@Test
- public void testLockIconViewBackgroundDisabledWhenUdfpsIsUnavailable() {
- // GIVEN Udfps sensor location is not available
- when(mAuthController.getUdfpsSensorLocation()).thenReturn(null);
+ public void testLockIconViewBackgroundDisabledWhenUdfpsIsNotSupported() {
+ // GIVEN Udfps sensor location is not supported
+ when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false);
mLockIconViewController.init();
captureAttachListener();
@@ -365,6 +366,7 @@
}
private Pair<Integer, PointF> setupUdfps() {
+ when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true);
final PointF udfpsLocation = new PointF(50, 75);
final int radius = 33;
final FingerprintSensorPropertiesInternal fpProps =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index af624ed..5de4c11 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -182,7 +182,7 @@
@Test
public void testShowImeButton() {
- mCommandQueue.setImeWindowStatus(DEFAULT_DISPLAY, null, 1, 2, true, false);
+ mCommandQueue.setImeWindowStatus(DEFAULT_DISPLAY, null, 1, 2, true);
waitForIdleSync();
verify(mCallbacks).setImeWindowStatus(
eq(DEFAULT_DISPLAY), eq(null), eq(1), eq(2), eq(true));
@@ -193,7 +193,7 @@
// First show in default display to update the "last updated ime display"
testShowImeButton();
- mCommandQueue.setImeWindowStatus(SECONDARY_DISPLAY, null, 1, 2, true, false);
+ mCommandQueue.setImeWindowStatus(SECONDARY_DISPLAY, null, 1, 2, true);
waitForIdleSync();
verify(mCallbacks).setImeWindowStatus(eq(DEFAULT_DISPLAY), eq(null), eq(IME_INVISIBLE),
eq(BACK_DISPOSITION_DEFAULT), eq(false));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 01f7fae..cb0d87a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -701,7 +701,7 @@
// GIVEN fingerprint is also running (not udfps)
when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
- when(mKeyguardUpdateMonitor.isUdfpsAvailable()).thenReturn(false);
+ when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false);
mController.setVisible(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index cf58c63..9a23eb6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -44,7 +44,7 @@
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.inflation.LowPriorityInflationHelper;
+import com.android.systemui.statusbar.notification.collection.legacy.LowPriorityInflationHelper;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
@@ -103,7 +103,6 @@
when(mVisualStabilityManager.areGroupChangesAllowed()).thenReturn(true);
when(mVisualStabilityManager.isReorderingAllowed()).thenReturn(true);
- when(mFeatureFlags.isNewNotifPipelineEnabled()).thenReturn(false);
when(mFeatureFlags.checkLegacyPipelineEnabled()).thenReturn(true);
mHelper = new NotificationTestHelper(mContext, mDependency, TestableLooper.get(this));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 902d115..f8effa1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -192,7 +192,6 @@
mEntry = createNotification();
mSbn = mEntry.getSbn();
- when(mFeatureFlags.isNewNotifPipelineEnabled()).thenReturn(false);
when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
mEntryManager = new NotificationEntryManager(
mLogger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index f08a74a..cf90cef6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -145,7 +145,6 @@
allowTestableLooperAsMainThread();
when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(true);
- when(mFeatureFlags.isNewNotifPipelineEnabled()).thenReturn(true);
when(mEulogizer.record(any(Exception.class))).thenAnswer(i -> i.getArguments()[0]);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
index fc94262..b91f7e6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
@@ -30,6 +30,7 @@
import com.android.internal.logging.InstanceId;
import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.SbnBuilder;
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection;
import com.android.systemui.util.time.FakeSystemClock;
import java.util.ArrayList;
@@ -53,6 +54,7 @@
/* ListEntry properties */
private GroupEntry mParent;
+ private NotifSection mNotifSection;
/* If set, use this creation time instead of mClock.uptimeMillis */
private long mCreationTime = -1;
@@ -71,6 +73,11 @@
mCreationTime = source.getCreationTime();
}
+ /** Update an the parent on an existing entry */
+ public static void setNewParent(NotificationEntry entry, GroupEntry parent) {
+ entry.setParent(parent);
+ }
+
/** Build a new instance of NotificationEntry */
public NotificationEntry build() {
return buildOrApply(null);
@@ -103,6 +110,7 @@
/* ListEntry properties */
entry.setParent(mParent);
+ entry.getAttachState().setSection(mNotifSection);
entry.getAttachState().setStableIndex(mStableIndex);
return entry;
}
@@ -116,6 +124,14 @@
}
/**
+ * Sets the parent.
+ */
+ public NotificationEntryBuilder setSection(@Nullable NotifSection section) {
+ mNotifSection = section;
+ return this;
+ }
+
+ /**
* Sets the SBN directly. If set, causes all calls to delegated SbnBuilder methods to be
* ignored.
*/
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
index 913ffd4..b254ed4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
@@ -18,6 +18,8 @@
import static com.android.systemui.statusbar.notification.collection.ListDumper.dumpTree;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -44,7 +46,7 @@
import android.testing.TestableLooper;
import android.util.ArrayMap;
-import androidx.annotation.Nullable;
+import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -79,10 +81,11 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
@SmallTest
@@ -124,7 +127,7 @@
mListBuilder.attach(mNotifCollection);
- mStabilityManager = new TestableStabilityManager();
+ mStabilityManager = spy(new TestableStabilityManager());
mListBuilder.setNotifStabilityManager(mStabilityManager);
Mockito.verify(mNotifCollection).setBuildListener(mBuildListenerCaptor.capture());
@@ -618,26 +621,53 @@
@Test
public void testNotifSectionsChildrenUpdated() {
- AtomicBoolean validChildren = new AtomicBoolean(false);
+ ArrayList<ListEntry> pkg1Entries = new ArrayList<>();
+ ArrayList<ListEntry> pkg2Entries = new ArrayList<>();
+ ArrayList<ListEntry> pkg3Entries = new ArrayList<>();
final NotifSectioner pkg1Sectioner = spy(new PackageSectioner(PACKAGE_1) {
- @Nullable
@Override
public void onEntriesUpdated(List<ListEntry> entries) {
super.onEntriesUpdated(entries);
- validChildren.set(entries.size() == 2);
+ pkg1Entries.addAll(entries);
}
});
- mListBuilder.setSectioners(asList(pkg1Sectioner));
+ final NotifSectioner pkg2Sectioner = spy(new PackageSectioner(PACKAGE_2) {
+ @Override
+ public void onEntriesUpdated(List<ListEntry> entries) {
+ super.onEntriesUpdated(entries);
+ pkg2Entries.addAll(entries);
+ }
+ });
+ final NotifSectioner pkg3Sectioner = spy(new PackageSectioner(PACKAGE_3) {
+ @Override
+ public void onEntriesUpdated(List<ListEntry> entries) {
+ super.onEntriesUpdated(entries);
+ pkg3Entries.addAll(entries);
+ }
+ });
+ mListBuilder.setSectioners(asList(pkg1Sectioner, pkg2Sectioner, pkg3Sectioner));
- addNotif(0, PACKAGE_4);
+ addNotif(0, PACKAGE_1);
addNotif(1, PACKAGE_1);
- addNotif(2, PACKAGE_1);
+ addNotif(2, PACKAGE_3);
addNotif(3, PACKAGE_3);
+ addNotif(4, PACKAGE_3);
dispatchBuild();
- verify(pkg1Sectioner, times(1)).onEntriesUpdated(any());
- assertTrue(validChildren.get());
+ verify(pkg1Sectioner).onEntriesUpdated(any());
+ verify(pkg2Sectioner).onEntriesUpdated(any());
+ verify(pkg3Sectioner).onEntriesUpdated(any());
+ assertThat(pkg1Entries).containsExactly(
+ mEntrySet.get(0),
+ mEntrySet.get(1)
+ ).inOrder();
+ assertThat(pkg2Entries).isEmpty();
+ assertThat(pkg3Entries).containsExactly(
+ mEntrySet.get(2),
+ mEntrySet.get(3),
+ mEntrySet.get(4)
+ ).inOrder();
}
@Test
@@ -835,13 +865,13 @@
.onBeforeTransformGroups(anyList());
inOrder.verify(promoter, atLeastOnce())
.shouldPromoteToTopLevel(any(NotificationEntry.class));
- inOrder.verify(mOnBeforeFinalizeFilterListener).onBeforeFinalizeFilter(anyList());
- inOrder.verify(preRenderFilter, atLeastOnce())
- .shouldFilterOut(any(NotificationEntry.class), anyLong());
inOrder.verify(mOnBeforeSortListener).onBeforeSort(anyList());
inOrder.verify(section, atLeastOnce()).isInSection(any(ListEntry.class));
inOrder.verify(comparator, atLeastOnce())
.compare(any(ListEntry.class), any(ListEntry.class));
+ inOrder.verify(mOnBeforeFinalizeFilterListener).onBeforeFinalizeFilter(anyList());
+ inOrder.verify(preRenderFilter, atLeastOnce())
+ .shouldFilterOut(any(NotificationEntry.class), anyLong());
inOrder.verify(mOnBeforeRenderListListener).onBeforeRenderList(anyList());
inOrder.verify(mOnRenderListListener).onRenderList(anyList());
}
@@ -947,13 +977,11 @@
);
// THEN all the new notifs, including the new GroupEntry, are passed to the listener
- assertEquals(
- asList(
- mEntrySet.get(0),
- mBuiltList.get(1),
- mEntrySet.get(4)),
- listener.mEntriesReceived
- );
+ assertThat(listener.mEntriesReceived).containsExactly(
+ mEntrySet.get(0),
+ mBuiltList.get(1),
+ mEntrySet.get(4)
+ ).inOrder(); // Order is a bonus because this listener is before sort
}
@Test
@@ -993,14 +1021,12 @@
);
// THEN all the new notifs, including the new GroupEntry, are passed to the listener
- assertEquals(
- asList(
- mEntrySet.get(0),
- mBuiltList.get(2),
- mEntrySet.get(7),
- mEntrySet.get(1)),
- listener.mEntriesReceived
- );
+ assertThat(listener.mEntriesReceived).containsExactly(
+ mEntrySet.get(0),
+ mEntrySet.get(1),
+ mBuiltList.get(2),
+ mEntrySet.get(7)
+ ).inOrder(); // Order is a bonus because this listener is before sort
}
@Test
@@ -1091,9 +1117,93 @@
}
@Test
+ public void testFinalizeFilteringGroupSummaryDoesNotBreakSort() {
+ // GIVEN children from 3 packages, with one in the middle of the sort order being a group
+ addNotif(0, PACKAGE_1);
+ addNotif(1, PACKAGE_2);
+ addNotif(2, PACKAGE_3);
+ addNotif(3, PACKAGE_1);
+ addNotif(4, PACKAGE_2);
+ addNotif(5, PACKAGE_3);
+ addGroupSummary(6, PACKAGE_2, GROUP_1);
+ addGroupChild(7, PACKAGE_2, GROUP_1);
+ addGroupChild(8, PACKAGE_2, GROUP_1);
+
+ // GIVEN that they should be sorted by package
+ mListBuilder.setComparators(asList(
+ new HypeComparator(PACKAGE_1),
+ new HypeComparator(PACKAGE_2),
+ new HypeComparator(PACKAGE_3)
+ ));
+
+ // WHEN a finalize filter removes the summary
+ mListBuilder.addFinalizeFilter(new NotifFilter("Test") {
+ @Override
+ public boolean shouldFilterOut(@NonNull NotificationEntry entry, long now) {
+ return entry == notif(6).entry;
+ }
+ });
+
+ dispatchBuild();
+
+ // THEN the notifications remain ordered by package, even though the children were promoted
+ verifyBuiltList(
+ notif(0),
+ notif(3),
+ notif(1),
+ notif(4),
+ notif(7), // promoted child
+ notif(8), // promoted child
+ notif(2),
+ notif(5)
+ );
+ }
+
+ @Test
+ public void testFinalizeFilteringGroupChildDoesNotBreakSort() {
+ // GIVEN children from 3 packages, with one in the middle of the sort order being a group
+ addNotif(0, PACKAGE_1);
+ addNotif(1, PACKAGE_2);
+ addNotif(2, PACKAGE_3);
+ addNotif(3, PACKAGE_1);
+ addNotif(4, PACKAGE_2);
+ addNotif(5, PACKAGE_3);
+ addGroupSummary(6, PACKAGE_2, GROUP_1);
+ addGroupChild(7, PACKAGE_2, GROUP_1);
+ addGroupChild(8, PACKAGE_2, GROUP_1);
+
+ // GIVEN that they should be sorted by package
+ mListBuilder.setComparators(asList(
+ new HypeComparator(PACKAGE_1),
+ new HypeComparator(PACKAGE_2),
+ new HypeComparator(PACKAGE_3)
+ ));
+
+ // WHEN a finalize filter one of the 2 children from a group
+ mListBuilder.addFinalizeFilter(new NotifFilter("Test") {
+ @Override
+ public boolean shouldFilterOut(@NonNull NotificationEntry entry, long now) {
+ return entry == notif(7).entry;
+ }
+ });
+
+ dispatchBuild();
+
+ // THEN the notifications remain ordered by package, even though the children were promoted
+ verifyBuiltList(
+ notif(0),
+ notif(3),
+ notif(1),
+ notif(4),
+ notif(8), // promoted child
+ notif(2),
+ notif(5)
+ );
+ }
+
+ @Test
public void testBrokenGroupNotificationOrdering() {
// GIVEN two group children with different sections & without a summary yet
-
addGroupChild(0, PACKAGE_2, GROUP_1);
addNotif(1, PACKAGE_1);
addGroupChild(2, PACKAGE_2, GROUP_1);
@@ -1224,13 +1334,11 @@
dispatchBuild();
// THEN all the new notifs are passed to the listener out of order
- assertEquals(
- asList(
- mEntrySet.get(0),
- mEntrySet.get(1),
- mEntrySet.get(2)),
- listener.mEntriesReceived
- );
+ assertThat(listener.mEntriesReceived).containsExactly(
+ mEntrySet.get(0),
+ mEntrySet.get(1),
+ mEntrySet.get(2)
+ ).inOrder(); // Checking out-of-order input to validate sorted output
// THEN the final list is in order
verifyBuiltList(
@@ -1256,13 +1364,11 @@
dispatchBuild();
// THEN all the new notifs are passed to the listener
- assertEquals(
- asList(
- mEntrySet.get(0),
- mEntrySet.get(1),
- mEntrySet.get(2)),
- listener.mEntriesReceived
- );
+ assertThat(listener.mEntriesReceived).containsExactly(
+ mEntrySet.get(0),
+ mEntrySet.get(1),
+ mEntrySet.get(2)
+ ).inOrder();
}
@Test
@@ -1365,6 +1471,7 @@
assertOrder("ABCDEFG", "ACDEFBG", "ABCDEFG"); // no change
assertOrder("ABCDEFG", "ACDEFBXZG", "XZABCDEFG"); // Z and X
assertOrder("ABCDEFG", "AXCDEZFBG", "XZABCDEFG"); // Z and X + gap
+ verify(mStabilityManager, times(4)).onEntryReorderSuppressed();
}
@Test
@@ -1373,6 +1480,7 @@
assertOrder("ABCDEFG", "ACDEFBG", "ACDEFBG"); // no change
assertOrder("ABCDEFG", "ACDEFBXZG", "ACDEFBXZG"); // Z and X
assertOrder("ABCDEFG", "AXCDEZFBG", "AXCDEZFBG"); // Z and X + gap
+ verify(mStabilityManager, never()).onEntryReorderSuppressed();
}
@Test
@@ -1410,6 +1518,26 @@
// THEN no exception thrown
}
+ @Test
+ public void testIsSorted() {
+ Comparator<Integer> intCmp = Integer::compare;
+ assertTrue(ShadeListBuilder.isSorted(Collections.emptyList(), intCmp));
+ assertTrue(ShadeListBuilder.isSorted(Collections.singletonList(1), intCmp));
+ assertTrue(ShadeListBuilder.isSorted(Arrays.asList(1, 2), intCmp));
+ assertTrue(ShadeListBuilder.isSorted(Arrays.asList(1, 2, 3), intCmp));
+ assertTrue(ShadeListBuilder.isSorted(Arrays.asList(1, 2, 3, 4), intCmp));
+ assertTrue(ShadeListBuilder.isSorted(Arrays.asList(1, 2, 3, 4, 5), intCmp));
+ assertTrue(ShadeListBuilder.isSorted(Arrays.asList(1, 1, 1, 1, 1), intCmp));
+ assertTrue(ShadeListBuilder.isSorted(Arrays.asList(1, 1, 2, 2, 3, 3), intCmp));
+
+ assertFalse(ShadeListBuilder.isSorted(Arrays.asList(2, 1), intCmp));
+ assertFalse(ShadeListBuilder.isSorted(Arrays.asList(2, 1, 2), intCmp));
+ assertFalse(ShadeListBuilder.isSorted(Arrays.asList(1, 2, 1), intCmp));
+ assertFalse(ShadeListBuilder.isSorted(Arrays.asList(1, 2, 3, 2, 5), intCmp));
+ assertFalse(ShadeListBuilder.isSorted(Arrays.asList(5, 2, 3, 4, 5), intCmp));
+ assertFalse(ShadeListBuilder.isSorted(Arrays.asList(1, 2, 3, 4, 1), intCmp));
+ }
+
/**
* Adds a notif to the collection that will be passed to the list builder when
* {@link #dispatchBuild()}s is called.
@@ -1815,6 +1943,15 @@
public boolean isEntryReorderingAllowed(ListEntry entry) {
return mAllowEntryReodering;
}
+
+ @Override
+ public boolean isEveryChangeAllowed() {
+ return mAllowEntryReodering && mAllowGroupChanges && mAllowSectionChanges;
+ }
+
+ @Override
+ public void onEntryReorderSuppressed() {
+ }
}
private static final String PACKAGE_1 = "com.test1";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
index bec5174..c3e10aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
@@ -26,6 +26,7 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import static java.util.Objects.requireNonNull;
@@ -33,10 +34,12 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder;
import com.android.systemui.statusbar.notification.collection.ListEntry;
@@ -44,8 +47,11 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
+import com.android.systemui.statusbar.notification.collection.inflation.NotifUiAdjustmentProvider;
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.render.NotifViewBarn;
import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager;
@@ -60,6 +66,7 @@
import org.mockito.Spy;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -79,24 +86,36 @@
@Captor private ArgumentCaptor<NotifCollectionListener> mCollectionListenerCaptor;
@Captor private ArgumentCaptor<OnBeforeFinalizeFilterListener> mBeforeFilterListenerCaptor;
@Captor private ArgumentCaptor<NotifInflater.InflationCallback> mCallbackCaptor;
+ @Captor private ArgumentCaptor<NotifInflater.Params> mParamsCaptor;
+ @Mock private NotifSectioner mNotifSectioner;
+ @Mock private NotifSection mNotifSection;
@Mock private NotifPipeline mNotifPipeline;
@Mock private IStatusBarService mService;
@Spy private FakeNotifInflater mNotifInflater = new FakeNotifInflater();
+ private final TestableAdjustmentProvider mAdjustmentProvider = new TestableAdjustmentProvider();
+
+ @NonNull
+ private NotificationEntryBuilder getNotificationEntryBuilder() {
+ return new NotificationEntryBuilder().setSection(mNotifSection);
+ }
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mEntry = new NotificationEntryBuilder().setParent(ROOT_ENTRY).build();
+ mEntry = getNotificationEntryBuilder().setParent(ROOT_ENTRY).build();
mInflationError = new Exception(TEST_MESSAGE);
mErrorManager = new NotifInflationErrorManager();
+ when(mNotifSection.getSectioner()).thenReturn(mNotifSectioner);
+ mAdjustmentProvider.setSectionIsLowPriority(false);
PreparationCoordinator coordinator = new PreparationCoordinator(
mock(PreparationCoordinatorLogger.class),
mNotifInflater,
mErrorManager,
mock(NotifViewBarn.class),
+ mAdjustmentProvider,
mService,
TEST_CHILD_BIND_CUTOFF,
TEST_MAX_GROUP_DELAY);
@@ -150,7 +169,7 @@
mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
// THEN we inflate it
- verify(mNotifInflater).inflateViews(eq(mEntry), any());
+ verify(mNotifInflater).inflateViews(eq(mEntry), any(), any());
// THEN we filter it out until it's done inflating.
assertTrue(mUninflatedFilter.shouldFilterOut(mEntry, 0));
@@ -161,7 +180,7 @@
// GIVEN an inflated notification
mCollectionListener.onEntryAdded(mEntry);
mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
- verify(mNotifInflater).inflateViews(eq(mEntry), mCallbackCaptor.capture());
+ verify(mNotifInflater).inflateViews(eq(mEntry), any(), mCallbackCaptor.capture());
mCallbackCaptor.getValue().onInflationFinished(mEntry);
// WHEN notification is updated
@@ -169,7 +188,90 @@
mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
// THEN we rebind it
- verify(mNotifInflater).rebindViews(eq(mEntry), any());
+ verify(mNotifInflater).rebindViews(eq(mEntry), any(), any());
+
+ // THEN we do not filter it because it's not the first inflation.
+ assertFalse(mUninflatedFilter.shouldFilterOut(mEntry, 0));
+ }
+
+ @Test
+ public void testEntrySmartReplyAdditionWillRebindViews() {
+ // GIVEN an inflated notification
+ mCollectionListener.onEntryAdded(mEntry);
+ mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
+ verify(mNotifInflater).inflateViews(eq(mEntry), any(), mCallbackCaptor.capture());
+ mCallbackCaptor.getValue().onInflationFinished(mEntry);
+
+ // WHEN notification ranking now has smart replies
+ mEntry.setRanking(new RankingBuilder(mEntry.getRanking()).setSmartReplies("yes").build());
+ mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
+
+ // THEN we rebind it
+ verify(mNotifInflater).rebindViews(eq(mEntry), any(), any());
+
+ // THEN we do not filter it because it's not the first inflation.
+ assertFalse(mUninflatedFilter.shouldFilterOut(mEntry, 0));
+ }
+
+ @Test
+ public void testEntryChangedToMinimizedSectionWillRebindViews() {
+ // GIVEN an inflated notification
+ mCollectionListener.onEntryAdded(mEntry);
+ mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
+ verify(mNotifInflater).inflateViews(eq(mEntry),
+ mParamsCaptor.capture(), mCallbackCaptor.capture());
+ assertFalse(mParamsCaptor.getValue().isLowPriority());
+ mCallbackCaptor.getValue().onInflationFinished(mEntry);
+
+ // WHEN notification moves to a min priority section
+ mAdjustmentProvider.setSectionIsLowPriority(true);
+ mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
+
+ // THEN we rebind it
+ verify(mNotifInflater).rebindViews(eq(mEntry), mParamsCaptor.capture(), any());
+ assertTrue(mParamsCaptor.getValue().isLowPriority());
+
+ // THEN we do not filter it because it's not the first inflation.
+ assertFalse(mUninflatedFilter.shouldFilterOut(mEntry, 0));
+ }
+
+ @Test
+ public void testMinimizedEntryMovedIntoGroupWillRebindViews() {
+ // GIVEN an inflated, minimized notification
+ mAdjustmentProvider.setSectionIsLowPriority(true);
+ mCollectionListener.onEntryAdded(mEntry);
+ mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
+ verify(mNotifInflater).inflateViews(eq(mEntry),
+ mParamsCaptor.capture(), mCallbackCaptor.capture());
+ assertTrue(mParamsCaptor.getValue().isLowPriority());
+ mCallbackCaptor.getValue().onInflationFinished(mEntry);
+
+ // WHEN notification is moved under a parent
+ NotificationEntryBuilder.setNewParent(mEntry, mock(GroupEntry.class));
+ mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
+
+ // THEN we rebind it as not-minimized
+ verify(mNotifInflater).rebindViews(eq(mEntry), mParamsCaptor.capture(), any());
+ assertFalse(mParamsCaptor.getValue().isLowPriority());
+
+ // THEN we do not filter it because it's not the first inflation.
+ assertFalse(mUninflatedFilter.shouldFilterOut(mEntry, 0));
+ }
+
+ @Test
+ public void testEntryRankChangeWillNotRebindViews() {
+ // GIVEN an inflated notification
+ mCollectionListener.onEntryAdded(mEntry);
+ mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
+ verify(mNotifInflater).inflateViews(eq(mEntry), any(), mCallbackCaptor.capture());
+ mCallbackCaptor.getValue().onInflationFinished(mEntry);
+
+ // WHEN notification ranking changes rank, which does not affect views
+ mEntry.setRanking(new RankingBuilder(mEntry.getRanking()).setRank(100).build());
+ mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
+
+ // THEN we do not rebind it
+ verify(mNotifInflater, never()).rebindViews(eq(mEntry), any(), any());
// THEN we do not filter it because it's not the first inflation.
assertFalse(mUninflatedFilter.shouldFilterOut(mEntry, 0));
@@ -180,7 +282,7 @@
// GIVEN an inflated notification
mCollectionListener.onEntryAdded(mEntry);
mBeforeFilterListener.onBeforeFinalizeFilter(List.of(mEntry));
- verify(mNotifInflater).inflateViews(eq(mEntry), mCallbackCaptor.capture());
+ verify(mNotifInflater).inflateViews(eq(mEntry), any(), mCallbackCaptor.capture());
mCallbackCaptor.getValue().onInflationFinished(mEntry);
// THEN it isn't filtered from shade list
@@ -191,13 +293,13 @@
public void testCutoffGroupChildrenNotInflated() {
// WHEN there is a new notification group is posted
int id = 0;
- NotificationEntry summary = new NotificationEntryBuilder()
+ NotificationEntry summary = getNotificationEntryBuilder()
.setOverrideGroupKey(TEST_GROUP_KEY)
.setId(id++)
.build();
List<NotificationEntry> children = new ArrayList<>();
for (int i = 0; i < TEST_CHILD_BIND_CUTOFF + 1; i++) {
- NotificationEntry child = new NotificationEntryBuilder()
+ NotificationEntry child = getNotificationEntryBuilder()
.setOverrideGroupKey(TEST_GROUP_KEY)
.setId(id++)
.build();
@@ -224,9 +326,9 @@
// THEN we inflate up to the cut-off only
for (int i = 0; i < children.size(); i++) {
if (i < TEST_CHILD_BIND_CUTOFF) {
- verify(mNotifInflater).inflateViews(eq(children.get(i)), any());
+ verify(mNotifInflater).inflateViews(eq(children.get(i)), any(), any());
} else {
- verify(mNotifInflater, never()).inflateViews(eq(children.get(i)), any());
+ verify(mNotifInflater, never()).inflateViews(eq(children.get(i)), any(), any());
}
}
}
@@ -236,9 +338,9 @@
// GIVEN a newly-posted group with a summary and two children
final GroupEntry group = new GroupEntryBuilder()
.setCreationTime(400)
- .setSummary(new NotificationEntryBuilder().setId(1).build())
- .addChild(new NotificationEntryBuilder().setId(2).build())
- .addChild(new NotificationEntryBuilder().setId(3).build())
+ .setSummary(getNotificationEntryBuilder().setId(1).build())
+ .addChild(getNotificationEntryBuilder().setId(2).build())
+ .addChild(getNotificationEntryBuilder().setId(3).build())
.build();
fireAddEvents(List.of(group));
final NotificationEntry child0 = group.getChildren().get(0);
@@ -256,9 +358,9 @@
// GIVEN a newly-posted group with a summary and two children
final GroupEntry group = new GroupEntryBuilder()
.setCreationTime(400)
- .setSummary(new NotificationEntryBuilder().setId(1).build())
- .addChild(new NotificationEntryBuilder().setId(2).build())
- .addChild(new NotificationEntryBuilder().setId(3).build())
+ .setSummary(getNotificationEntryBuilder().setId(1).build())
+ .addChild(getNotificationEntryBuilder().setId(2).build())
+ .addChild(getNotificationEntryBuilder().setId(3).build())
.build();
fireAddEvents(List.of(group));
final NotificationEntry summary = group.getSummary();
@@ -281,9 +383,9 @@
// GIVEN a newly-posted group with a summary and two children
final GroupEntry group = new GroupEntryBuilder()
.setCreationTime(400)
- .setSummary(new NotificationEntryBuilder().setId(1).build())
- .addChild(new NotificationEntryBuilder().setId(2).build())
- .addChild(new NotificationEntryBuilder().setId(3).build())
+ .setSummary(getNotificationEntryBuilder().setId(1).build())
+ .addChild(getNotificationEntryBuilder().setId(2).build())
+ .addChild(getNotificationEntryBuilder().setId(3).build())
.build();
fireAddEvents(List.of(group));
final NotificationEntry summary = group.getSummary();
@@ -307,9 +409,9 @@
// GIVEN a newly-posted group with a summary and two children
final GroupEntry group = new GroupEntryBuilder()
.setCreationTime(400)
- .setSummary(new NotificationEntryBuilder().setId(1).build())
- .addChild(new NotificationEntryBuilder().setId(2).build())
- .addChild(new NotificationEntryBuilder().setId(3).build())
+ .setSummary(getNotificationEntryBuilder().setId(1).build())
+ .addChild(getNotificationEntryBuilder().setId(2).build())
+ .addChild(getNotificationEntryBuilder().setId(3).build())
.build();
fireAddEvents(List.of(group));
final NotificationEntry child0 = group.getChildren().get(0);
@@ -324,19 +426,21 @@
}
private static class FakeNotifInflater implements NotifInflater {
- private Map<NotificationEntry, InflationCallback> mInflateCallbacks = new HashMap<>();
+ private final Map<NotificationEntry, InflationCallback> mInflateCallbacks = new HashMap<>();
@Override
- public void inflateViews(NotificationEntry entry, InflationCallback callback) {
+ public void inflateViews(@NonNull NotificationEntry entry, @NonNull Params params,
+ @NonNull InflationCallback callback) {
mInflateCallbacks.put(entry, callback);
}
@Override
- public void rebindViews(NotificationEntry entry, InflationCallback callback) {
+ public void rebindViews(@NonNull NotificationEntry entry, @NonNull Params params,
+ @NonNull InflationCallback callback) {
}
@Override
- public void abortInflation(NotificationEntry entry) {
+ public void abortInflation(@NonNull NotificationEntry entry) {
}
public InflationCallback getInflateCallback(NotificationEntry entry) {
@@ -365,4 +469,12 @@
private static final String TEST_GROUP_KEY = "TEST_GROUP_KEY";
private static final int TEST_CHILD_BIND_CUTOFF = 9;
private static final int TEST_MAX_GROUP_DELAY = 100;
+
+ private class TestableAdjustmentProvider extends NotifUiAdjustmentProvider {
+ private void setSectionIsLowPriority(boolean lowPriority) {
+ setLowPrioritySections(lowPriority
+ ? Collections.singleton(mNotifSection.getSectioner())
+ : Collections.emptyList());
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
index 2091cf8..abe33aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
@@ -19,6 +19,7 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
@@ -28,8 +29,7 @@
import static org.mockito.Mockito.when;
import android.app.Notification;
-import android.service.notification.NotificationListenerService;
-import android.service.notification.StatusBarNotification;
+import android.app.NotificationManager;
import android.testing.AndroidTestingRunner;
import androidx.annotation.Nullable;
@@ -38,10 +38,12 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.RankingBuilder;
+import com.android.systemui.statusbar.SbnBuilder;
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.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.inflation.NotifUiAdjustmentProvider;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
@@ -54,7 +56,6 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
-import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
@@ -66,12 +67,11 @@
@Mock private StatusBarStateController mStatusBarStateController;
@Mock private HighPriorityProvider mHighPriorityProvider;
+ @Mock private NotifUiAdjustmentProvider mAdjustmentProvider;
@Mock private NotifPipeline mNotifPipeline;
@Mock private NodeController mAlertingHeaderController;
@Mock private NodeController mSilentNodeController;
@Mock private SectionHeaderController mSilentHeaderController;
- @Mock private NotificationListenerService.Ranking mRanking;
- @Mock private StatusBarNotification mSbn;
@Captor private ArgumentCaptor<NotifFilter> mNotifFilterCaptor;
@@ -89,12 +89,14 @@
public void setup() {
MockitoAnnotations.initMocks(this);
mRankingCoordinator = new RankingCoordinator(
- mStatusBarStateController, mHighPriorityProvider, mAlertingHeaderController,
- mSilentHeaderController, mSilentNodeController);
+ mStatusBarStateController,
+ mHighPriorityProvider,
+ mAdjustmentProvider,
+ mAlertingHeaderController,
+ mSilentHeaderController,
+ mSilentNodeController);
mEntry = spy(new NotificationEntryBuilder().build());
- mRanking = spy(getRankingForUnfilteredNotif().build());
- mEntry.setRanking(mRanking);
- when(mEntry.getSbn()).thenReturn(mSbn);
+ mEntry.setRanking(getRankingForUnfilteredNotif().build());
mRankingCoordinator.attach(mNotifPipeline);
verify(mNotifPipeline, times(2)).addPreGroupFilter(mNotifFilterCaptor.capture());
@@ -109,23 +111,19 @@
@Test
public void testSilentHeaderClearableChildrenUpdate() {
- StatusBarNotification sbn = Mockito.mock(StatusBarNotification.class);
- Mockito.doReturn("key").when(sbn).getKey();
- Mockito.doReturn(Mockito.mock(Notification.class)).when(sbn).getNotification();
- NotificationEntry entry = new NotificationEntryBuilder().setSbn(sbn).build();
- ListEntry listEntry = new ListEntry("key", 0L) {
+ ListEntry listEntry = new ListEntry(mEntry.getKey(), 0L) {
@Nullable
@Override
public NotificationEntry getRepresentativeEntry() {
- return entry;
+ return mEntry;
}
};
- Mockito.doReturn(true).when(sbn).isClearable();
+ setRankingAmbient(false);
+ setSbnClearable(true);
mSilentSectioner.onEntriesUpdated(Arrays.asList(listEntry));
- when(mRanking.isAmbient()).thenReturn(false);
verify(mSilentHeaderController).setClearSectionEnabled(eq(true));
- mRankingCoordinator.resetClearAllFlags();
- Mockito.doReturn(false).when(sbn).isClearable();
+
+ setSbnClearable(false);
mSilentSectioner.onEntriesUpdated(Arrays.asList(listEntry));
verify(mSilentHeaderController).setClearSectionEnabled(eq(false));
}
@@ -204,7 +202,7 @@
public void testIncludeInSectionSilent() {
// GIVEN the entry isn't high priority
when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
- when(mRanking.isAmbient()).thenReturn(false);
+ setRankingAmbient(false);
// THEN entry is in the silent section
assertFalse(mAlertingSectioner.isInSection(mEntry));
@@ -213,24 +211,23 @@
@Test
public void testMinSection() {
- when(mEntry.getRanking()).thenReturn(mRanking);
when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
- when(mRanking.isAmbient()).thenReturn(true);
+ setRankingAmbient(true);
assertInSection(mEntry, mMinimizedSectioner);
}
@Test
public void testSilentSection() {
when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
- when(mRanking.isAmbient()).thenReturn(false);
+ setRankingAmbient(false);
assertInSection(mEntry, mSilentSectioner);
}
@Test
public void testClearableSilentSection() {
when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
- when(mSbn.isClearable()).thenReturn(true);
- when(mRanking.isAmbient()).thenReturn(false);
+ setSbnClearable(true);
+ setRankingAmbient(false);
mSilentSectioner.onEntriesUpdated(Arrays.asList(mEntry));
verify(mSilentHeaderController).setClearSectionEnabled(eq(true));
}
@@ -238,17 +235,17 @@
@Test
public void testClearableMinimizedSection() {
when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
- when(mSbn.isClearable()).thenReturn(true);
- when(mRanking.isAmbient()).thenReturn(true);
+ setSbnClearable(true);
+ setRankingAmbient(true);
mMinimizedSectioner.onEntriesUpdated(Arrays.asList(mEntry));
verify(mSilentHeaderController).setClearSectionEnabled(eq(true));
}
@Test
public void testNotClearableSilentSection() {
- when(mSbn.isClearable()).thenReturn(false);
+ setSbnClearable(false);
when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
- when(mRanking.isAmbient()).thenReturn(false);
+ setRankingAmbient(false);
mSilentSectioner.onEntriesUpdated(Arrays.asList(mEntry));
mMinimizedSectioner.onEntriesUpdated(Arrays.asList(mEntry));
mAlertingSectioner.onEntriesUpdated(Arrays.asList(mEntry));
@@ -257,9 +254,9 @@
@Test
public void testNotClearableMinimizedSection() {
- when(mSbn.isClearable()).thenReturn(false);
+ setSbnClearable(false);
when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
- when(mRanking.isAmbient()).thenReturn(true);
+ setRankingAmbient(true);
mSilentSectioner.onEntriesUpdated(Arrays.asList(mEntry));
mMinimizedSectioner.onEntriesUpdated(Arrays.asList(mEntry));
mAlertingSectioner.onEntriesUpdated(Arrays.asList(mEntry));
@@ -277,9 +274,24 @@
}
private RankingBuilder getRankingForUnfilteredNotif() {
- return new RankingBuilder()
- .setKey(mEntry.getKey())
+ return new RankingBuilder(mEntry.getRanking())
.setSuppressedVisualEffects(0)
.setSuspended(false);
}
+
+ private void setSbnClearable(boolean clearable) {
+ mEntry.setSbn(new SbnBuilder(mEntry.getSbn())
+ .setFlag(mContext, Notification.FLAG_NO_CLEAR, !clearable)
+ .build());
+ assertEquals(clearable, mEntry.getSbn().isClearable());
+ }
+
+ private void setRankingAmbient(boolean ambient) {
+ mEntry.setRanking(new RankingBuilder(mEntry.getRanking())
+ .setImportance(ambient
+ ? NotificationManager.IMPORTANCE_MIN
+ : NotificationManager.IMPORTANCE_DEFAULT)
+ .build());
+ assertEquals(ambient, mEntry.getRanking().isAmbient());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
index 4edca7d..5df1d28 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
@@ -19,8 +19,10 @@
import static junit.framework.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -30,6 +32,7 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -37,7 +40,6 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
-import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -57,9 +59,7 @@
private VisualStabilityCoordinator mCoordinator;
- // captured listeners and pluggables:
- private NotifCollectionListener mCollectionListener;
-
+ @Mock private DumpManager mDumpManager;
@Mock private NotifPipeline mNotifPipeline;
@Mock private WakefulnessLifecycle mWakefulnessLifecycle;
@Mock private StatusBarStateController mStatusBarStateController;
@@ -69,7 +69,6 @@
@Captor private ArgumentCaptor<WakefulnessLifecycle.Observer> mWakefulnessObserverCaptor;
@Captor private ArgumentCaptor<StatusBarStateController.StateListener> mSBStateListenerCaptor;
@Captor private ArgumentCaptor<NotifStabilityManager> mNotifStabilityManagerCaptor;
- @Captor private ArgumentCaptor<NotifCollectionListener> mNotifCollectionListenerCaptor;
private FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock);
@@ -84,6 +83,7 @@
MockitoAnnotations.initMocks(this);
mCoordinator = new VisualStabilityCoordinator(
+ mDumpManager,
mHeadsUpManager,
mWakefulnessLifecycle,
mStatusBarStateController,
@@ -107,6 +107,12 @@
.build();
when(mHeadsUpManager.isAlerting(mEntry.getKey())).thenReturn(false);
+
+ // Whenever we invalidate, the pipeline runs again, so we invalidate the state
+ doAnswer(i -> {
+ mNotifStabilityManager.onBeginRun();
+ return null;
+ }).when(mInvalidateListener).onPluggableInvalidated(eq(mNotifStabilityManager));
}
@Test
@@ -211,7 +217,7 @@
mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.uptimeMillis());
// THEN the notification list is invalidated
- verifyInvalidateCalled(true);
+ verify(mInvalidateListener, times(1)).onPluggableInvalidated(mNotifStabilityManager);
}
@Test
@@ -225,7 +231,7 @@
mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.currentTimeMillis());
// THEN invalidate is not called because this entry was never suppressed from reordering
- verifyInvalidateCalled(false);
+ verify(mInvalidateListener, never()).onPluggableInvalidated(mNotifStabilityManager);
}
@Test
@@ -241,7 +247,7 @@
// THEN invalidate is not called because this entry was never suppressed from reordering;
// THEN section changes are allowed for this notification
- verifyInvalidateCalled(false);
+ verify(mInvalidateListener, never()).onPluggableInvalidated(mNotifStabilityManager);
assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
// WHEN we're pulsing (now disallowing reordering)
@@ -268,13 +274,14 @@
// WHEN we temporarily allow section changes for this notification entry
mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.currentTimeMillis());
- verifyInvalidateCalled(true); // can now reorder, so invalidates
+ // can now reorder, so invalidates
+ verify(mInvalidateListener, times(1)).onPluggableInvalidated(mNotifStabilityManager);
// WHEN reordering is now allowed because device isn't pulsing anymore
setPulsing(false);
- // THEN invalidate isn't called since reordering was already allowed
- verifyInvalidateCalled(false);
+ // THEN invalidate isn't called a second time since reordering was already allowed
+ verify(mInvalidateListener, times(1)).onPluggableInvalidated(mNotifStabilityManager);
}
@Test
@@ -292,7 +299,7 @@
// THEN we never see any calls to invalidate since there weren't any notifications that
// were being suppressed from grouping or section changes
- verifyInvalidateCalled(false);
+ verify(mInvalidateListener, never()).onPluggableInvalidated(mNotifStabilityManager);
}
@Test
@@ -308,7 +315,41 @@
setPanelExpanded(false);
// invalidate is called because we were previously suppressing a group change
- verifyInvalidateCalled(true);
+ verify(mInvalidateListener, times(1)).onPluggableInvalidated(mNotifStabilityManager);
+ }
+
+ @Test
+ public void testNotSuppressingEntryReorderingAnymoreWillInvalidate() {
+ // GIVEN visual stability is being maintained b/c panel is expanded
+ setPulsing(false);
+ setScreenOn(true);
+ setPanelExpanded(true);
+
+ assertFalse(mNotifStabilityManager.isEntryReorderingAllowed(mEntry));
+ // The pipeline still has to report back that entry reordering was suppressed
+ mNotifStabilityManager.onEntryReorderSuppressed();
+
+ // WHEN the panel isn't expanded anymore
+ setPanelExpanded(false);
+
+ // invalidate is called because we were previously suppressing an entry reorder
+ verify(mInvalidateListener, times(1)).onPluggableInvalidated(mNotifStabilityManager);
+ }
+
+ @Test
+ public void testQueryingEntryReorderingButNotReportingReorderSuppressedDoesNotInvalidate() {
+ // GIVEN visual stability is being maintained b/c panel is expanded
+ setPulsing(false);
+ setScreenOn(true);
+ setPanelExpanded(true);
+
+ assertFalse(mNotifStabilityManager.isEntryReorderingAllowed(mEntry));
+
+ // WHEN the panel isn't expanded anymore
+ setPanelExpanded(false);
+
+ // invalidate is not called because we were not told that an entry reorder was suppressed
+ verify(mInvalidateListener, never()).onPluggableInvalidated(mNotifStabilityManager);
}
@Test
@@ -345,13 +386,4 @@
mStatusBarStateListener.onExpandedChanged(expanded);
}
- private void verifyInvalidateCalled(boolean invalidateCalled) {
- if (invalidateCalled) {
- verify(mInvalidateListener).onPluggableInvalidated(mNotifStabilityManager);
- } else {
- verify(mInvalidateListener, never()).onPluggableInvalidated(mNotifStabilityManager);
- }
-
- reset(mInvalidateListener);
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
index ed42ac3..a53fb78 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
@@ -70,8 +70,8 @@
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
-import com.android.systemui.statusbar.notification.collection.inflation.LowPriorityInflationHelper;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
+import com.android.systemui.statusbar.notification.collection.legacy.LowPriorityInflationHelper;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.notification.icon.IconBuilder;
@@ -179,7 +179,6 @@
.setNotification(notification)
.build();
- when(mFeatureFlags.isNewNotifPipelineEnabled()).thenReturn(false);
when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
mEntryManager = new NotificationEntryManager(
@@ -283,6 +282,7 @@
mRowBinder = new NotificationRowBinderImpl(
mContext,
+ mFeatureFlags,
new NotificationMessagingUtil(mContext),
mRemoteInputManager,
mLockscreenUserManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index f11f8c4..58e3cc8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -48,6 +48,7 @@
import com.android.systemui.ActivityStarterDelegate;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.KeyguardMediaController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
@@ -79,6 +80,7 @@
@Rule public final MockitoRule mockitoRule = MockitoJUnit.rule();
@Mock private NotificationStackScrollLayout mNssl;
+ @Mock private FeatureFlags mFeatureFlags;
@Mock private ActivityStarterDelegate mActivityStarterDelegate;
@Mock private StatusBarStateController mStatusBarStateController;
@Mock private ConfigurationController mConfigurationController;
@@ -120,6 +122,7 @@
when(mSilentHeaderController.getHeaderView()).thenReturn(mock(SectionHeaderView.class));
mSectionsManager =
new NotificationSectionsManager(
+ mFeatureFlags,
mStatusBarStateController,
mConfigurationController,
mKeyguardMediaController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index f89bbe8..766471b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -152,7 +152,7 @@
}
@Test
- public void onWallpaperColorsChanged_setsTheme() {
+ public void onWallpaperColorsChanged_setsTheme_whenForeground() {
// Should ask for a new theme when wallpaper colors change
WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
Color.valueOf(Color.BLUE), null);
@@ -180,13 +180,43 @@
// But should change theme after changing wallpapers
clearInvocations(mThemeOverlayApplier);
- mBroadcastReceiver.getValue().onReceive(null, new Intent(Intent.ACTION_WALLPAPER_CHANGED));
+ Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);
+ intent.putExtra(WallpaperManager.EXTRA_FROM_FOREGROUND_APP, true);
+ mBroadcastReceiver.getValue().onReceive(null, intent);
mColorsListener.getValue().onColorsChanged(new WallpaperColors(Color.valueOf(Color.BLACK),
null, null), WallpaperManager.FLAG_SYSTEM);
verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
}
@Test
+ public void onWallpaperColorsChanged_setsTheme_skipWhenBackground() {
+ // Should ask for a new theme when wallpaper colors change
+ WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
+ Color.valueOf(Color.BLUE), null);
+ mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
+ ArgumentCaptor<Map<String, OverlayIdentifier>> themeOverlays =
+ ArgumentCaptor.forClass(Map.class);
+
+ verify(mThemeOverlayApplier)
+ .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any());
+
+ // Assert that we received the colors that we were expecting
+ assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
+ .isEqualTo(new OverlayIdentifier("ffff0000"));
+ assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_ACCENT_COLOR))
+ .isEqualTo(new OverlayIdentifier("ffff0000"));
+
+ // Should not change theme after changing wallpapers, if intent doesn't have
+ // WallpaperManager.EXTRA_FROM_FOREGROUND_APP set to true.
+ clearInvocations(mThemeOverlayApplier);
+ mBroadcastReceiver.getValue().onReceive(null, new Intent(Intent.ACTION_WALLPAPER_CHANGED));
+ mColorsListener.getValue().onColorsChanged(new WallpaperColors(Color.valueOf(Color.BLACK),
+ null, null), WallpaperManager.FLAG_SYSTEM);
+ verify(mThemeOverlayApplier, never())
+ .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+ }
+
+ @Test
public void onWallpaperColorsChanged_preservesWallpaperPickerTheme() {
// Should ask for a new theme when wallpaper colors change
WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
@@ -455,7 +485,9 @@
// Regression test: null events should not reset the internal state and allow colors to be
// applied again.
clearInvocations(mThemeOverlayApplier);
- mBroadcastReceiver.getValue().onReceive(null, new Intent(Intent.ACTION_WALLPAPER_CHANGED));
+ Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);
+ intent.putExtra(WallpaperManager.EXTRA_FROM_FOREGROUND_APP, true);
+ mBroadcastReceiver.getValue().onReceive(null, intent);
mColorsListener.getValue().onColorsChanged(null, WallpaperManager.FLAG_SYSTEM);
verify(mThemeOverlayApplier, never()).applyCurrentUserOverlays(any(), any(), anyInt(),
any());
diff --git a/proto/src/criticalevents/critical_event_log.proto b/proto/src/criticalevents/critical_event_log.proto
index 0e03434..25814ec 100644
--- a/proto/src/criticalevents/critical_event_log.proto
+++ b/proto/src/criticalevents/critical_event_log.proto
@@ -57,6 +57,8 @@
Watchdog watchdog = 2;
HalfWatchdog half_watchdog = 3;
AppNotResponding anr = 4;
+ JavaCrash java_crash = 5;
+ NativeCrash native_crash = 6;
}
message Watchdog {
@@ -99,6 +101,47 @@
optional ProcessClass process_class = 5;
}
+ message JavaCrash {
+ // The crash exception class.
+ // Optional, may be redacted for privacy.
+ optional string exception_class = 1;
+
+ // Name of the crashed process.
+ // Optional, may be redacted for privacy.
+ optional string process = 2;
+
+ // PID of the crashed process.
+ // Required.
+ optional int32 pid = 3;
+
+ // UID of the crashed process.
+ // Required.
+ optional int32 uid = 4;
+
+ // Category of the crashed process (DATA_APP, SYSTEM_APP, etc).
+ // Required.
+ optional ProcessClass process_class = 5;
+ }
+
+ message NativeCrash {
+ // Name of the crashed process.
+ // Optional, may be redacted for privacy.
+ optional string process = 1;
+
+ // PID of the crashed process.
+ // Required.
+ optional int32 pid = 2;
+
+ // UID of the crashed process.
+ // Required.
+ optional int32 uid = 3;
+
+ // Category of the crashed process (DATA_APP, SYSTEM_APP, etc).
+ // Required.
+ optional ProcessClass process_class = 4;
+ }
+
+
// Mirrors definition & values in {@link android.server.ServerProtoEnums}.
enum ProcessClass {
PROCESS_CLASS_UNKNOWN = 0;
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index c62473d..51e3417 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -17,6 +17,7 @@
package com.android.server.accessibility.magnification;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
+import static android.view.accessibility.MagnificationAnimationCallback.STUB_ANIMATION_CALLBACK;
import android.animation.Animator;
import android.animation.ValueAnimator;
@@ -66,9 +67,6 @@
private static final boolean DEBUG = false;
private static final String LOG_TAG = "FullScreenMagnificationController";
- private static final MagnificationAnimationCallback STUB_ANIMATION_CALLBACK = success -> {
- };
-
private static final boolean DEBUG_SET_MAGNIFICATION_SPEC = false;
private final Object mLock;
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
index bfaab9a..e1ec273 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -18,6 +18,7 @@
import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK;
+import static android.view.accessibility.MagnificationAnimationCallback.STUB_ANIMATION_CALLBACK;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -74,7 +75,7 @@
public void onReceive(Context context, Intent intent) {
final int displayId = context.getDisplayId();
removeMagnificationButton(displayId);
- disableWindowMagnification(displayId, false);
+ disableWindowMagnification(displayId, false, null);
}
};
@@ -272,7 +273,7 @@
* or {@link Float#NaN} to leave unchanged.
*/
void enableWindowMagnification(int displayId, float scale, float centerX, float centerY) {
- enableWindowMagnification(displayId, scale, centerX, centerY, null);
+ enableWindowMagnification(displayId, scale, centerX, centerY, STUB_ANIMATION_CALLBACK);
}
/**
@@ -314,7 +315,7 @@
* @param clear {@true} Clears the state of window magnification.
*/
void disableWindowMagnification(int displayId, boolean clear) {
- disableWindowMagnification(displayId, clear, null);
+ disableWindowMagnification(displayId, clear, STUB_ANIMATION_CALLBACK);
}
/**
diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
index 1fd7951..2645f3f 100644
--- a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
+++ b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
@@ -17,6 +17,7 @@
package com.android.server.companion;
import static android.companion.AssociationRequest.DEVICE_PROFILE_APP_STREAMING;
+import static android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION;
import static android.companion.AssociationRequest.DEVICE_PROFILE_WATCH;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -32,6 +33,7 @@
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.role.RoleManager;
import android.companion.AssociationInfo;
import android.companion.AssociationRequest;
import android.companion.CompanionDeviceManager;
@@ -41,8 +43,10 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.Signature;
+import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.PackageUtils;
import android.util.Slog;
@@ -56,6 +60,7 @@
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -69,6 +74,8 @@
map.put(DEVICE_PROFILE_WATCH, Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH);
map.put(DEVICE_PROFILE_APP_STREAMING,
Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING);
+ map.put(DEVICE_PROFILE_AUTOMOTIVE_PROJECTION,
+ Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION);
DEVICE_PROFILE_TO_PERMISSION = unmodifiableMap(map);
}
@@ -87,12 +94,14 @@
private IFindDeviceCallback mFindDeviceCallback;
private String mCallingPackage;
private AndroidFuture<?> mOngoingDeviceDiscovery;
+ private RoleManager mRoleManager;
private PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>> mServiceConnectors;
- AssociationRequestsProcessor(CompanionDeviceManagerService service) {
+ AssociationRequestsProcessor(CompanionDeviceManagerService service, RoleManager roleManager) {
mContext = service.getContext();
mService = service;
+ mRoleManager = roleManager;
final Intent serviceIntent = new Intent().setComponent(SERVICE_TO_BIND_TO);
mServiceConnectors = new PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>>() {
@@ -165,6 +174,16 @@
}));
}
+ private boolean isRoleHolder(int userId, String packageName, String role) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ List<String> holders = mRoleManager.getRoleHoldersAsUser(role, UserHandle.of(userId));
+ return holders.contains(packageName);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
void stopScan(AssociationRequest request, IFindDeviceCallback callback, String callingPackage) {
if (DEBUG) {
Slog.d(TAG, "stopScan(request = " + request + ")");
@@ -186,6 +205,12 @@
"DEVICE_PROFILE_APP_STREAMING is not fully supported yet.");
}
+ if (DEVICE_PROFILE_AUTOMOTIVE_PROJECTION.equals(deviceProfile)) {
+ // TODO: remove, when properly supporting this profile.
+ throw new UnsupportedOperationException(
+ "DEVICE_PROFILE_AUTOMOTIVE_PROJECTION is not fully supported yet.");
+ }
+
if (!DEVICE_PROFILE_TO_PERMISSION.containsKey(deviceProfile)) {
throw new IllegalArgumentException("Unsupported device profile: " + deviceProfile);
}
@@ -217,6 +242,12 @@
}
private boolean mayAssociateWithoutPrompt(String packageName, int userId) {
+ if (mRequest.getDeviceProfile() != null
+ && isRoleHolder(userId, packageName, mRequest.getDeviceProfile())) {
+ // Don't need to collect user's consent since app already holds the role.
+ return true;
+ }
+
String[] sameOemPackages = mContext.getResources()
.getStringArray(com.android.internal.R.array.config_companionDevicePackages);
if (!ArrayUtils.contains(sameOemPackages, packageName)) {
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index f648afa..d5357dc 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -200,7 +200,6 @@
super(context);
mImpl = new CompanionDeviceManagerImpl();
mPersistentDataStore = new PersistentDataStore();
- mAssociationRequestsProcessor = new AssociationRequestsProcessor(this);
mPowerWhitelistManager = context.getSystemService(PowerWhitelistManager.class);
mRoleManager = context.getSystemService(RoleManager.class);
@@ -213,6 +212,7 @@
context.getSystemService(PermissionControllerManager.class));
mUserManager = context.getSystemService(UserManager.class);
mCompanionDevicePresenceController = new CompanionDevicePresenceController();
+ mAssociationRequestsProcessor = new AssociationRequestsProcessor(this, mRoleManager);
registerPackageMonitor();
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 16343e5..9f22489 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -149,9 +149,11 @@
"android.hardware.boot-V1.1-java",
"android.hardware.boot-V1.2-java",
"android.hardware.broadcastradio-V2.0-java",
- "android.hardware.health-V1.0-java",
- "android.hardware.health-V2.0-java",
- "android.hardware.health-V2.1-java",
+ "android.hardware.health-V1.0-java", // HIDL
+ "android.hardware.health-V2.0-java", // HIDL
+ "android.hardware.health-V2.1-java", // HIDL
+ "android.hardware.health-V1-java", // AIDL
+ "android.hardware.health-translate-java",
"android.hardware.light-V1-java",
"android.hardware.tv.cec-V1.1-java",
"android.hardware.weaver-V1.0-java",
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 728efa5..844ac86 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -17,7 +17,7 @@
package com.android.server;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import static com.android.server.health.Utils.copy;
+import static com.android.server.health.Utils.copyV1Battery;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -26,7 +26,7 @@
import android.content.Context;
import android.content.Intent;
import android.database.ContentObserver;
-import android.hardware.health.V1_0.HealthInfo;
+import android.hardware.health.HealthInfo;
import android.hardware.health.V2_1.BatteryCapacityLevel;
import android.metrics.LogMaker;
import android.os.BatteryManager;
@@ -138,7 +138,6 @@
private HealthInfo mHealthInfo;
private final HealthInfo mLastHealthInfo = new HealthInfo();
- private android.hardware.health.V2_1.HealthInfo mHealthInfo2p1;
private boolean mBatteryLevelCritical;
private int mLastBatteryStatus;
private int mLastBatteryHealth;
@@ -354,8 +353,8 @@
}
private boolean shouldShutdownLocked() {
- if (mHealthInfo2p1.batteryCapacityLevel != BatteryCapacityLevel.UNSUPPORTED) {
- return (mHealthInfo2p1.batteryCapacityLevel == BatteryCapacityLevel.CRITICAL);
+ if (mHealthInfo.batteryCapacityLevel != BatteryCapacityLevel.UNSUPPORTED) {
+ return (mHealthInfo.batteryCapacityLevel == BatteryCapacityLevel.CRITICAL);
}
if (mHealthInfo.batteryLevel > 0) {
return false;
@@ -397,7 +396,7 @@
// shut down gracefully if temperature is too high (> 68.0C by default)
// wait until the system has booted before attempting to display the
// shutdown dialog.
- if (mHealthInfo.batteryTemperature > mShutdownBatteryTemperature) {
+ if (mHealthInfo.batteryTemperatureTenthsCelsius > mShutdownBatteryTemperature) {
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -414,27 +413,23 @@
}
}
- private void update(android.hardware.health.V2_1.HealthInfo info) {
+ private void update(android.hardware.health.HealthInfo info) {
traceBegin("HealthInfoUpdate");
- Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryChargeCounter",
- info.legacy.legacy.batteryChargeCounter);
- Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryCurrent",
- info.legacy.legacy.batteryCurrent);
- Trace.traceCounter(Trace.TRACE_TAG_POWER, "PlugType",
- plugType(info.legacy.legacy));
- Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryStatus",
- info.legacy.legacy.batteryStatus);
+ Trace.traceCounter(
+ Trace.TRACE_TAG_POWER, "BatteryChargeCounter", info.batteryChargeCounterUah);
+ Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryCurrent", info.batteryCurrentMicroamps);
+ Trace.traceCounter(Trace.TRACE_TAG_POWER, "PlugType", plugType(info));
+ Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryStatus", info.batteryStatus);
synchronized (mLock) {
if (!mUpdatesStopped) {
- mHealthInfo = info.legacy.legacy;
- mHealthInfo2p1 = info;
+ mHealthInfo = info;
// Process the new values.
processValuesLocked(false);
mLock.notifyAll(); // for any waiters on new info
} else {
- copy(mLastHealthInfo, info.legacy.legacy);
+ copyV1Battery(mLastHealthInfo, info);
}
}
traceEnd();
@@ -470,11 +465,16 @@
// Let the battery stats keep track of the current level.
try {
- mBatteryStats.setBatteryState(mHealthInfo.batteryStatus, mHealthInfo.batteryHealth,
- mPlugType, mHealthInfo.batteryLevel, mHealthInfo.batteryTemperature,
- mHealthInfo.batteryVoltage, mHealthInfo.batteryChargeCounter,
- mHealthInfo.batteryFullCharge,
- mHealthInfo2p1.batteryChargeTimeToFullNowSeconds);
+ mBatteryStats.setBatteryState(
+ mHealthInfo.batteryStatus,
+ mHealthInfo.batteryHealth,
+ mPlugType,
+ mHealthInfo.batteryLevel,
+ mHealthInfo.batteryTemperatureTenthsCelsius,
+ mHealthInfo.batteryVoltageMillivolts,
+ mHealthInfo.batteryChargeCounterUah,
+ mHealthInfo.batteryFullChargeUah,
+ mHealthInfo.batteryChargeTimeToFullNowSeconds);
} catch (RemoteException e) {
// Should never happen.
}
@@ -482,17 +482,18 @@
shutdownIfNoPowerLocked();
shutdownIfOverTempLocked();
- if (force || (mHealthInfo.batteryStatus != mLastBatteryStatus ||
- mHealthInfo.batteryHealth != mLastBatteryHealth ||
- mHealthInfo.batteryPresent != mLastBatteryPresent ||
- mHealthInfo.batteryLevel != mLastBatteryLevel ||
- mPlugType != mLastPlugType ||
- mHealthInfo.batteryVoltage != mLastBatteryVoltage ||
- mHealthInfo.batteryTemperature != mLastBatteryTemperature ||
- mHealthInfo.maxChargingCurrent != mLastMaxChargingCurrent ||
- mHealthInfo.maxChargingVoltage != mLastMaxChargingVoltage ||
- mHealthInfo.batteryChargeCounter != mLastChargeCounter ||
- mInvalidCharger != mLastInvalidCharger)) {
+ if (force
+ || (mHealthInfo.batteryStatus != mLastBatteryStatus
+ || mHealthInfo.batteryHealth != mLastBatteryHealth
+ || mHealthInfo.batteryPresent != mLastBatteryPresent
+ || mHealthInfo.batteryLevel != mLastBatteryLevel
+ || mPlugType != mLastPlugType
+ || mHealthInfo.batteryVoltageMillivolts != mLastBatteryVoltage
+ || mHealthInfo.batteryTemperatureTenthsCelsius != mLastBatteryTemperature
+ || mHealthInfo.maxChargingCurrentMicroamps != mLastMaxChargingCurrent
+ || mHealthInfo.maxChargingVoltageMicrovolts != mLastMaxChargingVoltage
+ || mHealthInfo.batteryChargeCounterUah != mLastChargeCounter
+ || mInvalidCharger != mLastInvalidCharger)) {
if (mPlugType != mLastPlugType) {
if (mLastPlugType == BATTERY_PLUGGED_NONE) {
@@ -549,8 +550,11 @@
if (mHealthInfo.batteryLevel != mLastBatteryLevel) {
// Don't do this just from voltage or temperature changes, that is
// too noisy.
- EventLog.writeEvent(EventLogTags.BATTERY_LEVEL,
- mHealthInfo.batteryLevel, mHealthInfo.batteryVoltage, mHealthInfo.batteryTemperature);
+ EventLog.writeEvent(
+ EventLogTags.BATTERY_LEVEL,
+ mHealthInfo.batteryLevel,
+ mHealthInfo.batteryVoltageMillivolts,
+ mHealthInfo.batteryTemperatureTenthsCelsius);
}
if (mBatteryLevelCritical && !mLastBatteryLevelCritical &&
mPlugType == BATTERY_PLUGGED_NONE) {
@@ -656,11 +660,11 @@
mLastBatteryPresent = mHealthInfo.batteryPresent;
mLastBatteryLevel = mHealthInfo.batteryLevel;
mLastPlugType = mPlugType;
- mLastBatteryVoltage = mHealthInfo.batteryVoltage;
- mLastBatteryTemperature = mHealthInfo.batteryTemperature;
- mLastMaxChargingCurrent = mHealthInfo.maxChargingCurrent;
- mLastMaxChargingVoltage = mHealthInfo.maxChargingVoltage;
- mLastChargeCounter = mHealthInfo.batteryChargeCounter;
+ mLastBatteryVoltage = mHealthInfo.batteryVoltageMillivolts;
+ mLastBatteryTemperature = mHealthInfo.batteryTemperatureTenthsCelsius;
+ mLastMaxChargingCurrent = mHealthInfo.maxChargingCurrentMicroamps;
+ mLastMaxChargingVoltage = mHealthInfo.maxChargingVoltageMicrovolts;
+ mLastChargeCounter = mHealthInfo.batteryChargeCounterUah;
mLastBatteryLevelCritical = mBatteryLevelCritical;
mLastInvalidCharger = mInvalidCharger;
}
@@ -683,13 +687,17 @@
intent.putExtra(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);
intent.putExtra(BatteryManager.EXTRA_ICON_SMALL, icon);
intent.putExtra(BatteryManager.EXTRA_PLUGGED, mPlugType);
- intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.batteryVoltage);
- intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mHealthInfo.batteryTemperature);
+ intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.batteryVoltageMillivolts);
+ intent.putExtra(
+ BatteryManager.EXTRA_TEMPERATURE, mHealthInfo.batteryTemperatureTenthsCelsius);
intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mHealthInfo.batteryTechnology);
intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);
- intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_CURRENT, mHealthInfo.maxChargingCurrent);
- intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE, mHealthInfo.maxChargingVoltage);
- intent.putExtra(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.batteryChargeCounter);
+ intent.putExtra(
+ BatteryManager.EXTRA_MAX_CHARGING_CURRENT, mHealthInfo.maxChargingCurrentMicroamps);
+ intent.putExtra(
+ BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE,
+ mHealthInfo.maxChargingVoltageMicrovolts);
+ intent.putExtra(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.batteryChargeCounterUah);
if (DEBUG) {
Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED. scale:" + BATTERY_SCALE
+ ", info:" + mHealthInfo.toString());
@@ -709,9 +717,9 @@
event.putBoolean(BatteryManager.EXTRA_BATTERY_LOW, mSentLowBatteryBroadcast);
event.putInt(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);
event.putInt(BatteryManager.EXTRA_PLUGGED, mPlugType);
- event.putInt(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.batteryVoltage);
- event.putInt(BatteryManager.EXTRA_TEMPERATURE, mHealthInfo.batteryTemperature);
- event.putInt(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.batteryChargeCounter);
+ event.putInt(BatteryManager.EXTRA_VOLTAGE, mHealthInfo.batteryVoltageMillivolts);
+ event.putInt(BatteryManager.EXTRA_TEMPERATURE, mHealthInfo.batteryTemperatureTenthsCelsius);
+ event.putInt(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.batteryChargeCounterUah);
event.putLong(BatteryManager.EXTRA_EVENT_TIMESTAMP, now);
boolean queueWasEmpty = mBatteryLevelsEventQueue.isEmpty();
@@ -903,7 +911,7 @@
}
try {
if (!mUpdatesStopped) {
- copy(mLastHealthInfo, mHealthInfo);
+ copyV1Battery(mLastHealthInfo, mHealthInfo);
}
boolean update = true;
switch (key) {
@@ -926,10 +934,10 @@
mHealthInfo.batteryLevel = Integer.parseInt(value);
break;
case "counter":
- mHealthInfo.batteryChargeCounter = Integer.parseInt(value);
+ mHealthInfo.batteryChargeCounterUah = Integer.parseInt(value);
break;
case "temp":
- mHealthInfo.batteryTemperature = Integer.parseInt(value);
+ mHealthInfo.batteryTemperatureTenthsCelsius = Integer.parseInt(value);
break;
case "invalid":
mInvalidCharger = Integer.parseInt(value);
@@ -973,7 +981,7 @@
private void setChargerAcOnline(boolean online, boolean forceUpdate) {
if (!mUpdatesStopped) {
- copy(mLastHealthInfo, mHealthInfo);
+ copyV1Battery(mLastHealthInfo, mHealthInfo);
}
mHealthInfo.chargerAcOnline = online;
mUpdatesStopped = true;
@@ -982,7 +990,7 @@
private void setBatteryLevel(int level, boolean forceUpdate) {
if (!mUpdatesStopped) {
- copy(mLastHealthInfo, mHealthInfo);
+ copyV1Battery(mLastHealthInfo, mHealthInfo);
}
mHealthInfo.batteryLevel = level;
mUpdatesStopped = true;
@@ -991,7 +999,7 @@
private void unplugBattery(boolean forceUpdate, PrintWriter pw) {
if (!mUpdatesStopped) {
- copy(mLastHealthInfo, mHealthInfo);
+ copyV1Battery(mLastHealthInfo, mHealthInfo);
}
mHealthInfo.chargerAcOnline = false;
mHealthInfo.chargerUsbOnline = false;
@@ -1003,7 +1011,7 @@
private void resetBattery(boolean forceUpdate, @Nullable PrintWriter pw) {
if (mUpdatesStopped) {
mUpdatesStopped = false;
- copy(mHealthInfo, mLastHealthInfo);
+ copyV1Battery(mHealthInfo, mLastHealthInfo);
Binder.withCleanCallingIdentity(() -> processValuesLocked(forceUpdate, pw));
}
if (mBatteryInputSuspended) {
@@ -1038,16 +1046,16 @@
pw.println(" AC powered: " + mHealthInfo.chargerAcOnline);
pw.println(" USB powered: " + mHealthInfo.chargerUsbOnline);
pw.println(" Wireless powered: " + mHealthInfo.chargerWirelessOnline);
- pw.println(" Max charging current: " + mHealthInfo.maxChargingCurrent);
- pw.println(" Max charging voltage: " + mHealthInfo.maxChargingVoltage);
- pw.println(" Charge counter: " + mHealthInfo.batteryChargeCounter);
+ pw.println(" Max charging current: " + mHealthInfo.maxChargingCurrentMicroamps);
+ pw.println(" Max charging voltage: " + mHealthInfo.maxChargingVoltageMicrovolts);
+ pw.println(" Charge counter: " + mHealthInfo.batteryChargeCounterUah);
pw.println(" status: " + mHealthInfo.batteryStatus);
pw.println(" health: " + mHealthInfo.batteryHealth);
pw.println(" present: " + mHealthInfo.batteryPresent);
pw.println(" level: " + mHealthInfo.batteryLevel);
pw.println(" scale: " + BATTERY_SCALE);
- pw.println(" voltage: " + mHealthInfo.batteryVoltage);
- pw.println(" temperature: " + mHealthInfo.batteryTemperature);
+ pw.println(" voltage: " + mHealthInfo.batteryVoltageMillivolts);
+ pw.println(" temperature: " + mHealthInfo.batteryTemperatureTenthsCelsius);
pw.println(" technology: " + mHealthInfo.batteryTechnology);
} else {
Shell shell = new Shell();
@@ -1070,16 +1078,23 @@
batteryPluggedValue = OsProtoEnums.BATTERY_PLUGGED_WIRELESS;
}
proto.write(BatteryServiceDumpProto.PLUGGED, batteryPluggedValue);
- proto.write(BatteryServiceDumpProto.MAX_CHARGING_CURRENT, mHealthInfo.maxChargingCurrent);
- proto.write(BatteryServiceDumpProto.MAX_CHARGING_VOLTAGE, mHealthInfo.maxChargingVoltage);
- proto.write(BatteryServiceDumpProto.CHARGE_COUNTER, mHealthInfo.batteryChargeCounter);
+ proto.write(
+ BatteryServiceDumpProto.MAX_CHARGING_CURRENT,
+ mHealthInfo.maxChargingCurrentMicroamps);
+ proto.write(
+ BatteryServiceDumpProto.MAX_CHARGING_VOLTAGE,
+ mHealthInfo.maxChargingVoltageMicrovolts);
+ proto.write(
+ BatteryServiceDumpProto.CHARGE_COUNTER, mHealthInfo.batteryChargeCounterUah);
proto.write(BatteryServiceDumpProto.STATUS, mHealthInfo.batteryStatus);
proto.write(BatteryServiceDumpProto.HEALTH, mHealthInfo.batteryHealth);
proto.write(BatteryServiceDumpProto.IS_PRESENT, mHealthInfo.batteryPresent);
proto.write(BatteryServiceDumpProto.LEVEL, mHealthInfo.batteryLevel);
proto.write(BatteryServiceDumpProto.SCALE, BATTERY_SCALE);
- proto.write(BatteryServiceDumpProto.VOLTAGE, mHealthInfo.batteryVoltage);
- proto.write(BatteryServiceDumpProto.TEMPERATURE, mHealthInfo.batteryTemperature);
+ proto.write(BatteryServiceDumpProto.VOLTAGE, mHealthInfo.batteryVoltageMillivolts);
+ proto.write(
+ BatteryServiceDumpProto.TEMPERATURE,
+ mHealthInfo.batteryTemperatureTenthsCelsius);
proto.write(BatteryServiceDumpProto.TECHNOLOGY, mHealthInfo.batteryTechnology);
}
proto.flush();
@@ -1207,14 +1222,14 @@
@Override
public int getBatteryChargeCounter() {
synchronized (mLock) {
- return mHealthInfo.batteryChargeCounter;
+ return mHealthInfo.batteryChargeCounterUah;
}
}
@Override
public int getBatteryFullCharge() {
synchronized (mLock) {
- return mHealthInfo.batteryFullCharge;
+ return mHealthInfo.batteryFullChargeUah;
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 0eed190..4c2cd53 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8049,11 +8049,16 @@
crashInfo.throwFileName,
crashInfo.throwLineNumber);
+ int processClassEnum = processName.equals("system_server") ? ServerProtoEnums.SYSTEM_SERVER
+ : (r != null) ? r.getProcessClassEnum()
+ : ServerProtoEnums.ERROR_SOURCE_UNKNOWN;
+ int uid = (r != null) ? r.uid : -1;
+ int pid = (r != null) ? r.getPid() : -1;
FrameworkStatsLog.write(FrameworkStatsLog.APP_CRASH_OCCURRED,
- (r != null) ? r.uid : -1,
+ uid,
eventType,
processName,
- (r != null) ? r.getPid() : -1,
+ pid,
(r != null && r.info != null) ? r.info.packageName : "",
(r != null && r.info != null) ? (r.info.isInstantApp()
? FrameworkStatsLog.APP_CRASH_OCCURRED__IS_INSTANT_APP__TRUE
@@ -8063,9 +8068,7 @@
? FrameworkStatsLog.APP_CRASH_OCCURRED__FOREGROUND_STATE__FOREGROUND
: FrameworkStatsLog.APP_CRASH_OCCURRED__FOREGROUND_STATE__BACKGROUND)
: FrameworkStatsLog.APP_CRASH_OCCURRED__FOREGROUND_STATE__UNKNOWN,
- processName.equals("system_server") ? ServerProtoEnums.SYSTEM_SERVER
- : (r != null) ? r.getProcessClassEnum()
- : ServerProtoEnums.ERROR_SOURCE_UNKNOWN,
+ processClassEnum,
incrementalMetrics != null /* isIncremental */, loadingProgress,
incrementalMetrics != null ? incrementalMetrics.getMillisSinceOldestPendingRead()
: -1,
@@ -8092,6 +8095,13 @@
: -1
);
+ if (eventType.equals("native_crash")) {
+ CriticalEventLog.getInstance().logNativeCrash(processClassEnum, processName, uid, pid);
+ } else if (eventType.equals("crash")) {
+ CriticalEventLog.getInstance().logJavaCrash(crashInfo.exceptionClassName,
+ processClassEnum, processName, uid, pid);
+ }
+
final int relaunchReason = r == null ? RELAUNCH_REASON_NONE
: r.getWindowProcessController().computeRelaunchReason();
final String relaunchReasonString = relaunchReasonToString(relaunchReason);
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index b144c8d..5b33a71 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -87,6 +87,7 @@
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
@@ -1889,16 +1890,21 @@
int userId = Integer.parseInt(getNextArgRequired());
boolean switched;
- if (wait) {
- switched = switchUserAndWaitForComplete(userId);
- } else {
- switched = mInterface.switchUser(userId);
- }
- if (switched) {
- return 0;
- } else {
- pw.printf("Error: Failed to switch to user %d\n", userId);
- return 1;
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "shell_runSwitchUser");
+ try {
+ if (wait) {
+ switched = switchUserAndWaitForComplete(userId);
+ } else {
+ switched = mInterface.switchUser(userId);
+ }
+ if (switched) {
+ return 0;
+ } else {
+ pw.printf("Error: Failed to switch to user %d\n", userId);
+ return 1;
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index b5a9ee9..c48ff9f 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -97,6 +97,7 @@
DeviceConfig.NAMESPACE_SURFACE_FLINGER_NATIVE_BOOT,
DeviceConfig.NAMESPACE_SWCODEC_NATIVE,
DeviceConfig.NAMESPACE_TETHERING,
+ DeviceConfig.NAMESPACE_VIRTUALIZATION_FRAMEWORK_NATIVE,
DeviceConfig.NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT,
};
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 6dbdead..16d7f1d 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1406,7 +1406,7 @@
@Nullable IProgressListener unlockListener) {
TimingsTraceAndSlog t = new TimingsTraceAndSlog();
- t.traceBegin("startUser-" + userId + "-" + (foreground ? "fg" : "bg"));
+ t.traceBegin("UserController.startUser-" + userId + "-" + (foreground ? "fg" : "bg"));
try {
return startUserInternal(userId, foreground, unlockListener, t);
} finally {
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index e0775d4..f42870b 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -1381,7 +1381,8 @@
Slog.d(TAG, "handleAuthenticate: modality(" + preAuthStatus.first
+ "), status(" + preAuthStatus.second + "), preAuthInfo: " + preAuthInfo
- + " requestId: " + requestId);
+ + " requestId: " + requestId + " promptInfo.isIgnoreEnrollmentState: "
+ + promptInfo.isIgnoreEnrollmentState());
if (preAuthStatus.second == BiometricConstants.BIOMETRIC_SUCCESS) {
// If BIOMETRIC_WEAK or BIOMETRIC_STRONG are allowed, but not enrolled, but
diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
index cd0ff10..a5a3542 100644
--- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java
+++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
@@ -83,6 +83,7 @@
final List<Pair<BiometricSensor, Integer>> ineligibleSensors;
final boolean credentialAvailable;
final boolean confirmationRequested;
+ final boolean ignoreEnrollmentState;
static PreAuthInfo create(ITrustManager trustManager,
DevicePolicyManager devicePolicyManager,
@@ -114,7 +115,8 @@
@AuthenticatorStatus int status = getStatusForBiometricAuthenticator(
devicePolicyManager, settingObserver, sensor, userId, opPackageName,
checkDevicePolicyManager, requestedStrength,
- promptInfo.getAllowedSensorIds());
+ promptInfo.getAllowedSensorIds(),
+ promptInfo.isIgnoreEnrollmentState());
Slog.d(TAG, "Package: " + opPackageName
+ " Sensor ID: " + sensor.id
@@ -130,7 +132,8 @@
}
return new PreAuthInfo(biometricRequested, requestedStrength, credentialRequested,
- eligibleSensors, ineligibleSensors, credentialAvailable, confirmationRequested);
+ eligibleSensors, ineligibleSensors, credentialAvailable, confirmationRequested,
+ promptInfo.isIgnoreEnrollmentState());
}
/**
@@ -145,7 +148,8 @@
BiometricService.SettingObserver settingObserver,
BiometricSensor sensor, int userId, String opPackageName,
boolean checkDevicePolicyManager, int requestedStrength,
- @NonNull List<Integer> requestedSensorIds) {
+ @NonNull List<Integer> requestedSensorIds,
+ boolean ignoreEnrollmentState) {
if (!requestedSensorIds.isEmpty() && !requestedSensorIds.contains(sensor.id)) {
return BIOMETRIC_NO_HARDWARE;
@@ -167,7 +171,8 @@
return BIOMETRIC_HARDWARE_NOT_DETECTED;
}
- if (!sensor.impl.hasEnrolledTemplates(userId, opPackageName)) {
+ if (!sensor.impl.hasEnrolledTemplates(userId, opPackageName)
+ && !ignoreEnrollmentState) {
return BIOMETRIC_NOT_ENROLLED;
}
@@ -238,7 +243,7 @@
private PreAuthInfo(boolean biometricRequested, int biometricStrengthRequested,
boolean credentialRequested, List<BiometricSensor> eligibleSensors,
List<Pair<BiometricSensor, Integer>> ineligibleSensors, boolean credentialAvailable,
- boolean confirmationRequested) {
+ boolean confirmationRequested, boolean ignoreEnrollmentState) {
mBiometricRequested = biometricRequested;
mBiometricStrengthRequested = biometricStrengthRequested;
this.credentialRequested = credentialRequested;
@@ -247,6 +252,7 @@
this.ineligibleSensors = ineligibleSensors;
this.credentialAvailable = credentialAvailable;
this.confirmationRequested = confirmationRequested;
+ this.ignoreEnrollmentState = ignoreEnrollmentState;
}
private Pair<BiometricSensor, Integer> calculateErrorByPriority() {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index af8e8c2..3e70ee5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -281,7 +281,7 @@
@Override // Binder call
public long authenticate(final IBinder token, final long operationId,
final int sensorId, final int userId, final IFingerprintServiceReceiver receiver,
- final String opPackageName) {
+ final String opPackageName, boolean ignoreEnrollmentState) {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final int callingUserId = UserHandle.getCallingUserId();
@@ -333,7 +333,8 @@
&& sensorProps != null && sensorProps.isAnyUdfpsType()) {
final long identity2 = Binder.clearCallingIdentity();
try {
- return authenticateWithPrompt(operationId, sensorProps, userId, receiver);
+ return authenticateWithPrompt(operationId, sensorProps, userId, receiver,
+ ignoreEnrollmentState);
} finally {
Binder.restoreCallingIdentity(identity2);
}
@@ -347,7 +348,8 @@
final long operationId,
@NonNull final FingerprintSensorPropertiesInternal props,
final int userId,
- final IFingerprintServiceReceiver receiver) {
+ final IFingerprintServiceReceiver receiver,
+ boolean ignoreEnrollmentState) {
final Context context = getUiContext();
final Executor executor = context.getMainExecutor();
@@ -368,6 +370,7 @@
})
.setAllowedSensorIds(new ArrayList<>(
Collections.singletonList(props.sensorId)))
+ .setIgnoreEnrollmentState(ignoreEnrollmentState)
.build();
final BiometricPrompt.AuthenticationCallback promptCallback =
diff --git a/services/core/java/com/android/server/communal/CommunalManagerService.java b/services/core/java/com/android/server/communal/CommunalManagerService.java
index 2fda4a5..b506230 100644
--- a/services/core/java/com/android/server/communal/CommunalManagerService.java
+++ b/services/core/java/com/android/server/communal/CommunalManagerService.java
@@ -236,7 +236,18 @@
private boolean shouldIntercept(ActivityInfo activityInfo) {
if (!mCommunalViewIsShowing.get() || !mKeyguardManager.isKeyguardLocked()) return false;
- return !isAppAllowed(activityInfo.applicationInfo);
+ ApplicationInfo appInfo = activityInfo.applicationInfo;
+ // Dreams are allowed to show, and don't require the showWhenLocked attribute.
+ if (isActiveDream(appInfo)) return false;
+
+ // If the activity doesn't have showWhenLocked enabled, disallow the activity.
+ final boolean showWhenLocked =
+ (activityInfo.flags & ActivityInfo.FLAG_SHOW_WHEN_LOCKED) != 0;
+ if (!showWhenLocked) {
+ return true;
+ }
+
+ return !isAppAllowed(appInfo);
}
private final class BinderService extends ICommunalManager.Stub {
diff --git a/services/core/java/com/android/server/criticalevents/CriticalEventLog.java b/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
index 30b3524..ab480e8 100644
--- a/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
+++ b/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
@@ -29,6 +29,8 @@
import com.android.server.criticalevents.nano.CriticalEventProto;
import com.android.server.criticalevents.nano.CriticalEventProto.AppNotResponding;
import com.android.server.criticalevents.nano.CriticalEventProto.HalfWatchdog;
+import com.android.server.criticalevents.nano.CriticalEventProto.JavaCrash;
+import com.android.server.criticalevents.nano.CriticalEventProto.NativeCrash;
import com.android.server.criticalevents.nano.CriticalEventProto.Watchdog;
import java.io.File;
@@ -179,8 +181,56 @@
log(event);
}
+ /**
+ * Logs a java crash.
+ *
+ * @param exceptionClass the crash exception class.
+ * @param processClassEnum {@link android.server.ServerProtoEnums} value for the crashed
+ * process.
+ * @param processName name of the crashed process.
+ * @param uid uid of the crashed process.
+ * @param pid pid of the crashed process.
+ */
+ public void logJavaCrash(String exceptionClass, int processClassEnum, String processName,
+ int uid, int pid) {
+ JavaCrash crash = new JavaCrash();
+ crash.exceptionClass = exceptionClass;
+ crash.processClass = processClassEnum;
+ crash.process = processName;
+ crash.uid = uid;
+ crash.pid = pid;
+ CriticalEventProto event = new CriticalEventProto();
+ event.setJavaCrash(crash);
+ log(event);
+ }
+
+ /**
+ * Logs a native crash.
+ *
+ * @param processClassEnum {@link android.server.ServerProtoEnums} value for the crashed
+ * process.
+ * @param processName name of the crashed process.
+ * @param uid uid of the crashed process.
+ * @param pid pid of the crashed process.
+ */
+ public void logNativeCrash(int processClassEnum, String processName, int uid, int pid) {
+ NativeCrash crash = new NativeCrash();
+ crash.processClass = processClassEnum;
+ crash.process = processName;
+ crash.uid = uid;
+ crash.pid = pid;
+ CriticalEventProto event = new CriticalEventProto();
+ event.setNativeCrash(crash);
+ log(event);
+ }
+
private void log(CriticalEventProto event) {
event.timestampMs = getWallTimeMillis();
+ appendAndSave(event);
+ }
+
+ @VisibleForTesting
+ void appendAndSave(CriticalEventProto event) {
mEvents.append(event);
saveLogToFile();
}
@@ -420,7 +470,18 @@
if (shouldSanitize(anr.processClass, anr.process, anr.uid)) {
return sanitizeAnr(event);
}
+ } else if (event.hasJavaCrash()) {
+ JavaCrash crash = event.getJavaCrash();
+ if (shouldSanitize(crash.processClass, crash.process, crash.uid)) {
+ return sanitizeJavaCrash(event);
+ }
+ } else if (event.hasNativeCrash()) {
+ NativeCrash crash = event.getNativeCrash();
+ if (shouldSanitize(crash.processClass, crash.process, crash.uid)) {
+ return sanitizeNativeCrash(event);
+ }
}
+ // No redaction needed.
return event;
}
@@ -428,21 +489,52 @@
boolean sameApp = processName != null && processName.equals(mTraceProcessName)
&& mTraceUid == uid;
- // Only sanitize when both the ANR event and trace file are for different data apps.
+ // Only sanitize when both the critical event and trace file are for different data
+ // apps.
return processClassEnum == CriticalEventProto.DATA_APP
&& mTraceProcessClassEnum == CriticalEventProto.DATA_APP
&& !sameApp;
}
private static CriticalEventProto sanitizeAnr(CriticalEventProto base) {
- CriticalEventProto sanitized = new CriticalEventProto();
- sanitized.timestampMs = base.timestampMs;
AppNotResponding anr = new AppNotResponding();
- sanitized.setAnr(anr);
// Do not set subject and process.
anr.processClass = base.getAnr().processClass;
anr.uid = base.getAnr().uid;
anr.pid = base.getAnr().pid;
+
+ CriticalEventProto sanitized = sanitizeCriticalEventProto(base);
+ sanitized.setAnr(anr);
+ return sanitized;
+ }
+
+ private static CriticalEventProto sanitizeJavaCrash(CriticalEventProto base) {
+ JavaCrash crash = new JavaCrash();
+ // Do not set exceptionClass and process.
+ crash.processClass = base.getJavaCrash().processClass;
+ crash.uid = base.getJavaCrash().uid;
+ crash.pid = base.getJavaCrash().pid;
+
+ CriticalEventProto sanitized = sanitizeCriticalEventProto(base);
+ sanitized.setJavaCrash(crash);
+ return sanitized;
+ }
+
+ private static CriticalEventProto sanitizeNativeCrash(CriticalEventProto base) {
+ NativeCrash crash = new NativeCrash();
+ // Do not set process.
+ crash.processClass = base.getNativeCrash().processClass;
+ crash.uid = base.getNativeCrash().uid;
+ crash.pid = base.getNativeCrash().pid;
+
+ CriticalEventProto sanitized = sanitizeCriticalEventProto(base);
+ sanitized.setNativeCrash(crash);
+ return sanitized;
+ }
+
+ private static CriticalEventProto sanitizeCriticalEventProto(CriticalEventProto base) {
+ CriticalEventProto sanitized = new CriticalEventProto();
+ sanitized.timestampMs = base.timestampMs;
return sanitized;
}
}
diff --git a/services/core/java/com/android/server/health/HealthHalCallbackHidl.java b/services/core/java/com/android/server/health/HealthHalCallbackHidl.java
index 6b4d7b7..7a66980 100644
--- a/services/core/java/com/android/server/health/HealthHalCallbackHidl.java
+++ b/services/core/java/com/android/server/health/HealthHalCallbackHidl.java
@@ -16,6 +16,8 @@
package com.android.server.health;
+import static android.hardware.health.Translate.h2aTranslate;
+
import android.annotation.NonNull;
import android.hardware.health.V2_0.IHealth;
import android.hardware.health.V2_0.Result;
@@ -64,12 +66,12 @@
propsLatest.batteryChargeTimeToFullNowSeconds =
Constants.BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED;
- mCallback.update(propsLatest);
+ mCallback.update(h2aTranslate(propsLatest));
}
@Override
public void healthInfoChanged_2_1(android.hardware.health.V2_1.HealthInfo props) {
- mCallback.update(props);
+ mCallback.update(h2aTranslate(props));
}
// on new service registered
diff --git a/services/core/java/com/android/server/health/HealthInfoCallback.java b/services/core/java/com/android/server/health/HealthInfoCallback.java
index 8136ca0..c2a77fc 100644
--- a/services/core/java/com/android/server/health/HealthInfoCallback.java
+++ b/services/core/java/com/android/server/health/HealthInfoCallback.java
@@ -27,6 +27,5 @@
*
* @param props the new health info.
*/
- // TODO(b/177269435): AIDL
- void update(android.hardware.health.V2_1.HealthInfo props);
+ void update(android.hardware.health.HealthInfo props);
}
diff --git a/services/core/java/com/android/server/health/HealthServiceWrapper.java b/services/core/java/com/android/server/health/HealthServiceWrapper.java
index 0b43f26..9b97554 100644
--- a/services/core/java/com/android/server/health/HealthServiceWrapper.java
+++ b/services/core/java/com/android/server/health/HealthServiceWrapper.java
@@ -68,8 +68,7 @@
* service-specific error when calling {@code getHealthInfo}, e.g. it is unsupported.
* @throws RemoteException for any transaction-level errors
*/
- // TODO(b/177269435): AIDL
- public abstract android.hardware.health.V1_0.HealthInfo getHealthInfo() throws RemoteException;
+ public abstract android.hardware.health.HealthInfo getHealthInfo() throws RemoteException;
/**
* Create a new HealthServiceWrapper instance.
diff --git a/services/core/java/com/android/server/health/HealthServiceWrapperHidl.java b/services/core/java/com/android/server/health/HealthServiceWrapperHidl.java
index 3bff2f8..0301174 100644
--- a/services/core/java/com/android/server/health/HealthServiceWrapperHidl.java
+++ b/services/core/java/com/android/server/health/HealthServiceWrapperHidl.java
@@ -16,9 +16,11 @@
package com.android.server.health;
+import static android.hardware.health.Translate.h2aTranslate;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.hardware.health.V1_0.HealthInfo;
+import android.hardware.health.HealthInfo;
import android.hardware.health.V2_0.IHealth;
import android.hardware.health.V2_0.Result;
import android.hidl.manager.V1_0.IServiceManager;
@@ -155,7 +157,7 @@
service.getHealthInfo(
(result, value) -> {
if (result == Result.SUCCESS) {
- ret.value = value.legacy;
+ ret.value = h2aTranslate(value.legacy);
}
});
return ret.value;
diff --git a/services/core/java/com/android/server/health/Utils.java b/services/core/java/com/android/server/health/Utils.java
index fc039eb..a8c978c 100644
--- a/services/core/java/com/android/server/health/Utils.java
+++ b/services/core/java/com/android/server/health/Utils.java
@@ -50,4 +50,36 @@
dst.batteryChargeCounter = src.batteryChargeCounter;
dst.batteryTechnology = src.batteryTechnology;
}
+
+ /**
+ * Copy battery fields of {@link android.hardware.health.HealthInfo} V1. This excludes
+ * non-battery fields like {@link android.hardware.health.HealthInfo#diskStats diskStats} and
+ * {@link android.hardware.health.HealthInfo#storageInfos storageInfos}
+ *
+ * @param dst destination
+ * @param src source
+ */
+ public static void copyV1Battery(
+ android.hardware.health.HealthInfo dst, android.hardware.health.HealthInfo src) {
+ dst.chargerAcOnline = src.chargerAcOnline;
+ dst.chargerUsbOnline = src.chargerUsbOnline;
+ dst.chargerWirelessOnline = src.chargerWirelessOnline;
+ dst.maxChargingCurrentMicroamps = src.maxChargingCurrentMicroamps;
+ dst.maxChargingVoltageMicrovolts = src.maxChargingVoltageMicrovolts;
+ dst.batteryStatus = src.batteryStatus;
+ dst.batteryHealth = src.batteryHealth;
+ dst.batteryPresent = src.batteryPresent;
+ dst.batteryLevel = src.batteryLevel;
+ dst.batteryVoltageMillivolts = src.batteryVoltageMillivolts;
+ dst.batteryTemperatureTenthsCelsius = src.batteryTemperatureTenthsCelsius;
+ dst.batteryCurrentMicroamps = src.batteryCurrentMicroamps;
+ dst.batteryCycleCount = src.batteryCycleCount;
+ dst.batteryFullChargeUah = src.batteryFullChargeUah;
+ dst.batteryChargeCounterUah = src.batteryChargeCounterUah;
+ dst.batteryTechnology = src.batteryTechnology;
+ dst.batteryCurrentAverageMicroamps = src.batteryCurrentAverageMicroamps;
+ dst.batteryCapacityLevel = src.batteryCapacityLevel;
+ dst.batteryChargeTimeToFullNowSeconds = src.batteryChargeTimeToFullNowSeconds;
+ dst.batteryFullChargeDesignCapacityUah = src.batteryFullChargeDesignCapacityUah;
+ }
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index d14ef16..d74b3d7 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -1827,7 +1827,7 @@
broadcastFilterForAllUsers.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
mContext.registerReceiverAsUser(new ImmsBroadcastReceiverForAllUsers(),
UserHandle.ALL, broadcastFilterForAllUsers, null, null,
- Context.RECEIVER_NOT_EXPORTED);
+ Context.RECEIVER_EXPORTED);
final String defaultImiId = mSettings.getSelectedInputMethod();
final boolean imeSelectedOnBoot = !TextUtils.isEmpty(defaultImiId);
@@ -2916,7 +2916,7 @@
final boolean needsToShowImeSwitcher = shouldShowImeSwitcherLocked(vis);
if (mStatusBar != null) {
mStatusBar.setImeWindowStatus(mCurTokenDisplayId, mCurToken, vis, backDisposition,
- needsToShowImeSwitcher, false /*isMultiClientImeEnabled*/);
+ needsToShowImeSwitcher);
}
final InputMethodInfo imi = mMethodMap.get(mCurMethodId);
if (imi != null && needsToShowImeSwitcher) {
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 31a82a3..42fed36 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -2151,6 +2151,23 @@
// Enabled notification listener only works within the same user.
return false;
}
+ // Verify whether package name and controller UID.
+ // It will indirectly check whether the caller has obtained the package name and UID
+ // via ControllerInfo or with the valid package name visibility.
+ try {
+ int actualControllerUid = mContext.getPackageManager().getPackageUidAsUser(
+ controllerPackageName,
+ UserHandle.getUserId(controllerUid));
+ if (controllerUid != actualControllerUid) {
+ Log.w(TAG, "Failed to check enabled notification listener. Package name and"
+ + " UID doesn't match");
+ return false;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Failed to check enabled notification listener. Package name doesn't"
+ + " exist");
+ return false;
+ }
if (mNotificationManager.hasEnabledNotificationListener(controllerPackageName,
UserHandle.getUserHandleForUid(controllerUid))) {
diff --git a/services/core/java/com/android/server/net/OWNERS b/services/core/java/com/android/server/net/OWNERS
index 28ae6a4..a15fc3e 100644
--- a/services/core/java/com/android/server/net/OWNERS
+++ b/services/core/java/com/android/server/net/OWNERS
@@ -1,11 +1,7 @@
set noparent
-codewiz@google.com
-jchalard@google.com
+include platform/packages/modules/Connectivity:/OWNERS
+
jsharkey@android.com
-junyulai@google.com
-lorenzo@google.com
-reminv@google.com
-satk@google.com
sudheersai@google.com
yamasani@google.com
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 5516109..cc965d3 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2612,7 +2612,8 @@
private int pullNotificationStates(int atomTag, List<StatsEvent> data) {
switch(atomTag) {
case PACKAGE_NOTIFICATION_PREFERENCES:
- mPreferencesHelper.pullPackagePreferencesStats(data);
+ mPreferencesHelper.pullPackagePreferencesStats(data,
+ getAllUsersNotificationPermissions());
break;
case PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES:
mPreferencesHelper.pullPackageChannelPreferencesStats(data);
@@ -5065,16 +5066,18 @@
final DumpFilter filter = DumpFilter.parseFromArguments(args);
final long token = Binder.clearCallingIdentity();
try {
+ final ArrayMap<Pair<Integer, String>, Boolean> pkgPermissions =
+ getAllUsersNotificationPermissions();
if (filter.stats) {
- dumpJson(pw, filter);
+ dumpJson(pw, filter, pkgPermissions);
} else if (filter.rvStats) {
dumpRemoteViewStats(pw, filter);
} else if (filter.proto) {
- dumpProto(fd, filter);
+ dumpProto(fd, filter, pkgPermissions);
} else if (filter.criticalPriority) {
dumpNotificationRecords(pw, filter);
} else {
- dumpImpl(pw, filter);
+ dumpImpl(pw, filter, pkgPermissions);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -5896,12 +5899,38 @@
return null;
}
- private void dumpJson(PrintWriter pw, @NonNull DumpFilter filter) {
+ // Gets packages that have requested notification permission, and whether that has been
+ // allowed/denied, for all users on the device.
+ // Returns a single map containing that info keyed by (uid, package name) for all users.
+ // Because this calls into mPermissionHelper, this method must never be called with a lock held.
+ @VisibleForTesting
+ protected ArrayMap<Pair<Integer, String>, Boolean> getAllUsersNotificationPermissions() {
+ // don't bother if migration is not enabled
+ if (!mEnableAppSettingMigration) {
+ return null;
+ }
+ ArrayMap<Pair<Integer, String>, Boolean> allPermissions = new ArrayMap<>();
+ final List<UserInfo> allUsers = mUm.getUsers();
+ // for each of these, get the package notification permissions that are associated
+ // with this user and add it to the map
+ for (UserInfo ui : allUsers) {
+ ArrayMap<Pair<Integer, String>, Boolean> userPermissions =
+ mPermissionHelper.getNotificationPermissionValues(
+ ui.getUserHandle().getIdentifier());
+ for (Pair<Integer, String> pair : userPermissions.keySet()) {
+ allPermissions.put(pair, userPermissions.get(pair));
+ }
+ }
+ return allPermissions;
+ }
+
+ private void dumpJson(PrintWriter pw, @NonNull DumpFilter filter,
+ ArrayMap<Pair<Integer, String>, Boolean> pkgPermissions) {
JSONObject dump = new JSONObject();
try {
dump.put("service", "Notification Manager");
- dump.put("bans", mPreferencesHelper.dumpBansJson(filter));
- dump.put("ranking", mPreferencesHelper.dumpJson(filter));
+ dump.put("bans", mPreferencesHelper.dumpBansJson(filter, pkgPermissions));
+ dump.put("ranking", mPreferencesHelper.dumpJson(filter, pkgPermissions));
dump.put("stats", mUsageStats.dumpJson(filter));
dump.put("channels", mPreferencesHelper.dumpChannelsJson(filter));
} catch (JSONException e) {
@@ -5919,7 +5948,8 @@
stats.dump(REPORT_REMOTE_VIEWS, pw, filter);
}
- private void dumpProto(FileDescriptor fd, @NonNull DumpFilter filter) {
+ private void dumpProto(FileDescriptor fd, @NonNull DumpFilter filter,
+ ArrayMap<Pair<Integer, String>, Boolean> pkgPermissions) {
final ProtoOutputStream proto = new ProtoOutputStream(fd);
synchronized (mNotificationLock) {
int N = mNotificationList.size();
@@ -5986,7 +6016,7 @@
long rankingToken = proto.start(NotificationServiceDumpProto.RANKING_CONFIG);
mRankingHelper.dump(proto, filter);
- mPreferencesHelper.dump(proto, filter);
+ mPreferencesHelper.dump(proto, filter, pkgPermissions);
proto.end(rankingToken);
}
@@ -6009,7 +6039,8 @@
}
}
- void dumpImpl(PrintWriter pw, @NonNull DumpFilter filter) {
+ void dumpImpl(PrintWriter pw, @NonNull DumpFilter filter,
+ ArrayMap<Pair<Integer, String>, Boolean> pkgPermissions) {
pw.print("Current Notification Manager state");
if (filter.filtered) {
pw.print(" (filtered to "); pw.print(filter); pw.print(")");
@@ -6088,7 +6119,7 @@
mRankingHelper.dump(pw, " ", filter);
pw.println("\n Notification Preferences:");
- mPreferencesHelper.dump(pw, " ", filter);
+ mPreferencesHelper.dump(pw, " ", filter, pkgPermissions);
pw.println("\n Notification listeners:");
mListeners.dump(pw, filter);
@@ -6784,11 +6815,13 @@
// blocked apps
+ boolean isMediaNotification = n.isMediaNotification()
+ && n.extras.getParcelable(Notification.EXTRA_MEDIA_SESSION) != null;
boolean isBlocked = !areNotificationsEnabledForPackageInt(pkg, uid);
synchronized (mNotificationLock) {
isBlocked |= isRecordBlockedLocked(r);
}
- if (isBlocked) {
+ if (isBlocked && !isMediaNotification) {
if (DBG) {
Slog.e(TAG, "Suppressing notification from package " + r.getSbn().getPackageName()
+ " by user request.");
@@ -7174,7 +7207,13 @@
return;
}
- if (appBanned || isRecordBlockedLocked(r)) {
+ final StatusBarNotification n = r.getSbn();
+ final Notification notification = n.getNotification();
+
+ boolean isMediaNotification = notification.isMediaNotification()
+ && notification.extras.getParcelable(
+ Notification.EXTRA_MEDIA_SESSION) != null;
+ if (!isMediaNotification && (appBanned || isRecordBlockedLocked(r))) {
mUsageStats.registerBlocked(r);
if (DBG) {
Slog.e(TAG, "Suppressing notification from package " + pkg);
@@ -7189,8 +7228,6 @@
mUsageStats.registerSuspendedByAdmin(r);
}
NotificationRecord old = mNotificationsByKey.get(key);
- final StatusBarNotification n = r.getSbn();
- final Notification notification = n.getNotification();
// Make sure the SBN has an instance ID for statsd logging.
if (old == null || old.getSbn().getInstanceId() == null) {
@@ -7837,7 +7874,10 @@
final int waitMs = mAudioManager.getFocusRampTimeMs(
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
record.getAudioAttributes());
- if (DBG) Slog.v(TAG, "Delaying vibration by " + waitMs + "ms");
+ if (DBG) {
+ Slog.v(TAG, "Delaying vibration for notification "
+ + record.getKey() + " by " + waitMs + "ms");
+ }
try {
Thread.sleep(waitMs);
} catch (InterruptedException e) { }
@@ -7845,9 +7885,17 @@
// so need to check the notification still valide for vibrate.
synchronized (mNotificationLock) {
if (mNotificationsByKey.get(record.getKey()) != null) {
- vibrate(record, effect, true);
+ if (record.getKey().equals(mVibrateNotificationKey)) {
+ vibrate(record, effect, true);
+ } else {
+ if (DBG) {
+ Slog.v(TAG, "No vibration for notification "
+ + record.getKey() + ": a new notification is "
+ + "vibrating, or effects were cleared while waiting");
+ }
+ }
} else {
- Slog.e(TAG, "No vibration for canceled notification : "
+ Slog.w(TAG, "No vibration for canceled notification "
+ record.getKey());
}
}
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 16fe4b6..cfa6ba5 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -1931,31 +1931,41 @@
}
public void dump(PrintWriter pw, String prefix,
- @NonNull NotificationManagerService.DumpFilter filter) {
+ @NonNull NotificationManagerService.DumpFilter filter,
+ ArrayMap<Pair<Integer, String>, Boolean> pkgPermissions) {
pw.print(prefix);
pw.println("per-package config version: " + XML_VERSION);
pw.println("PackagePreferences:");
synchronized (mPackagePreferences) {
- dumpPackagePreferencesLocked(pw, prefix, filter, mPackagePreferences);
+ dumpPackagePreferencesLocked(pw, prefix, filter, mPackagePreferences, pkgPermissions);
}
pw.println("Restored without uid:");
- dumpPackagePreferencesLocked(pw, prefix, filter, mRestoredWithoutUids);
+ dumpPackagePreferencesLocked(pw, prefix, filter, mRestoredWithoutUids, null);
}
public void dump(ProtoOutputStream proto,
- @NonNull NotificationManagerService.DumpFilter filter) {
+ @NonNull NotificationManagerService.DumpFilter filter,
+ ArrayMap<Pair<Integer, String>, Boolean> pkgPermissions) {
synchronized (mPackagePreferences) {
dumpPackagePreferencesLocked(proto, RankingHelperProto.RECORDS, filter,
- mPackagePreferences);
+ mPackagePreferences, pkgPermissions);
}
dumpPackagePreferencesLocked(proto, RankingHelperProto.RECORDS_RESTORED_WITHOUT_UID, filter,
- mRestoredWithoutUids);
+ mRestoredWithoutUids, null);
}
private void dumpPackagePreferencesLocked(PrintWriter pw, String prefix,
@NonNull NotificationManagerService.DumpFilter filter,
- ArrayMap<String, PackagePreferences> packagePreferences) {
+ ArrayMap<String, PackagePreferences> packagePreferences,
+ ArrayMap<Pair<Integer, String>, Boolean> packagePermissions) {
+ // Used for tracking which package preferences we've seen already for notification
+ // permission reasons; after handling packages with local preferences, we'll want to dump
+ // the ones with notification permissions set but not local prefs.
+ Set<Pair<Integer, String>> pkgsWithPermissionsToHandle = null;
+ if (packagePermissions != null) {
+ pkgsWithPermissionsToHandle = packagePermissions.keySet();
+ }
final int N = packagePreferences.size();
for (int i = 0; i < N; i++) {
final PackagePreferences r = packagePreferences.valueAt(i);
@@ -1966,9 +1976,21 @@
pw.print(" (");
pw.print(r.uid == UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(r.uid));
pw.print(')');
- if (!mPermissionHelper.isMigrationEnabled() && r.importance != DEFAULT_IMPORTANCE) {
- pw.print(" importance=");
- pw.print(NotificationListenerService.Ranking.importanceToString(r.importance));
+ if (!mPermissionHelper.isMigrationEnabled()) {
+ if (r.importance != DEFAULT_IMPORTANCE) {
+ pw.print(" importance=");
+ pw.print(NotificationListenerService.Ranking.importanceToString(
+ r.importance));
+ }
+ } else {
+ Pair<Integer, String> key = new Pair<>(r.uid, r.pkg);
+ if (packagePermissions != null && pkgsWithPermissionsToHandle.contains(key)) {
+ pw.print(" importance=");
+ pw.print(NotificationListenerService.Ranking.importanceToString(
+ packagePermissions.get(key)
+ ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE));
+ pkgsWithPermissionsToHandle.remove(key);
+ }
}
if (r.priority != DEFAULT_PRIORITY) {
pw.print(" priority=");
@@ -2007,11 +2029,34 @@
}
}
}
+ // Handle any remaining packages with permissions
+ if (mPermissionHelper.isMigrationEnabled() && pkgsWithPermissionsToHandle != null) {
+ for (Pair<Integer, String> p : pkgsWithPermissionsToHandle) {
+ // p.first is the uid of this package; p.second is the package name
+ if (filter.matches(p.second)) {
+ pw.print(prefix);
+ pw.print(" AppSettings: ");
+ pw.print(p.second);
+ pw.print(" (");
+ pw.print(p.first == UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(p.first));
+ pw.print(')');
+ pw.print(" importance=");
+ pw.print(NotificationListenerService.Ranking.importanceToString(
+ packagePermissions.get(p) ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE));
+ }
+ }
+ }
}
- private static void dumpPackagePreferencesLocked(ProtoOutputStream proto, long fieldId,
+ private void dumpPackagePreferencesLocked(ProtoOutputStream proto, long fieldId,
@NonNull NotificationManagerService.DumpFilter filter,
- ArrayMap<String, PackagePreferences> packagePreferences) {
+ ArrayMap<String, PackagePreferences> packagePreferences,
+ ArrayMap<Pair<Integer, String>, Boolean> packagePermissions) {
+ Set<Pair<Integer, String>> pkgsWithPermissionsToHandle = null;
+ if (packagePermissions != null) {
+ pkgsWithPermissionsToHandle = packagePermissions.keySet();
+ }
+
final int N = packagePreferences.size();
long fToken;
for (int i = 0; i < N; i++) {
@@ -2021,7 +2066,16 @@
proto.write(RankingHelperProto.RecordProto.PACKAGE, r.pkg);
proto.write(RankingHelperProto.RecordProto.UID, r.uid);
- proto.write(RankingHelperProto.RecordProto.IMPORTANCE, r.importance);
+ if (mPermissionHelper.isMigrationEnabled()) {
+ Pair<Integer, String> key = new Pair<>(r.uid, r.pkg);
+ if (packagePermissions != null && pkgsWithPermissionsToHandle.contains(key)) {
+ proto.write(RankingHelperProto.RecordProto.IMPORTANCE,
+ packagePermissions.get(key) ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE);
+ pkgsWithPermissionsToHandle.remove(key);
+ }
+ } else {
+ proto.write(RankingHelperProto.RecordProto.IMPORTANCE, r.importance);
+ }
proto.write(RankingHelperProto.RecordProto.PRIORITY, r.priority);
proto.write(RankingHelperProto.RecordProto.VISIBILITY, r.visibility);
proto.write(RankingHelperProto.RecordProto.SHOW_BADGE, r.showBadge);
@@ -2036,28 +2090,82 @@
proto.end(fToken);
}
}
+
+ if (mPermissionHelper.isMigrationEnabled() && pkgsWithPermissionsToHandle != null) {
+ for (Pair<Integer, String> p : pkgsWithPermissionsToHandle) {
+ if (filter.matches(p.second)) {
+ fToken = proto.start(fieldId);
+ proto.write(RankingHelperProto.RecordProto.PACKAGE, p.second);
+ proto.write(RankingHelperProto.RecordProto.UID, p.first);
+ proto.write(RankingHelperProto.RecordProto.IMPORTANCE,
+ packagePermissions.get(p) ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE);
+ proto.end(fToken);
+ }
+ }
+ }
}
/**
* Fills out {@link PackageNotificationPreferences} proto and wraps it in a {@link StatsEvent}.
*/
- public void pullPackagePreferencesStats(List<StatsEvent> events) {
+ public void pullPackagePreferencesStats(List<StatsEvent> events,
+ ArrayMap<Pair<Integer, String>, Boolean> pkgPermissions) {
+ Set<Pair<Integer, String>> pkgsWithPermissionsToHandle = null;
+ if (pkgPermissions != null) {
+ pkgsWithPermissionsToHandle = pkgPermissions.keySet();
+ }
+ int pulledEvents = 0;
synchronized (mPackagePreferences) {
for (int i = 0; i < mPackagePreferences.size(); i++) {
- if (i > NOTIFICATION_PREFERENCES_PULL_LIMIT) {
+ if (pulledEvents > NOTIFICATION_PREFERENCES_PULL_LIMIT) {
break;
}
+ pulledEvents++;
SysUiStatsEvent.Builder event = mStatsEventBuilderFactory.newBuilder()
.setAtomId(PACKAGE_NOTIFICATION_PREFERENCES);
final PackagePreferences r = mPackagePreferences.valueAt(i);
event.writeInt(r.uid);
event.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
- event.writeInt(r.importance);
+ if (mPermissionHelper.isMigrationEnabled()) {
+ // Even if this package's data is not present, we need to write something;
+ // so default to IMPORTANCE_NONE, since if PM doesn't know about the package
+ // for some reason, notifications are not allowed.
+ int importance = IMPORTANCE_NONE;
+ Pair<Integer, String> key = new Pair<>(r.uid, r.pkg);
+ if (pkgPermissions != null && pkgsWithPermissionsToHandle.contains(key)) {
+ importance = pkgPermissions.get(key) ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE;
+ pkgsWithPermissionsToHandle.remove(key);
+ }
+ event.writeInt(importance);
+ } else {
+ event.writeInt(r.importance);
+ }
event.writeInt(r.visibility);
event.writeInt(r.lockedAppFields);
events.add(event.build());
}
}
+
+ // handle remaining packages with PackageManager permissions but not local settings
+ if (mPermissionHelper.isMigrationEnabled() && pkgPermissions != null) {
+ for (Pair<Integer, String> p : pkgsWithPermissionsToHandle) {
+ if (pulledEvents > NOTIFICATION_PREFERENCES_PULL_LIMIT) {
+ break;
+ }
+ pulledEvents++;
+ SysUiStatsEvent.Builder event = mStatsEventBuilderFactory.newBuilder()
+ .setAtomId(PACKAGE_NOTIFICATION_PREFERENCES);
+ event.writeInt(p.first);
+ event.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
+ event.writeInt(pkgPermissions.get(p) ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE);
+
+ // fill out the rest of the fields with default values so as not to confuse the
+ // builder
+ event.writeInt(DEFAULT_VISIBILITY);
+ event.writeInt(DEFAULT_LOCKED_APP_FIELDS);
+ events.add(event.build());
+ }
+ }
}
/**
@@ -2126,7 +2234,8 @@
}
}
- public JSONObject dumpJson(NotificationManagerService.DumpFilter filter) {
+ public JSONObject dumpJson(NotificationManagerService.DumpFilter filter,
+ ArrayMap<Pair<Integer, String>, Boolean> pkgPermissions) {
JSONObject ranking = new JSONObject();
JSONArray PackagePreferencess = new JSONArray();
try {
@@ -2134,6 +2243,13 @@
} catch (JSONException e) {
// pass
}
+
+ // Track data that we've handled from the permissions-based list
+ Set<Pair<Integer, String>> pkgsWithPermissionsToHandle = null;
+ if (pkgPermissions != null) {
+ pkgsWithPermissionsToHandle = pkgPermissions.keySet();
+ }
+
synchronized (mPackagePreferences) {
final int N = mPackagePreferences.size();
for (int i = 0; i < N; i++) {
@@ -2143,10 +2259,22 @@
try {
PackagePreferences.put("userId", UserHandle.getUserId(r.uid));
PackagePreferences.put("packageName", r.pkg);
- if (r.importance != DEFAULT_IMPORTANCE) {
- PackagePreferences.put("importance",
- NotificationListenerService.Ranking.importanceToString(
- r.importance));
+ if (mPermissionHelper.isMigrationEnabled()) {
+ Pair<Integer, String> key = new Pair<>(r.uid, r.pkg);
+ if (pkgPermissions != null
+ && pkgsWithPermissionsToHandle.contains(key)) {
+ PackagePreferences.put("importance",
+ NotificationListenerService.Ranking.importanceToString(
+ pkgPermissions.get(key)
+ ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE));
+ pkgsWithPermissionsToHandle.remove(key);
+ }
+ } else {
+ if (r.importance != DEFAULT_IMPORTANCE) {
+ PackagePreferences.put("importance",
+ NotificationListenerService.Ranking.importanceToString(
+ r.importance));
+ }
}
if (r.priority != DEFAULT_PRIORITY) {
PackagePreferences.put("priority",
@@ -2176,6 +2304,27 @@
}
}
}
+
+ // handle packages for which there are permissions but no local settings
+ if (mPermissionHelper.isMigrationEnabled() && pkgsWithPermissionsToHandle != null) {
+ for (Pair<Integer, String> p : pkgsWithPermissionsToHandle) {
+ if (filter == null || filter.matches(p.second)) {
+ JSONObject PackagePreferences = new JSONObject();
+ try {
+ PackagePreferences.put("userId", UserHandle.getUserId(p.first));
+ PackagePreferences.put("packageName", p.second);
+ PackagePreferences.put("importance",
+ NotificationListenerService.Ranking.importanceToString(
+ pkgPermissions.get(p)
+ ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE));
+ } catch (JSONException e) {
+ // pass
+ }
+ PackagePreferencess.put(PackagePreferences);
+ }
+ }
+ }
+
try {
ranking.put("PackagePreferencess", PackagePreferencess);
} catch (JSONException e) {
@@ -2193,9 +2342,11 @@
* @param filter
* @return
*/
- public JSONArray dumpBansJson(NotificationManagerService.DumpFilter filter) {
+ public JSONArray dumpBansJson(NotificationManagerService.DumpFilter filter,
+ ArrayMap<Pair<Integer, String>, Boolean> pkgPermissions) {
JSONArray bans = new JSONArray();
- Map<Integer, String> packageBans = getPackageBans();
+ Map<Integer, String> packageBans = mPermissionHelper.isMigrationEnabled()
+ ? getPermissionBasedPackageBans(pkgPermissions) : getPackageBans();
for (Map.Entry<Integer, String> ban : packageBans.entrySet()) {
final int userId = UserHandle.getUserId(ban.getKey());
final String packageName = ban.getValue();
@@ -2228,6 +2379,21 @@
}
}
+ // Same functionality as getPackageBans by extracting the set of packages from the provided
+ // map that are disallowed from sending notifications.
+ protected Map<Integer, String> getPermissionBasedPackageBans(
+ ArrayMap<Pair<Integer, String>, Boolean> pkgPermissions) {
+ ArrayMap<Integer, String> packageBans = new ArrayMap<>();
+ if (pkgPermissions != null) {
+ for (Pair<Integer, String> p : pkgPermissions.keySet()) {
+ if (!pkgPermissions.get(p)) {
+ packageBans.put(p.first, p.second);
+ }
+ }
+ }
+ return packageBans;
+ }
+
/**
* Dump only the channel information as structured JSON for the stats collector.
*
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 93c92c0..3f3cfd6 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -1499,8 +1499,8 @@
ai.primaryCpuAbi = ps.getPrimaryCpuAbi();
ai.secondaryCpuAbi = ps.getSecondaryCpuAbi();
ai.setVersionCode(ps.getVersionCode());
- ai.flags = ps.getPkgFlags();
- ai.privateFlags = ps.getPkgPrivateFlags();
+ ai.flags = ps.getFlags();
+ ai.privateFlags = ps.getPrivateFlags();
pi.applicationInfo = PackageInfoWithoutStateUtils.generateDelegateApplicationInfo(
ai, flags, state, userId);
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index b792a17..9306741 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -829,7 +829,7 @@
}
final String packageName = ps.getPkg().getPackageName();
// Skip over if system app or static shared library
- if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0
+ if ((ps.getFlags() & ApplicationInfo.FLAG_SYSTEM) != 0
|| !TextUtils.isEmpty(ps.getPkg().getStaticSharedLibName())) {
continue;
}
diff --git a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
index 239a9d71..fac73f5 100644
--- a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
@@ -16,13 +16,10 @@
package com.android.server.pm;
-import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
-import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME;
import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME;
-import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
import static com.android.server.pm.PackageManagerService.SCAN_AS_APK_IN_APEX;
import static com.android.server.pm.PackageManagerService.SCAN_AS_PRIVILEGED;
import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM;
@@ -33,23 +30,18 @@
import static com.android.server.pm.PackageManagerService.SCAN_REQUIRE_KNOWN;
import static com.android.server.pm.PackageManagerService.SYSTEM_PARTITIONS;
import static com.android.server.pm.PackageManagerService.TAG;
-import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
import android.annotation.Nullable;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
import android.content.pm.parsing.ParsingPackageUtils;
import android.os.Environment;
import android.os.SystemClock;
import android.os.Trace;
import android.util.ArrayMap;
import android.util.EventLog;
-import android.util.Log;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.om.OverlayConfig;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.EventLogTags;
import com.android.server.pm.parsing.PackageCacher;
@@ -69,13 +61,12 @@
*/
final class InitAndSystemPackageHelper {
private final PackageManagerService mPm;
- private final RemovePackageHelper mRemovePackageHelper;
- private final AppDataHelper mAppDataHelper;
private final List<ScanPartition> mDirsToScanAsSystem;
private final int mScanFlags;
private final int mSystemParseFlags;
private final int mSystemScanFlags;
+ private final InstallPackageHelper mInstallPackageHelper;
/**
* Tracks new system packages [received in an OTA] that we expect to
@@ -85,11 +76,9 @@
private final ArrayMap<String, File> mExpectingBetter = new ArrayMap<>();
// TODO(b/198166813): remove PMS dependency
- InitAndSystemPackageHelper(PackageManagerService pm, RemovePackageHelper removePackageHelper,
- AppDataHelper appDataHelper) {
+ InitAndSystemPackageHelper(PackageManagerService pm) {
mPm = pm;
- mRemovePackageHelper = removePackageHelper;
- mAppDataHelper = appDataHelper;
+ mInstallPackageHelper = new InstallPackageHelper(pm);
mDirsToScanAsSystem = getSystemScanPartitions();
// Set flag to monitor and not change apk file paths when scanning install directories.
int scanFlags = SCAN_BOOTING | SCAN_INITIAL;
@@ -158,11 +147,88 @@
consumer -> mPm.forEachPackage(
pkg -> consumer.accept(pkg, pkg.isSystem(),
apkInApexPreInstalledPaths.get(pkg.getPackageName()))));
- cleanupSystemPackagesAndInstallStubs(packageParser, executorService, packageSettings,
- startTime, userIds);
+ // Prune any system packages that no longer exist.
+ final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();
+ // Stub packages must either be replaced with full versions in the /data
+ // partition or be disabled.
+ final List<String> stubSystemApps = new ArrayList<>();
+
+ if (!mPm.isOnlyCoreApps()) {
+ // do this first before mucking with mPackages for the "expecting better" case
+ updateStubSystemAppsList(stubSystemApps);
+ mInstallPackageHelper.prepareSystemPackageCleanUp(packageSettings,
+ possiblyDeletedUpdatedSystemApps, mExpectingBetter, userIds);
+ }
+
+ final int cachedSystemApps = PackageCacher.sCachedPackageReadCount.get();
+
+ // Remove any shared userIDs that have no associated packages
+ mPm.mSettings.pruneSharedUsersLPw();
+ final long systemScanTime = SystemClock.uptimeMillis() - startTime;
+ final int systemPackagesCount = mPm.mPackages.size();
+ Slog.i(TAG, "Finished scanning system apps. Time: " + systemScanTime
+ + " ms, packageCount: " + systemPackagesCount
+ + " , timePerPackage: "
+ + (systemPackagesCount == 0 ? 0 : systemScanTime / systemPackagesCount)
+ + " , cached: " + cachedSystemApps);
+ if (mPm.isDeviceUpgrading() && systemPackagesCount > 0) {
+ //CHECKSTYLE:OFF IndentationCheck
+ FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
+ BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME,
+ systemScanTime / systemPackagesCount);
+ //CHECKSTYLE:ON IndentationCheck
+ }
+
+ if (!mPm.isOnlyCoreApps()) {
+ EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
+ SystemClock.uptimeMillis());
+ scanDirTracedLI(mPm.getAppInstallDir(), 0, mScanFlags | SCAN_REQUIRE_KNOWN, 0,
+ packageParser, executorService);
+
+ }
+
+ List<Runnable> unfinishedTasks = executorService.shutdownNow();
+ if (!unfinishedTasks.isEmpty()) {
+ throw new IllegalStateException("Not all tasks finished before calling close: "
+ + unfinishedTasks);
+ }
+
+ if (!mPm.isOnlyCoreApps()) {
+ mInstallPackageHelper.cleanupDisabledPackageSettings(possiblyDeletedUpdatedSystemApps,
+ userIds, mScanFlags);
+ mInstallPackageHelper.checkExistingBetterPackages(mExpectingBetter,
+ stubSystemApps, mSystemScanFlags, mSystemParseFlags);
+
+ // Uncompress and install any stubbed system applications.
+ // This must be done last to ensure all stubs are replaced or disabled.
+ mInstallPackageHelper.installSystemStubPackages(stubSystemApps, mScanFlags);
+
+ final int cachedNonSystemApps = PackageCacher.sCachedPackageReadCount.get()
+ - cachedSystemApps;
+
+ final long dataScanTime = SystemClock.uptimeMillis() - systemScanTime - startTime;
+ final int dataPackagesCount = mPm.mPackages.size() - systemPackagesCount;
+ Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime
+ + " ms, packageCount: " + dataPackagesCount
+ + " , timePerPackage: "
+ + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount)
+ + " , cached: " + cachedNonSystemApps);
+ if (mPm.isDeviceUpgrading() && dataPackagesCount > 0) {
+ //CHECKSTYLE:OFF IndentationCheck
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
+ BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME,
+ dataScanTime / dataPackagesCount);
+ //CHECKSTYLE:OFF IndentationCheck
+ }
+ }
+ mExpectingBetter.clear();
+
+ mPm.mSettings.pruneRenamedPackagesLPw();
packageParser.close();
return overlayConfig;
}
+
/**
* First part of init dir scanning
*/
@@ -205,255 +271,15 @@
}
}
- /**
- * Second part of init dir scanning
- */
- @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
- private void cleanupSystemPackagesAndInstallStubs(PackageParser2 packageParser,
- ExecutorService executorService,
- WatchedArrayMap<String, PackageSetting> packageSettings,
- long startTime, int[] userIds) {
- // Prune any system packages that no longer exist.
- final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();
- // Stub packages must either be replaced with full versions in the /data
- // partition or be disabled.
- final List<String> stubSystemApps = new ArrayList<>();
-
- if (!mPm.isOnlyCoreApps()) {
- // do this first before mucking with mPackages for the "expecting better" case
- final int numPackages = mPm.mPackages.size();
- for (int index = 0; index < numPackages; index++) {
- final AndroidPackage pkg = mPm.mPackages.valueAt(index);
- if (pkg.isStub()) {
- stubSystemApps.add(pkg.getPackageName());
- }
- }
-
- // Iterates PackageSettings in reversed order because the item could be removed
- // during the iteration.
- for (int index = packageSettings.size() - 1; index >= 0; index--) {
- final PackageSetting ps = packageSettings.valueAt(index);
-
- /*
- * If this is not a system app, it can't be a
- * disable system app.
- */
- if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
- continue;
- }
-
- /*
- * If the package is scanned, it's not erased.
- */
- final AndroidPackage scannedPkg = mPm.mPackages.get(ps.getPackageName());
- final PackageSetting disabledPs =
- mPm.mSettings.getDisabledSystemPkgLPr(ps.getPackageName());
- if (scannedPkg != null) {
- /*
- * If the system app is both scanned and in the
- * disabled packages list, then it must have been
- * added via OTA. Remove it from the currently
- * scanned package so the previously user-installed
- * application can be scanned.
- */
- if (disabledPs != null) {
- logCriticalInfo(Log.WARN,
- "Expecting better updated system app for "
- + ps.getPackageName()
- + "; removing system app. Last known"
- + " codePath=" + ps.getPathString()
- + ", versionCode=" + ps.getVersionCode()
- + "; scanned versionCode="
- + scannedPkg.getLongVersionCode());
- mRemovePackageHelper.removePackageLI(scannedPkg, true);
- mExpectingBetter.put(ps.getPackageName(), ps.getPath());
- }
-
- continue;
- }
-
- if (disabledPs == null) {
- logCriticalInfo(Log.WARN, "System package " + ps.getPackageName()
- + " no longer exists; its data will be wiped");
- mRemovePackageHelper.removePackageDataLIF(ps, userIds, null, 0, false);
- } else {
- // we still have a disabled system package, but, it still might have
- // been removed. check the code path still exists and check there's
- // still a package. the latter can happen if an OTA keeps the same
- // code path, but, changes the package name.
- if (disabledPs.getPath() == null || !disabledPs.getPath().exists()
- || disabledPs.getPkg() == null) {
- possiblyDeletedUpdatedSystemApps.add(ps.getPackageName());
- } else {
- // We're expecting that the system app should remain disabled, but add
- // it to expecting better to recover in case the data version cannot
- // be scanned.
- mExpectingBetter.put(disabledPs.getPackageName(), disabledPs.getPath());
- }
- }
+ @GuardedBy("mPm.mLock")
+ private void updateStubSystemAppsList(List<String> stubSystemApps) {
+ final int numPackages = mPm.mPackages.size();
+ for (int index = 0; index < numPackages; index++) {
+ final AndroidPackage pkg = mPm.mPackages.valueAt(index);
+ if (pkg.isStub()) {
+ stubSystemApps.add(pkg.getPackageName());
}
}
-
- final int cachedSystemApps = PackageCacher.sCachedPackageReadCount.get();
-
- // Remove any shared userIDs that have no associated packages
- mPm.mSettings.pruneSharedUsersLPw();
- final long systemScanTime = SystemClock.uptimeMillis() - startTime;
- final int systemPackagesCount = mPm.mPackages.size();
- Slog.i(TAG, "Finished scanning system apps. Time: " + systemScanTime
- + " ms, packageCount: " + systemPackagesCount
- + " , timePerPackage: "
- + (systemPackagesCount == 0 ? 0 : systemScanTime / systemPackagesCount)
- + " , cached: " + cachedSystemApps);
- if (mPm.isDeviceUpgrading() && systemPackagesCount > 0) {
- //CHECKSTYLE:OFF IndentationCheck
- FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
- BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME,
- systemScanTime / systemPackagesCount);
- //CHECKSTYLE:ON IndentationCheck
- }
- if (!mPm.isOnlyCoreApps()) {
- EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
- SystemClock.uptimeMillis());
- scanDirTracedLI(mPm.getAppInstallDir(), 0, mScanFlags | SCAN_REQUIRE_KNOWN, 0,
- packageParser, executorService);
-
- }
-
- List<Runnable> unfinishedTasks = executorService.shutdownNow();
- if (!unfinishedTasks.isEmpty()) {
- throw new IllegalStateException("Not all tasks finished before calling close: "
- + unfinishedTasks);
- }
-
- if (!mPm.isOnlyCoreApps()) {
- final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm);
- // Remove disable package settings for updated system apps that were
- // removed via an OTA. If the update is no longer present, remove the
- // app completely. Otherwise, revoke their system privileges.
- for (int i = possiblyDeletedUpdatedSystemApps.size() - 1; i >= 0; --i) {
- final String packageName = possiblyDeletedUpdatedSystemApps.get(i);
- final AndroidPackage pkg = mPm.mPackages.get(packageName);
- final String msg;
-
- // remove from the disabled system list; do this first so any future
- // scans of this package are performed without this state
- mPm.mSettings.removeDisabledSystemPackageLPw(packageName);
-
- if (pkg == null) {
- // should have found an update, but, we didn't; remove everything
- msg = "Updated system package " + packageName
- + " no longer exists; removing its data";
- // Actual deletion of code and data will be handled by later
- // reconciliation step
- } else {
- // found an update; revoke system privileges
- msg = "Updated system package " + packageName
- + " no longer exists; rescanning package on data";
-
- // NOTE: We don't do anything special if a stub is removed from the
- // system image. But, if we were [like removing the uncompressed
- // version from the /data partition], this is where it'd be done.
-
- // remove the package from the system and re-scan it without any
- // special privileges
- mRemovePackageHelper.removePackageLI(pkg, true);
- try {
- final File codePath = new File(pkg.getPath());
- scanPackageHelper.scanPackageTracedLI(codePath, 0, mScanFlags, 0, null);
- } catch (PackageManagerException e) {
- Slog.e(TAG, "Failed to parse updated, ex-system package: "
- + e.getMessage());
- }
- }
-
- // one final check. if we still have a package setting [ie. it was
- // previously scanned and known to the system], but, we don't have
- // a package [ie. there was an error scanning it from the /data
- // partition], completely remove the package data.
- final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
- if (ps != null && mPm.mPackages.get(packageName) == null) {
- mRemovePackageHelper.removePackageDataLIF(ps, userIds, null, 0, false);
-
- }
- logCriticalInfo(Log.WARN, msg);
- }
-
- /*
- * Make sure all system apps that we expected to appear on
- * the userdata partition actually showed up. If they never
- * appeared, crawl back and revive the system version.
- */
- for (int i = 0; i < mExpectingBetter.size(); i++) {
- final String packageName = mExpectingBetter.keyAt(i);
- if (!mPm.mPackages.containsKey(packageName)) {
- final File scanFile = mExpectingBetter.valueAt(i);
-
- logCriticalInfo(Log.WARN, "Expected better " + packageName
- + " but never showed up; reverting to system");
-
- @ParsingPackageUtils.ParseFlags int reparseFlags = 0;
- @PackageManagerService.ScanFlags int rescanFlags = 0;
- for (int i1 = mDirsToScanAsSystem.size() - 1; i1 >= 0; i1--) {
- final ScanPartition partition = mDirsToScanAsSystem.get(i1);
- if (partition.containsPrivApp(scanFile)) {
- reparseFlags = mSystemParseFlags;
- rescanFlags = mSystemScanFlags | SCAN_AS_PRIVILEGED
- | partition.scanFlag;
- break;
- }
- if (partition.containsApp(scanFile)) {
- reparseFlags = mSystemParseFlags;
- rescanFlags = mSystemScanFlags | partition.scanFlag;
- break;
- }
- }
- if (rescanFlags == 0) {
- Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);
- continue;
- }
- mPm.mSettings.enableSystemPackageLPw(packageName);
-
- try {
- final AndroidPackage newPkg = scanPackageHelper.scanPackageTracedLI(
- scanFile, reparseFlags, rescanFlags, 0, null);
- // We rescanned a stub, add it to the list of stubbed system packages
- if (newPkg.isStub()) {
- stubSystemApps.add(packageName);
- }
- } catch (PackageManagerException e) {
- Slog.e(TAG, "Failed to parse original system package: "
- + e.getMessage());
- }
- }
- }
-
- // Uncompress and install any stubbed system applications.
- // This must be done last to ensure all stubs are replaced or disabled.
- new InstallPackageHelper(mPm).installSystemStubPackages(stubSystemApps, mScanFlags);
-
- final int cachedNonSystemApps = PackageCacher.sCachedPackageReadCount.get()
- - cachedSystemApps;
-
- final long dataScanTime = SystemClock.uptimeMillis() - systemScanTime - startTime;
- final int dataPackagesCount = mPm.mPackages.size() - systemPackagesCount;
- Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime
- + " ms, packageCount: " + dataPackagesCount
- + " , timePerPackage: "
- + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount)
- + " , cached: " + cachedNonSystemApps);
- if (mPm.isDeviceUpgrading() && dataPackagesCount > 0) {
- //CHECKSTYLE:OFF IndentationCheck
- FrameworkStatsLog.write(
- FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
- BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME,
- dataScanTime / dataPackagesCount);
- //CHECKSTYLE:OFF IndentationCheck
- }
- }
- mExpectingBetter.clear();
-
- mPm.mSettings.pruneRenamedPackagesLPw();
}
@GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
@@ -461,89 +287,13 @@
long currentTime, PackageParser2 packageParser, ExecutorService executorService) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
try {
- scanDirLI(scanDir, parseFlags, scanFlags, currentTime, packageParser, executorService);
+ mInstallPackageHelper.installSystemPackagesFromDir(scanDir, parseFlags, scanFlags,
+ currentTime, packageParser, executorService);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
- @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
- private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime,
- PackageParser2 packageParser, ExecutorService executorService) {
- final File[] files = scanDir.listFiles();
- if (ArrayUtils.isEmpty(files)) {
- Log.d(TAG, "No files in app dir " + scanDir);
- return;
- }
-
- if (DEBUG_PACKAGE_SCANNING) {
- Log.d(TAG, "Scanning app dir " + scanDir + " scanFlags=" + scanFlags
- + " flags=0x" + Integer.toHexString(parseFlags));
- }
-
- ParallelPackageParser parallelPackageParser =
- new ParallelPackageParser(packageParser, executorService);
-
- // Submit files for parsing in parallel
- int fileCount = 0;
- for (File file : files) {
- final boolean isPackage = (isApkFile(file) || file.isDirectory())
- && !PackageInstallerService.isStageName(file.getName());
- if (!isPackage) {
- // Ignore entries which are not packages
- continue;
- }
- parallelPackageParser.submit(file, parseFlags);
- fileCount++;
- }
-
- final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm);
- // Process results one by one
- for (; fileCount > 0; fileCount--) {
- ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
- Throwable throwable = parseResult.throwable;
- int errorCode = PackageManager.INSTALL_SUCCEEDED;
- String errorMsg = null;
-
- if (throwable == null) {
- // TODO(b/194319951): move lower in the scan chain
- // Static shared libraries have synthetic package names
- if (parseResult.parsedPackage.isStaticSharedLibrary()) {
- PackageManagerService.renameStaticSharedLibraryPackage(
- parseResult.parsedPackage);
- }
- try {
- scanPackageHelper.addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
- currentTime, null);
- } catch (PackageManagerException e) {
- errorCode = e.error;
- errorMsg = "Failed to scan " + parseResult.scanFile + ": " + e.getMessage();
- Slog.w(TAG, errorMsg);
- }
- } else if (throwable instanceof PackageManagerException) {
- PackageManagerException e = (PackageManagerException) throwable;
- errorCode = e.error;
- errorMsg = "Failed to parse " + parseResult.scanFile + ": " + e.getMessage();
- Slog.w(TAG, errorMsg);
- } else {
- throw new IllegalStateException("Unexpected exception occurred while parsing "
- + parseResult.scanFile, throwable);
- }
-
- if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0 && errorCode != INSTALL_SUCCEEDED) {
- mPm.mApexManager.reportErrorWithApkInApex(scanDir.getAbsolutePath(), errorMsg);
- }
-
- // Delete invalid userdata apps
- if ((scanFlags & SCAN_AS_SYSTEM) == 0
- && errorCode != PackageManager.INSTALL_SUCCEEDED) {
- logCriticalInfo(Log.WARN,
- "Deleting invalid package at " + parseResult.scanFile);
- mRemovePackageHelper.removeCodePathLI(parseResult.scanFile);
- }
- }
- }
-
public boolean isExpectingBetter(String packageName) {
return mExpectingBetter.containsKey(packageName);
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 8ba8e45..2e5a6ea 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -36,8 +36,10 @@
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_RESTORE;
import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP;
+import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
import static android.content.pm.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4;
+import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile;
import static android.os.PowerExemptionManager.REASON_PACKAGE_REPLACED;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.os.incremental.IncrementalManager.isIncrementalPath;
@@ -159,9 +161,9 @@
import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.permission.Permission;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
-import com.android.server.pm.pkg.PackageUserState;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.rollback.RollbackManagerInternal;
+import com.android.server.utils.WatchedArrayMap;
import com.android.server.utils.WatchedLongSparseArray;
import dalvik.system.VMRuntime;
@@ -185,6 +187,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.ExecutorService;
final class InstallPackageHelper {
/**
@@ -196,6 +199,8 @@
private final AppDataHelper mAppDataHelper;
private final PackageManagerServiceInjector mInjector;
private final BroadcastHelper mBroadcastHelper;
+ private final RemovePackageHelper mRemovePackageHelper;
+ private final ScanPackageHelper mScanPackageHelper;
// TODO(b/198166813): remove PMS dependency
InstallPackageHelper(PackageManagerService pm, AppDataHelper appDataHelper) {
@@ -203,6 +208,8 @@
mInjector = pm.mInjector;
mAppDataHelper = appDataHelper;
mBroadcastHelper = new BroadcastHelper(mInjector);
+ mRemovePackageHelper = new RemovePackageHelper(pm);
+ mScanPackageHelper = new ScanPackageHelper(pm);
}
InstallPackageHelper(PackageManagerService pm) {
@@ -214,6 +221,8 @@
mInjector = injector;
mAppDataHelper = new AppDataHelper(pm, mInjector);
mBroadcastHelper = new BroadcastHelper(injector);
+ mRemovePackageHelper = new RemovePackageHelper(pm);
+ mScanPackageHelper = new ScanPackageHelper(pm);
}
@GuardedBy("mPm.mLock")
@@ -1132,7 +1141,6 @@
new ArrayMap<>(requests.size());
final Map<String, Boolean> createdAppId = new ArrayMap<>(requests.size());
boolean success = false;
- final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm);
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackagesLI");
for (InstallRequest request : requests) {
@@ -1161,7 +1169,7 @@
installResults.put(packageName, request.mInstallResult);
installArgs.put(packageName, request.mArgs);
try {
- final ScanResult result = scanPackageHelper.scanPackageTracedLI(
+ final ScanResult result = mScanPackageHelper.scanPackageTracedLI(
prepareResult.mPackageToScan, prepareResult.mParseFlags,
prepareResult.mScanFlags, System.currentTimeMillis(),
request.mArgs.mUser, request.mArgs.mAbiOverride);
@@ -1174,8 +1182,7 @@
+ " in multi-package install request.");
return;
}
- createdAppId.put(packageName,
- scanPackageHelper.optimisticallyRegisterAppId(result));
+ createdAppId.put(packageName, optimisticallyRegisterAppId(result));
versionInfos.put(result.mPkgSetting.getPkg().getPackageName(),
mPm.getSettingsVersionForPackage(result.mPkgSetting.getPkg()));
if (result.mStaticSharedLibraryInfo != null) {
@@ -1253,7 +1260,7 @@
for (ScanResult result : preparedScans.values()) {
if (createdAppId.getOrDefault(result.mRequest.mParsedPackage.getPackageName(),
false)) {
- scanPackageHelper.cleanUpAppIdCreation(result);
+ cleanUpAppIdCreation(result);
}
}
// TODO(b/194319951): create a more descriptive reason than unknown
@@ -2185,15 +2192,16 @@
reconciledPkg.mPrepareResult.mExistingPackage.getPackageName());
if ((reconciledPkg.mInstallArgs.mInstallFlags & PackageManager.DONT_KILL_APP)
== 0) {
- if (ps1.mOldCodePaths == null) {
- ps1.mOldCodePaths = new ArraySet<>();
+ if (ps1.getOldCodePaths() == null) {
+ ps1.setOldCodePaths(new ArraySet<>());
}
- Collections.addAll(ps1.mOldCodePaths, oldPackage.getBaseApkPath());
+ Collections.addAll(ps1.getOldCodePaths(), oldPackage.getBaseApkPath());
if (oldPackage.getSplitCodePaths() != null) {
- Collections.addAll(ps1.mOldCodePaths, oldPackage.getSplitCodePaths());
+ Collections.addAll(ps1.getOldCodePaths(),
+ oldPackage.getSplitCodePaths());
}
} else {
- ps1.mOldCodePaths = null;
+ ps1.setOldCodePaths(null);
}
if (reconciledPkg.mInstallResult.mReturnCode
@@ -3357,9 +3365,8 @@
}
final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm);
removePackageHelper.removePackageLI(stubPkg, true /*chatty*/);
- final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm);
try {
- return scanPackageHelper.scanPackageTracedLI(scanFile, parseFlags, scanFlags, 0, null);
+ return scanSystemPackageTracedLI(scanFile, parseFlags, scanFlags, 0, null);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.getPackageName(),
e);
@@ -3509,9 +3516,7 @@
| ParsingPackageUtils.PARSE_MUST_BE_APK
| ParsingPackageUtils.PARSE_IS_SYSTEM_DIR;
@PackageManagerService.ScanFlags int scanFlags = mPm.getSystemPackageScanFlags(codePath);
- final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm);
- final AndroidPackage pkg =
- scanPackageHelper.scanPackageTracedLI(
+ final AndroidPackage pkg = scanSystemPackageTracedLI(
codePath, parseFlags, scanFlags, 0 /*currentTime*/, null);
PackageSetting pkgSetting = mPm.mSettings.getPackageLPr(pkg.getPackageName());
@@ -3582,4 +3587,388 @@
}
}
+ @GuardedBy("mPm.mLock")
+ public void prepareSystemPackageCleanUp(
+ WatchedArrayMap<String, PackageSetting> packageSettings,
+ List<String> possiblyDeletedUpdatedSystemApps,
+ ArrayMap<String, File> expectingBetter, int[] userIds) {
+ // Iterates PackageSettings in reversed order because the item could be removed
+ // during the iteration.
+ for (int index = packageSettings.size() - 1; index >= 0; index--) {
+ final PackageSetting ps = packageSettings.valueAt(index);
+ final String packageName = ps.getPackageName();
+ /*
+ * If this is not a system app, it can't be a
+ * disable system app.
+ */
+ if (!ps.isSystem()) {
+ continue;
+ }
+
+ /*
+ * If the package is scanned, it's not erased.
+ */
+ final AndroidPackage scannedPkg = mPm.mPackages.get(packageName);
+ final PackageSetting disabledPs =
+ mPm.mSettings.getDisabledSystemPkgLPr(packageName);
+ if (scannedPkg != null) {
+ /*
+ * If the system app is both scanned and in the
+ * disabled packages list, then it must have been
+ * added via OTA. Remove it from the currently
+ * scanned package so the previously user-installed
+ * application can be scanned.
+ */
+ if (disabledPs != null) {
+ logCriticalInfo(Log.WARN,
+ "Expecting better updated system app for "
+ + packageName
+ + "; removing system app. Last known"
+ + " codePath=" + ps.getPathString()
+ + ", versionCode=" + ps.getVersionCode()
+ + "; scanned versionCode="
+ + scannedPkg.getLongVersionCode());
+ mRemovePackageHelper.removePackageLI(scannedPkg, true);
+ expectingBetter.put(ps.getPackageName(), ps.getPath());
+ }
+
+ continue;
+ }
+
+ if (disabledPs == null) {
+ logCriticalInfo(Log.WARN, "System package " + packageName
+ + " no longer exists; its data will be wiped");
+ mRemovePackageHelper.removePackageDataLIF(ps, userIds, null, 0, false);
+ } else {
+ // we still have a disabled system package, but, it still might have
+ // been removed. check the code path still exists and check there's
+ // still a package. the latter can happen if an OTA keeps the same
+ // code path, but, changes the package name.
+ if (disabledPs.getPath() == null || !disabledPs.getPath().exists()
+ || disabledPs.getPkg() == null) {
+ possiblyDeletedUpdatedSystemApps.add(packageName);
+ } else {
+ // We're expecting that the system app should remain disabled, but add
+ // it to expecting better to recover in case the data version cannot
+ // be scanned.
+ expectingBetter.put(disabledPs.getPackageName(), disabledPs.getPath());
+ }
+ }
+ }
+ }
+
+ @GuardedBy("mPm.mLock")
+ // Remove disable package settings for updated system apps that were
+ // removed via an OTA. If the update is no longer present, remove the
+ // app completely. Otherwise, revoke their system privileges.
+ public void cleanupDisabledPackageSettings(List<String> possiblyDeletedUpdatedSystemApps,
+ int[] userIds, int scanFlags) {
+ for (int i = possiblyDeletedUpdatedSystemApps.size() - 1; i >= 0; --i) {
+ final String packageName = possiblyDeletedUpdatedSystemApps.get(i);
+ final AndroidPackage pkg = mPm.mPackages.get(packageName);
+ final String msg;
+
+ // remove from the disabled system list; do this first so any future
+ // scans of this package are performed without this state
+ mPm.mSettings.removeDisabledSystemPackageLPw(packageName);
+
+ if (pkg == null) {
+ // should have found an update, but, we didn't; remove everything
+ msg = "Updated system package " + packageName
+ + " no longer exists; removing its data";
+ // Actual deletion of code and data will be handled by later
+ // reconciliation step
+ } else {
+ // found an update; revoke system privileges
+ msg = "Updated system package " + packageName
+ + " no longer exists; rescanning package on data";
+
+ // NOTE: We don't do anything special if a stub is removed from the
+ // system image. But, if we were [like removing the uncompressed
+ // version from the /data partition], this is where it'd be done.
+
+ // remove the package from the system and re-scan it without any
+ // special privileges
+ mRemovePackageHelper.removePackageLI(pkg, true);
+ try {
+ final File codePath = new File(pkg.getPath());
+ scanSystemPackageTracedLI(codePath, 0, scanFlags, 0, null);
+ } catch (PackageManagerException e) {
+ Slog.e(TAG, "Failed to parse updated, ex-system package: "
+ + e.getMessage());
+ }
+ }
+
+ // one final check. if we still have a package setting [ie. it was
+ // previously scanned and known to the system], but, we don't have
+ // a package [ie. there was an error scanning it from the /data
+ // partition], completely remove the package data.
+ final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
+ if (ps != null && mPm.mPackages.get(packageName) == null) {
+ mRemovePackageHelper.removePackageDataLIF(ps, userIds, null, 0, false);
+ }
+ logCriticalInfo(Log.WARN, msg);
+ }
+ }
+
+ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+ public void installSystemPackagesFromDir(File scanDir, int parseFlags, int scanFlags,
+ long currentTime, PackageParser2 packageParser, ExecutorService executorService) {
+ final File[] files = scanDir.listFiles();
+ if (ArrayUtils.isEmpty(files)) {
+ Log.d(TAG, "No files in app dir " + scanDir);
+ return;
+ }
+
+ if (DEBUG_PACKAGE_SCANNING) {
+ Log.d(TAG, "Scanning app dir " + scanDir + " scanFlags=" + scanFlags
+ + " flags=0x" + Integer.toHexString(parseFlags));
+ }
+ ParallelPackageParser parallelPackageParser =
+ new ParallelPackageParser(packageParser, executorService);
+
+ // Submit files for parsing in parallel
+ int fileCount = 0;
+ for (File file : files) {
+ final boolean isPackage = (isApkFile(file) || file.isDirectory())
+ && !PackageInstallerService.isStageName(file.getName());
+ if (!isPackage) {
+ // Ignore entries which are not packages
+ continue;
+ }
+ parallelPackageParser.submit(file, parseFlags);
+ fileCount++;
+ }
+
+ // Process results one by one
+ for (; fileCount > 0; fileCount--) {
+ ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
+ Throwable throwable = parseResult.throwable;
+ int errorCode = PackageManager.INSTALL_SUCCEEDED;
+ String errorMsg = null;
+
+ if (throwable == null) {
+ // TODO(b/194319951): move lower in the scan chain
+ // Static shared libraries have synthetic package names
+ if (parseResult.parsedPackage.isStaticSharedLibrary()) {
+ PackageManagerService.renameStaticSharedLibraryPackage(
+ parseResult.parsedPackage);
+ }
+ try {
+ addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags, currentTime,
+ null);
+ } catch (PackageManagerException e) {
+ errorCode = e.error;
+ errorMsg = "Failed to scan " + parseResult.scanFile + ": " + e.getMessage();
+ Slog.w(TAG, errorMsg);
+ }
+ } else if (throwable instanceof PackageManagerException) {
+ PackageManagerException e = (PackageManagerException) throwable;
+ errorCode = e.error;
+ errorMsg = "Failed to parse " + parseResult.scanFile + ": " + e.getMessage();
+ Slog.w(TAG, errorMsg);
+ } else {
+ throw new IllegalStateException("Unexpected exception occurred while parsing "
+ + parseResult.scanFile, throwable);
+ }
+
+ if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0 && errorCode != INSTALL_SUCCEEDED) {
+ mPm.mApexManager.reportErrorWithApkInApex(scanDir.getAbsolutePath(), errorMsg);
+ }
+
+ // Delete invalid userdata apps
+ if ((scanFlags & SCAN_AS_SYSTEM) == 0
+ && errorCode != PackageManager.INSTALL_SUCCEEDED) {
+ logCriticalInfo(Log.WARN,
+ "Deleting invalid package at " + parseResult.scanFile);
+ mRemovePackageHelper.removeCodePathLI(parseResult.scanFile);
+ }
+ }
+ }
+
+ /**
+ * Make sure all system apps that we expected to appear on
+ * the userdata partition actually showed up. If they never
+ * appeared, crawl back and revive the system version.
+ */
+ @GuardedBy("mPm.mLock")
+ public void checkExistingBetterPackages(ArrayMap<String, File> expectingBetterPackages,
+ List<String> stubSystemApps, int systemScanFlags, int systemParseFlags) {
+ for (int i = 0; i < expectingBetterPackages.size(); i++) {
+ final String packageName = expectingBetterPackages.keyAt(i);
+ if (mPm.mPackages.containsKey(packageName)) {
+ continue;
+ }
+ final File scanFile = expectingBetterPackages.valueAt(i);
+
+ logCriticalInfo(Log.WARN, "Expected better " + packageName
+ + " but never showed up; reverting to system");
+
+ final Pair<Integer, Integer> rescanAndReparseFlags =
+ mPm.getSystemPackageRescanFlagsAndReparseFlags(scanFile,
+ systemScanFlags, systemParseFlags);
+ @PackageManagerService.ScanFlags int rescanFlags = rescanAndReparseFlags.first;
+ @ParsingPackageUtils.ParseFlags int reparseFlags = rescanAndReparseFlags.second;
+
+ if (rescanFlags == 0) {
+ Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);
+ continue;
+ }
+ mPm.mSettings.enableSystemPackageLPw(packageName);
+
+ try {
+ final AndroidPackage newPkg = scanSystemPackageTracedLI(
+ scanFile, reparseFlags, rescanFlags, 0, null);
+ // We rescanned a stub, add it to the list of stubbed system packages
+ if (newPkg.isStub()) {
+ stubSystemApps.add(packageName);
+ }
+ } catch (PackageManagerException e) {
+ Slog.e(TAG, "Failed to parse original system package: "
+ + e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Traces a package scan.
+ * @see #scanSystemPackageLI(File, int, int, long, UserHandle)
+ */
+ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+ public AndroidPackage scanSystemPackageTracedLI(File scanFile, final int parseFlags,
+ int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage [" + scanFile.toString() + "]");
+ try {
+ return scanSystemPackageLI(scanFile, parseFlags, scanFlags, currentTime, user);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+
+ /**
+ * Scans a package and returns the newly parsed package.
+ * Returns {@code null} in case of errors and the error code is stored in mLastScanError
+ */
+ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+ private AndroidPackage scanSystemPackageLI(File scanFile, int parseFlags, int scanFlags,
+ long currentTime, UserHandle user) throws PackageManagerException {
+ if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
+
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
+ final ParsedPackage parsedPackage;
+ try (PackageParser2 pp = mInjector.getScanningPackageParser()) {
+ parsedPackage = pp.parsePackage(scanFile, parseFlags, false);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+
+ // Static shared libraries have synthetic package names
+ if (parsedPackage.isStaticSharedLibrary()) {
+ PackageManagerService.renameStaticSharedLibraryPackage(parsedPackage);
+ }
+
+ return addForInitLI(parsedPackage, parseFlags, scanFlags, currentTime, user);
+ }
+
+ /**
+ * Adds a new package to the internal data structures during platform initialization.
+ * <p>After adding, the package is known to the system and available for querying.
+ * <p>For packages located on the device ROM [eg. packages located in /system, /vendor,
+ * etc...], additional checks are performed. Basic verification [such as ensuring
+ * matching signatures, checking version codes, etc...] occurs if the package is
+ * identical to a previously known package. If the package fails a signature check,
+ * the version installed on /data will be removed. If the version of the new package
+ * is less than or equal than the version on /data, it will be ignored.
+ * <p>Regardless of the package location, the results are applied to the internal
+ * structures and the package is made available to the rest of the system.
+ * <p>NOTE: The return value should be removed. It's the passed in package object.
+ */
+ @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
+ private AndroidPackage addForInitLI(ParsedPackage parsedPackage,
+ @ParsingPackageUtils.ParseFlags int parseFlags,
+ @PackageManagerService.ScanFlags int scanFlags, long currentTime,
+ @Nullable UserHandle user) throws PackageManagerException {
+
+ final Pair<ScanResult, Boolean> scanResultPair = mScanPackageHelper.scanSystemPackageLI(
+ parsedPackage, parseFlags, scanFlags, currentTime, user);
+ final ScanResult scanResult = scanResultPair.first;
+ boolean shouldHideSystemApp = scanResultPair.second;
+ if (scanResult.mSuccess) {
+ synchronized (mPm.mLock) {
+ boolean appIdCreated = false;
+ try {
+ final String pkgName = scanResult.mPkgSetting.getPackageName();
+ final Map<String, ReconciledPackage> reconcileResult =
+ reconcilePackagesLocked(
+ new ReconcileRequest(
+ Collections.singletonMap(pkgName, scanResult),
+ mPm.mSharedLibraries,
+ mPm.mPackages,
+ Collections.singletonMap(
+ pkgName,
+ mPm.getSettingsVersionForPackage(
+ parsedPackage)),
+ Collections.singletonMap(pkgName,
+ mPm.getSharedLibLatestVersionSetting(
+ scanResult))),
+ mPm.mSettings.getKeySetManagerService(), mInjector);
+ appIdCreated = optimisticallyRegisterAppId(scanResult);
+ commitReconciledScanResultLocked(
+ reconcileResult.get(pkgName), mPm.mUserManager.getUserIds());
+ } catch (PackageManagerException e) {
+ if (appIdCreated) {
+ cleanUpAppIdCreation(scanResult);
+ }
+ throw e;
+ }
+ }
+ }
+
+ if (shouldHideSystemApp) {
+ synchronized (mPm.mLock) {
+ mPm.mSettings.disableSystemPackageLPw(parsedPackage.getPackageName(), true);
+ }
+ }
+ if (mPm.mIncrementalManager != null && isIncrementalPath(parsedPackage.getPath())) {
+ if (scanResult.mPkgSetting != null && scanResult.mPkgSetting.isPackageLoading()) {
+ // Continue monitoring loading progress of active incremental packages
+ mPm.mIncrementalManager.registerLoadingProgressCallback(parsedPackage.getPath(),
+ new IncrementalProgressListener(parsedPackage.getPackageName(), mPm));
+ }
+ }
+ return scanResult.mPkgSetting.getPkg();
+ }
+
+ /**
+ * Prepares the system to commit a {@link ScanResult} in a way that will not fail by registering
+ * the app ID required for reconcile.
+ * @return {@code true} if a new app ID was registered and will need to be cleaned up on
+ * failure.
+ */
+ private boolean optimisticallyRegisterAppId(@NonNull ScanResult result)
+ throws PackageManagerException {
+ if (!result.mExistingSettingCopied || result.needsNewAppId()) {
+ synchronized (mPm.mLock) {
+ // THROWS: when we can't allocate a user id. add call to check if there's
+ // enough space to ensure we won't throw; otherwise, don't modify state
+ return mPm.mSettings.registerAppIdLPw(result.mPkgSetting, result.needsNewAppId());
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Reverts any app ID creation that were made by
+ * {@link #optimisticallyRegisterAppId(ScanResult)}. Note: this is only necessary if the
+ * referenced method returned true.
+ */
+ private void cleanUpAppIdCreation(@NonNull ScanResult result) {
+ // iff we've acquired an app ID for a new package setting, remove it so that it can be
+ // acquired by another request.
+ if (result.mPkgSetting.getAppId() > 0) {
+ mPm.mSettings.removeAppIdLPw(result.mPkgSetting.getAppId());
+ }
+ }
+
+
}
diff --git a/services/core/java/com/android/server/pm/InstallParams.java b/services/core/java/com/android/server/pm/InstallParams.java
index 0dff7d1..d996fe4 100644
--- a/services/core/java/com/android/server/pm/InstallParams.java
+++ b/services/core/java/com/android/server/pm/InstallParams.java
@@ -169,13 +169,15 @@
final long sizeBytes = PackageManagerServiceUtils.calculateInstalledSize(
mOriginInfo.mResolvedPath, mPackageAbiOverride);
if (sizeBytes >= 0) {
- try {
- mPm.mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
- pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mPm.mContext,
- mPackageLite, mOriginInfo.mResolvedPath, mInstallFlags,
- mPackageAbiOverride);
- } catch (Installer.InstallerException e) {
- Slog.w(TAG, "Failed to free cache", e);
+ synchronized (mPm.mInstallLock) {
+ try {
+ mPm.mInstaller.freeCache(null, sizeBytes + lowThreshold, 0);
+ pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mPm.mContext,
+ mPackageLite, mOriginInfo.mResolvedPath, mInstallFlags,
+ mPackageAbiOverride);
+ } catch (Installer.InstallerException e) {
+ Slog.w(TAG, "Failed to free cache", e);
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 55355d8..c8bd2c0 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -632,11 +632,15 @@
}
}
- public void freeCache(String uuid, long targetFreeBytes, long cacheReservedBytes, int flags)
- throws InstallerException {
+ /**
+ * Deletes cache from specified uuid until targetFreeBytes amount of space is free.
+ * flag denotes aggressive or non-aggresive mode where cache under quota is eligible or not
+ * respectively for clearing.
+ */
+ public void freeCache(String uuid, long targetFreeBytes, int flags) throws InstallerException {
if (!checkBeforeRemote()) return;
try {
- mInstalld.freeCache(uuid, targetFreeBytes, cacheReservedBytes, flags);
+ mInstalld.freeCache(uuid, targetFreeBytes, flags);
} catch (Exception e) {
throw InstallerException.from(e);
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index e845aec..a5b42f0 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -46,6 +46,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.SharedLibraryInfo;
@@ -65,6 +66,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.content.F2fsUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
import com.android.server.apphibernation.AppHibernationManagerInternal;
@@ -141,6 +143,7 @@
private final Injector mInjector;
+ private final Context mContext;
private static final Random sRandom = new Random();
PackageDexOptimizer(Installer installer, Object installLock, Context context,
@@ -159,6 +162,7 @@
}
protected PackageDexOptimizer(PackageDexOptimizer from) {
+ this.mContext = from.mContext;
this.mInstaller = from.mInstaller;
this.mInstallLock = from.mInstallLock;
this.mDexoptWakeLock = from.mDexoptWakeLock;
@@ -169,6 +173,7 @@
@VisibleForTesting
PackageDexOptimizer(@NonNull Injector injector, Installer installer, Object installLock,
Context context, String wakeLockTag) {
+ this.mContext = context;
this.mInstaller = installer;
this.mInstallLock = installLock;
@@ -434,6 +439,13 @@
long endTime = System.currentTimeMillis();
packageStats.setCompileTime(path, (int)(endTime - startTime));
}
+ if (oatDir != null) {
+ // Release odex/vdex compressed blocks to save user space.
+ // Compression support will be checked in F2fsUtils.
+ // The system app may be dexed, oatDir may be null, skip this situation.
+ final ContentResolver resolver = mContext.getContentResolver();
+ F2fsUtils.releaseCompressedBlocks(resolver, new File(oatDir));
+ }
return DEX_OPT_PERFORMED;
} catch (InstallerException e) {
Slog.w(TAG, "Failed to dexopt", e);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index fc266c8..de1c2ad 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -107,9 +107,13 @@
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.Random;
+import java.util.TreeMap;
+import java.util.TreeSet;
import java.util.function.IntPredicate;
import java.util.function.Supplier;
@@ -1436,13 +1440,74 @@
}
}
- void dump(IndentingPrintWriter pw) {
- synchronized (mSessions) {
- pw.println("Active install sessions:");
+ static class ParentChildSessionMap {
+ private TreeMap<PackageInstallerSession, TreeSet<PackageInstallerSession>> mSessionMap;
+
+ private final Comparator<PackageInstallerSession> mSessionCreationComparator =
+ Comparator.comparingLong((PackageInstallerSession sess) -> sess.createdMillis)
+ .thenComparingInt(sess -> sess.sessionId);
+
+ ParentChildSessionMap() {
+ mSessionMap = new TreeMap<>(mSessionCreationComparator);
+ }
+
+ boolean containsSession() {
+ return !(mSessionMap.isEmpty());
+ }
+
+ private void addParentSession(PackageInstallerSession session) {
+ if (!mSessionMap.containsKey(session)) {
+ mSessionMap.put(session, new TreeSet<>(mSessionCreationComparator));
+ }
+ }
+
+ private void addChildSession(PackageInstallerSession session,
+ PackageInstallerSession parentSession) {
+ addParentSession(parentSession);
+ mSessionMap.get(parentSession).add(session);
+ }
+
+ void addSession(PackageInstallerSession session,
+ PackageInstallerSession parentSession) {
+ if (session.hasParentSessionId()) {
+ addChildSession(session, parentSession);
+ } else {
+ addParentSession(session);
+ }
+ }
+
+ void dump(String tag, IndentingPrintWriter pw) {
+ pw.println(tag + " install sessions:");
pw.increaseIndent();
- List<PackageInstallerSession> finalizedSessions = new ArrayList<>();
- List<PackageInstallerSession> orphanedChildSessions = new ArrayList<>();
+ for (Map.Entry<PackageInstallerSession, TreeSet<PackageInstallerSession>> entry
+ : mSessionMap.entrySet()) {
+ PackageInstallerSession parentSession = entry.getKey();
+ pw.print(tag + " ");
+ parentSession.dump(pw);
+ pw.println();
+ pw.increaseIndent();
+
+ for (PackageInstallerSession childSession : entry.getValue()) {
+ pw.print(tag + " Child ");
+ childSession.dump(pw);
+ pw.println();
+ }
+
+ pw.decreaseIndent();
+ }
+
+ pw.println();
+ pw.decreaseIndent();
+ }
+ }
+
+ void dump(IndentingPrintWriter pw) {
+ synchronized (mSessions) {
+ ParentChildSessionMap activeSessionMap = new ParentChildSessionMap();
+ ParentChildSessionMap orphanedChildSessionMap = new ParentChildSessionMap();
+ ParentChildSessionMap finalizedSessionMap = new ParentChildSessionMap();
+
int N = mSessions.size();
for (int i = 0; i < N; i++) {
final PackageInstallerSession session = mSessions.valueAt(i);
@@ -1452,47 +1517,28 @@
: session;
// Do not print orphaned child sessions as active install sessions
if (rootSession == null) {
- orphanedChildSessions.add(session);
+ orphanedChildSessionMap.addSession(session, rootSession);
continue;
}
// Do not print finalized staged session as active install sessions
if (rootSession.isStagedAndInTerminalState()) {
- finalizedSessions.add(session);
+ finalizedSessionMap.addSession(session, rootSession);
continue;
}
- session.dump(pw);
- pw.println();
+ activeSessionMap.addSession(session, rootSession);
}
- pw.println();
- pw.decreaseIndent();
- if (!orphanedChildSessions.isEmpty()) {
+ activeSessionMap.dump("Active", pw);
+
+ if (orphanedChildSessionMap.containsSession()) {
// Presence of orphaned sessions indicate leak in cleanup for multi-package and
// should be cleaned up.
- pw.println("Orphaned install sessions:");
- pw.increaseIndent();
- N = orphanedChildSessions.size();
- for (int i = 0; i < N; i++) {
- final PackageInstallerSession session = orphanedChildSessions.get(i);
- session.dump(pw);
- pw.println();
- }
- pw.println();
- pw.decreaseIndent();
+ orphanedChildSessionMap.dump("Orphaned", pw);
}
- pw.println("Finalized install sessions:");
- pw.increaseIndent();
- N = finalizedSessions.size();
- for (int i = 0; i < N; i++) {
- final PackageInstallerSession session = finalizedSessions.get(i);
- session.dump(pw);
- pw.println();
- }
- pw.println();
- pw.decreaseIndent();
+ finalizedSessionMap.dump("Finalized", pw);
pw.println("Historical install sessions:");
pw.increaseIndent();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e71ac1a..aa005a3 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -146,13 +146,7 @@
import android.content.pm.parsing.component.ParsedIntentInfo;
import android.content.pm.parsing.component.ParsedMainComponent;
import android.content.pm.parsing.component.ParsedProvider;
-
-import com.android.server.pm.pkg.PackageStateInternal;
-import com.android.server.pm.pkg.PackageStateUtils;
-import com.android.server.pm.pkg.PackageUserState;
-import com.android.server.pm.pkg.PackageUserStateInternal;
import android.content.pm.pkg.PackageUserStateUtils;
-import com.android.server.pm.pkg.SuspendParams;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Bitmap;
@@ -257,6 +251,11 @@
import com.android.server.pm.pkg.AndroidPackageApi;
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateImpl;
+import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.PackageStateUtils;
+import com.android.server.pm.pkg.PackageUserState;
+import com.android.server.pm.pkg.PackageUserStateInternal;
+import com.android.server.pm.pkg.SuspendParams;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import com.android.server.pm.verify.domain.DomainVerificationService;
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
@@ -1829,8 +1828,7 @@
mBroadcastHelper = new BroadcastHelper(mInjector);
mAppDataHelper = new AppDataHelper(this);
mRemovePackageHelper = new RemovePackageHelper(this, mAppDataHelper);
- mInitAndSystemPackageHelper = new InitAndSystemPackageHelper(this, mRemovePackageHelper,
- mAppDataHelper);
+ mInitAndSystemPackageHelper = new InitAndSystemPackageHelper(this);
mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper,
mAppDataHelper);
mPreferredActivityHelper = new PreferredActivityHelper(this);
@@ -2073,7 +2071,7 @@
int size = packageSettings.size();
for (int i = 0; i < size; i++) {
final PackageSetting ps = packageSettings.valueAt(i);
- if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ if ((ps.getFlags() & ApplicationInfo.FLAG_SYSTEM) != 0) {
continue;
}
ps.disableComponentLPw(PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME,
@@ -2909,7 +2907,6 @@
volumeUuid);
final boolean aggressive = (storageFlags
& StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0;
- final long reservedBytes = storage.getStorageCacheBytes(file, storageFlags);
// 1. Pre-flight to determine if we have any chance to succeed
// 2. Consider preloaded data (after 1w honeymoon, unless aggressive)
@@ -2926,10 +2923,11 @@
}
// 4. Consider cached app data (above quotas)
- try {
- mInstaller.freeCache(volumeUuid, bytes, reservedBytes,
- Installer.FLAG_FREE_CACHE_V2);
- } catch (InstallerException ignored) {
+ synchronized (mInstallLock) {
+ try {
+ mInstaller.freeCache(volumeUuid, bytes, Installer.FLAG_FREE_CACHE_V2);
+ } catch (InstallerException ignored) {
+ }
}
if (file.getUsableSpace() >= bytes) return;
@@ -2953,10 +2951,12 @@
}
// 8. Consider cached app data (below quotas)
- try {
- mInstaller.freeCache(volumeUuid, bytes, reservedBytes,
- Installer.FLAG_FREE_CACHE_V2 | Installer.FLAG_FREE_CACHE_V2_DEFY_QUOTA);
- } catch (InstallerException ignored) {
+ synchronized (mInstallLock) {
+ try {
+ mInstaller.freeCache(volumeUuid, bytes,
+ Installer.FLAG_FREE_CACHE_V2 | Installer.FLAG_FREE_CACHE_V2_DEFY_QUOTA);
+ } catch (InstallerException ignored) {
+ }
}
if (file.getUsableSpace() >= bytes) return;
@@ -2982,9 +2982,11 @@
// 12. Clear temp install session files
mInstallerService.freeStageDirs(volumeUuid);
} else {
- try {
- mInstaller.freeCache(volumeUuid, bytes, 0, 0);
- } catch (InstallerException ignored) {
+ synchronized (mInstallLock) {
+ try {
+ mInstaller.freeCache(volumeUuid, bytes, 0);
+ } catch (InstallerException ignored) {
+ }
}
}
if (file.getUsableSpace() >= bytes) return;
@@ -3368,11 +3370,11 @@
final String libName = libInfo.getName();
if (libInfo.isStatic()) {
- final int libIdx = ArrayUtils.indexOf(ps.usesStaticLibraries, libName);
+ final int libIdx = ArrayUtils.indexOf(ps.getUsesStaticLibraries(), libName);
if (libIdx < 0) {
continue;
}
- if (ps.usesStaticLibrariesVersions[libIdx] != libInfo.getLongVersion()) {
+ if (ps.getUsesStaticLibrariesVersions()[libIdx] != libInfo.getLongVersion()) {
continue;
}
if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
@@ -4044,13 +4046,13 @@
if (shouldFilterApplicationLocked(sus, callingUid, callingUserId)) {
return 0;
}
- return sus.pkgFlags;
+ return sus.getFlags();
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
return 0;
}
- return ps.pkgFlags;
+ return ps.getFlags();
}
}
return 0;
@@ -4071,13 +4073,13 @@
if (shouldFilterApplicationLocked(sus, callingUid, callingUserId)) {
return 0;
}
- return sus.pkgPrivateFlags;
+ return sus.getPrivateFlags();
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
return 0;
}
- return ps.pkgPrivateFlags;
+ return ps.getPrivateFlags();
}
}
return 0;
@@ -6865,9 +6867,11 @@
return false;
}
if (systemUserApp) {
- ps.pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER;
+ ps.setPrivateFlags(ps.getPrivateFlags()
+ | ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER);
} else {
- ps.pkgPrivateFlags &= ~ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER;
+ ps.setPrivateFlags(ps.getPrivateFlags()
+ & ~ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER);
}
writeSettingsLPrTEMP();
}
@@ -7798,7 +7802,7 @@
: "Unknown package: " + packageName);
}
if (callingUid == Process.SHELL_UID
- && (pkgSetting.pkgFlags & ApplicationInfo.FLAG_TEST_ONLY) == 0) {
+ && (pkgSetting.getFlags() & ApplicationInfo.FLAG_TEST_ONLY) == 0) {
// Shell can only change whole packages between ENABLED and DISABLED_USER states
// unless it is a test package.
final int oldState = pkgSetting.getEnabled(userId);
@@ -11186,4 +11190,28 @@
}
return scanFlags;
}
+
+ Pair<Integer, Integer> getSystemPackageRescanFlagsAndReparseFlags(File scanFile,
+ int systemScanFlags, int systemParseFlags) {
+ List<ScanPartition> dirsToScanAsSystem =
+ mInitAndSystemPackageHelper.getDirsToScanAsSystem();
+ @ParsingPackageUtils.ParseFlags int reparseFlags = 0;
+ @PackageManagerService.ScanFlags int rescanFlags = 0;
+ for (int i1 = dirsToScanAsSystem.size() - 1; i1 >= 0; i1--) {
+ final ScanPartition partition = dirsToScanAsSystem.get(i1);
+ if (partition.containsPrivApp(scanFile)) {
+ reparseFlags = systemParseFlags;
+ rescanFlags = systemScanFlags | SCAN_AS_PRIVILEGED
+ | partition.scanFlag;
+ break;
+ }
+ if (partition.containsApp(scanFile)) {
+ reparseFlags = systemParseFlags;
+ rescanFlags = systemScanFlags | partition.scanFlag;
+ break;
+ }
+ }
+ return new Pair<>(rescanFlags, reparseFlags);
+ }
+
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 098c42e..d13a554 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -989,11 +989,11 @@
}
public static boolean isSystemApp(PackageSetting ps) {
- return (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0;
+ return (ps.getFlags() & ApplicationInfo.FLAG_SYSTEM) != 0;
}
public static boolean isUpdatedSystemApp(PackageSetting ps) {
- return (ps.pkgFlags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
+ return (ps.getFlags() & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
}
// Static to give access to ComputeEngine
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 3554cc0..fc59541 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -81,6 +81,7 @@
import android.os.ShellCommand;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.incremental.V4Signature;
@@ -2629,6 +2630,7 @@
if (userType == null) {
userType = UserInfo.getDefaultUserType(flags);
}
+ Trace.traceBegin(Trace.TRACE_TAG_PACKAGE_MANAGER, "shell_runCreateUser");
try {
if (UserManager.isUserTypeRestricted(userType)) {
// In non-split user mode, userId can only be SYSTEM
@@ -2645,6 +2647,8 @@
}
} catch (ServiceSpecificException e) {
getErrPrintWriter().println("Error: " + e);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_PACKAGE_MANAGER);
}
if (info != null) {
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 64fb630..de9c732 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -84,31 +84,20 @@
* shared user setting with the package setting. The shared user ID lets us link the
* two objects.
*/
- protected int sharedUserId;
+ private int sharedUserId;
- /**
- * @see PackageState#getMimeGroups()
- */
@Nullable
- Map<String, ArraySet<String>> mimeGroups;
+ private Map<String, Set<String>> mimeGroups;
- /**
- * Non-persisted value. During an "upgrade without restart", we need the set
- * of all previous code paths so we can surgically add the new APKs to the
- * active classloader. If at any point an application is upgraded with a
- * restart, this field will be cleared since the classloader would be created
- * using the full set of code paths when the package's process is started.
- * TODO: Remove
- */
@Deprecated
@Nullable
- Set<String> mOldCodePaths;
+ private Set<String> mOldCodePaths;
@Nullable
- String[] usesStaticLibraries;
+ private String[] usesStaticLibraries;
@Nullable
- long[] usesStaticLibrariesVersions;
+ private long[] usesStaticLibrariesVersions;
/**
* The path under which native libraries have been unpacked. This path is
@@ -220,7 +209,7 @@
String secondaryCpuAbi, String cpuAbiOverride,
long longVersionCode, int pkgFlags, int pkgPrivateFlags,
int sharedUserId, String[] usesStaticLibraries,
- long[] usesStaticLibrariesVersions, Map<String, ArraySet<String>> mimeGroups,
+ long[] usesStaticLibrariesVersions, Map<String, Set<String>> mimeGroups,
@NonNull UUID domainSetId) {
super(pkgFlags, pkgPrivateFlags);
this.mName = name;
@@ -322,7 +311,7 @@
}
public List<String> getMimeGroup(String mimeGroup) {
- ArraySet<String> mimeTypes = getMimeGroupInternal(mimeGroup);
+ Set<String> mimeTypes = getMimeGroupInternal(mimeGroup);
if (mimeTypes == null) {
throw new IllegalArgumentException("Unknown MIME group " + mimeGroup
+ " for package " + mName);
@@ -330,7 +319,7 @@
return new ArrayList<>(mimeTypes);
}
- private ArraySet<String> getMimeGroupInternal(String mimeGroup) {
+ private Set<String> getMimeGroupInternal(String mimeGroup) {
return mimeGroups != null ? mimeGroups.get(mimeGroup) : null;
}
@@ -411,7 +400,7 @@
}
public boolean setMimeGroup(String mimeGroup, List<String> mimeTypes) {
- ArraySet<String> oldMimeTypes = getMimeGroupInternal(mimeGroup);
+ Set<String> oldMimeTypes = getMimeGroupInternal(mimeGroup);
if (oldMimeTypes == null) {
throw new IllegalArgumentException("Unknown MIME group " + mimeGroup
+ " for package " + mName);
@@ -458,7 +447,7 @@
@Override
public boolean isExternalStorage() {
- return (pkgFlags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
+ return (getFlags() & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
}
public PackageSetting setUpdateAvailable(boolean updateAvailable) {
@@ -481,7 +470,7 @@
+ " " + mName + "/" + mAppId + "}";
}
- protected void copyMimeGroups(@Nullable Map<String, ArraySet<String>> newMimeGroups) {
+ protected void copyMimeGroups(@Nullable Map<String, Set<String>> newMimeGroups) {
if (newMimeGroups == null) {
mimeGroups = null;
return;
@@ -489,7 +478,7 @@
mimeGroups = new ArrayMap<>(newMimeGroups.size());
for (String mimeGroup : newMimeGroups.keySet()) {
- ArraySet<String> mimeTypes = newMimeGroups.get(mimeGroup);
+ Set<String> mimeTypes = newMimeGroups.get(mimeGroup);
if (mimeTypes != null) {
mimeGroups.put(mimeGroup, new ArraySet<>(mimeTypes));
@@ -524,7 +513,7 @@
mimeGroups = Collections.emptyMap();
}
- ArrayMap<String, ArraySet<String>> updatedMimeGroups =
+ ArrayMap<String, Set<String>> updatedMimeGroups =
new ArrayMap<>(newMimeGroupNames.size());
for (String mimeGroup : newMimeGroupNames) {
@@ -553,36 +542,36 @@
}
public boolean isPrivileged() {
- return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
+ return (getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
}
public boolean isOem() {
- return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0;
+ return (getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_OEM) != 0;
}
public boolean isVendor() {
- return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
+ return (getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
}
public boolean isProduct() {
- return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0;
+ return (getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0;
}
@Override
public boolean isRequiredForSystemUser() {
- return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER) != 0;
+ return (getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER) != 0;
}
public boolean isSystemExt() {
- return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) != 0;
+ return (getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) != 0;
}
public boolean isOdm() {
- return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_ODM) != 0;
+ return (getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_ODM) != 0;
}
public boolean isSystem() {
- return (pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0;
+ return (getFlags() & ApplicationInfo.FLAG_SYSTEM) != 0;
}
public SigningDetails getSigningDetails() {
@@ -1210,6 +1199,9 @@
return versionCode;
}
+ /**
+ * @see PackageState#getMimeGroups()
+ */
@Nullable
@Override
public Map<String, Set<String>> getMimeGroups() {
@@ -1305,6 +1297,30 @@
return this;
}
+ public PackageSetting setMimeGroups(@NonNull Map<String, Set<String>> mimeGroups) {
+ this.mimeGroups = mimeGroups;
+ onChanged();
+ return this;
+ }
+
+ public PackageSetting setOldCodePaths(Set<String> oldCodePaths) {
+ mOldCodePaths = oldCodePaths;
+ onChanged();
+ return this;
+ }
+
+ public PackageSetting setUsesStaticLibraries(String[] usesStaticLibraries) {
+ this.usesStaticLibraries = usesStaticLibraries;
+ onChanged();
+ return this;
+ }
+
+ public PackageSetting setUsesStaticLibrariesVersions(long[] usesStaticLibrariesVersions) {
+ this.usesStaticLibrariesVersions = usesStaticLibrariesVersions;
+ onChanged();
+ return this;
+ }
+
@NonNull
@Override
public PackageStateUnserialized getTransientState() {
@@ -1316,14 +1332,18 @@
return mUserStates;
}
- @Override
- public int getPkgFlags() {
- return pkgFlags;
- }
+ public PackageSetting addMimeTypes(String mimeGroup, Set<String> mimeTypes) {
+ if (mimeGroups == null) {
+ mimeGroups = new ArrayMap<>();
+ }
- @Override
- public int getPkgPrivateFlags() {
- return pkgPrivateFlags;
+ Set<String> existingMimeTypes = mimeGroups.get(mimeGroup);
+ if (existingMimeTypes == null) {
+ existingMimeTypes = new ArraySet<>();
+ mimeGroups.put(mimeGroup, existingMimeTypes);
+ }
+ existingMimeTypes.addAll(mimeTypes);
+ return this;
}
@@ -1341,14 +1361,6 @@
//@formatter:off
- /**
- * Non-persisted value. During an "upgrade without restart", we need the set
- * of all previous code paths so we can surgically add the new APKs to the
- * active classloader. If at any point an application is upgraded with a
- * restart, this field will be cleared since the classloader would be created
- * using the full set of code paths when the package's process is started.
- * TODO: Remove
- */
@DataClass.Generated.Member
public @Deprecated @Nullable Set<String> getOldCodePaths() {
return mOldCodePaths;
@@ -1504,10 +1516,10 @@
}
@DataClass.Generated(
- time = 1635392392447L,
+ time = 1636652483231L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/PackageSetting.java",
- 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 mLoadingProgress\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.PackageUserStateImpl> mUserStates\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 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.PackageUserStateImpl modifyUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(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 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,com.android.server.pm.pkg.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,com.android.server.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.PackageUserStateImpl 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 getVersionCode()\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.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\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 @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic @java.lang.Override int getPkgFlags()\npublic @java.lang.Override int getPkgPrivateFlags()\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
+ inputSignatures = "private int sharedUserId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @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 mLoadingProgress\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.PackageUserStateImpl> mUserStates\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 java.util.Set<java.lang.String> getMimeGroupInternal(java.lang.String)\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,java.util.Set<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.PackageUserStateImpl modifyUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(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 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,com.android.server.pm.pkg.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,com.android.server.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.PackageUserStateImpl 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 getVersionCode()\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.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\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 setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic com.android.server.pm.PackageSetting setOldCodePaths(java.util.Set<java.lang.String>)\npublic com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic com.android.server.pm.PackageSetting addMimeTypes(java.lang.String,java.util.Set<java.lang.String>)\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\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 731c6ca..6cc94ce 100644
--- a/services/core/java/com/android/server/pm/ScanPackageHelper.java
+++ b/services/core/java/com/android/server/pm/ScanPackageHelper.java
@@ -23,7 +23,6 @@
import static android.content.pm.PackageManager.INSTALL_FAILED_PROCESS_NOT_DEFINED;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
-import static android.os.incremental.IncrementalManager.isIncrementalPath;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.PackageManagerService.DEBUG_ABI_SELECTION;
@@ -92,7 +91,6 @@
import com.android.internal.util.ArrayUtils;
import com.android.server.SystemConfig;
import com.android.server.pm.parsing.PackageInfoUtils;
-import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.library.PackageBackwardCompatibility;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
@@ -106,7 +104,6 @@
import java.security.DigestException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -118,66 +115,17 @@
*/
final class ScanPackageHelper {
final PackageManagerService mPm;
- final InstallPackageHelper mInstallPackageHelper;
final PackageManagerServiceInjector mInjector;
// TODO(b/198166813): remove PMS dependency
public ScanPackageHelper(PackageManagerService pm) {
mPm = pm;
mInjector = pm.mInjector;
- mInstallPackageHelper = new InstallPackageHelper(mPm, mInjector);
}
ScanPackageHelper(PackageManagerService pm, PackageManagerServiceInjector injector) {
mPm = pm;
mInjector = injector;
- mInstallPackageHelper = new InstallPackageHelper(mPm, injector);
- }
-
- /**
- * Prepares the system to commit a {@link ScanResult} in a way that will not fail by registering
- * the app ID required for reconcile.
- * @return {@code true} if a new app ID was registered and will need to be cleaned up on
- * failure.
- */
- public boolean optimisticallyRegisterAppId(@NonNull ScanResult result)
- throws PackageManagerException {
- if (!result.mExistingSettingCopied || result.needsNewAppId()) {
- synchronized (mPm.mLock) {
- // THROWS: when we can't allocate a user id. add call to check if there's
- // enough space to ensure we won't throw; otherwise, don't modify state
- return mPm.mSettings.registerAppIdLPw(result.mPkgSetting, result.needsNewAppId());
- }
- }
- return false;
- }
-
- /**
- * Reverts any app ID creation that were made by
- * {@link #optimisticallyRegisterAppId(ScanResult)}. Note: this is only necessary if the
- * referenced method returned true.
- */
- public void cleanUpAppIdCreation(@NonNull ScanResult result) {
- // iff we've acquired an app ID for a new package setting, remove it so that it can be
- // acquired by another request.
- if (result.mPkgSetting.getAppId() > 0) {
- mPm.mSettings.removeAppIdLPw(result.mPkgSetting.getAppId());
- }
- }
-
- /**
- * Traces a package scan.
- * @see #scanPackageLI(File, int, int, long, UserHandle)
- */
- @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
- public AndroidPackage scanPackageTracedLI(File scanFile, final int parseFlags,
- int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage [" + scanFile.toString() + "]");
- try {
- return scanPackageLI(scanFile, parseFlags, scanFlags, currentTime, user);
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
}
/**
@@ -197,31 +145,6 @@
}
}
- /**
- * Scans a package and returns the newly parsed package.
- * Returns {@code null} in case of errors and the error code is stored in mLastScanError
- */
- @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
- private AndroidPackage scanPackageLI(File scanFile, int parseFlags, int scanFlags,
- long currentTime, UserHandle user) throws PackageManagerException {
- if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
-
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
- final ParsedPackage parsedPackage;
- try (PackageParser2 pp = mInjector.getScanningPackageParser()) {
- parsedPackage = pp.parsePackage(scanFile, parseFlags, false);
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
-
- // Static shared libraries have synthetic package names
- if (parsedPackage.isStaticSharedLibrary()) {
- PackageManagerService.renameStaticSharedLibraryPackage(parsedPackage);
- }
-
- return addForInitLI(parsedPackage, parseFlags, scanFlags, currentTime, user);
- }
-
// TODO(b/199937291): scanPackageNewLI() and scanPackageOnlyLI() should be merged.
// But, first, committing the results / removing app data needs to be moved up a level to the
// callers of this method. Also, we need to solve the problem of potentially creating a new
@@ -611,10 +534,10 @@
}
pkgSetting.setLastModifiedTime(scanFileTime);
// TODO(b/135203078): Remove, move to constructor
- pkgSetting.setPkg(parsedPackage);
- pkgSetting.pkgFlags = PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting);
- pkgSetting.pkgPrivateFlags =
- PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting);
+ pkgSetting.setPkg(parsedPackage)
+ .setFlags(PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting))
+ .setPrivateFlags(
+ PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting));
if (parsedPackage.getLongVersionCode() != pkgSetting.getVersionCode()) {
pkgSetting.setLongVersionCode(parsedPackage.getLongVersionCode());
}
@@ -680,27 +603,27 @@
if (systemPkgSetting != null) {
// updated system application, must at least have SCAN_AS_SYSTEM
scanFlags |= SCAN_AS_SYSTEM;
- if ((systemPkgSetting.pkgPrivateFlags
+ if ((systemPkgSetting.getPrivateFlags()
& ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
scanFlags |= SCAN_AS_PRIVILEGED;
}
- if ((systemPkgSetting.pkgPrivateFlags
+ if ((systemPkgSetting.getPrivateFlags()
& ApplicationInfo.PRIVATE_FLAG_OEM) != 0) {
scanFlags |= SCAN_AS_OEM;
}
- if ((systemPkgSetting.pkgPrivateFlags
+ if ((systemPkgSetting.getPrivateFlags()
& ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0) {
scanFlags |= SCAN_AS_VENDOR;
}
- if ((systemPkgSetting.pkgPrivateFlags
+ if ((systemPkgSetting.getPrivateFlags()
& ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0) {
scanFlags |= SCAN_AS_PRODUCT;
}
- if ((systemPkgSetting.pkgPrivateFlags
+ if ((systemPkgSetting.getPrivateFlags()
& ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) != 0) {
scanFlags |= SCAN_AS_SYSTEM_EXT;
}
- if ((systemPkgSetting.pkgPrivateFlags
+ if ((systemPkgSetting.getPrivateFlags()
& ApplicationInfo.PRIVATE_FLAG_ODM) != 0) {
scanFlags |= SCAN_AS_ODM;
}
@@ -748,22 +671,7 @@
return scanFlags;
}
-
- /**
- * Adds a new package to the internal data structures during platform initialization.
- * <p>After adding, the package is known to the system and available for querying.
- * <p>For packages located on the device ROM [eg. packages located in /system, /vendor,
- * etc...], additional checks are performed. Basic verification [such as ensuring
- * matching signatures, checking version codes, etc...] occurs if the package is
- * identical to a previously known package. If the package fails a signature check,
- * the version installed on /data will be removed. If the version of the new package
- * is less than or equal than the version on /data, it will be ignored.
- * <p>Regardless of the package location, the results are applied to the internal
- * structures and the package is made available to the rest of the system.
- * <p>NOTE: The return value should be removed. It's the passed in package object.
- */
- @GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
- public AndroidPackage addForInitLI(ParsedPackage parsedPackage,
+ public Pair<ScanResult, Boolean> scanSystemPackageLI(ParsedPackage parsedPackage,
@ParsingPackageUtils.ParseFlags int parseFlags,
@PackageManagerService.ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user) throws PackageManagerException {
@@ -828,7 +736,7 @@
// we're updating the disabled package, so, scan it as the package setting
boolean isPlatformPackage = platformPackage != null
&& platformPackage.getPackageName().equals(
- parsedPackage.getPackageName());
+ parsedPackage.getPackageName());
final ScanRequest request = new ScanRequest(parsedPackage, sharedUserSetting,
null, disabledPkgSetting /* pkgSetting */,
null /* disabledPkgSetting */, null /* originalPkgSetting */,
@@ -872,7 +780,7 @@
final InstallArgs args = new FileInstallArgs(
pkgSetting.getPathString(), getAppDexInstructionSets(
- pkgSetting.getPrimaryCpuAbi(), pkgSetting.getSecondaryCpuAbi()), mPm);
+ pkgSetting.getPrimaryCpuAbi(), pkgSetting.getSecondaryCpuAbi()), mPm);
args.cleanUpResourcesLI();
synchronized (mPm.mLock) {
mPm.mSettings.enableSystemPackageLPw(pkgSetting.getPackageName());
@@ -957,7 +865,7 @@
+ parsedPackage.getPath());
InstallArgs args = new FileInstallArgs(
pkgSetting.getPathString(), getAppDexInstructionSets(
- pkgSetting.getPrimaryCpuAbi(), pkgSetting.getSecondaryCpuAbi()),
+ pkgSetting.getPrimaryCpuAbi(), pkgSetting.getSecondaryCpuAbi()),
mPm);
synchronized (mPm.mInstallLock) {
args.cleanUpResourcesLI();
@@ -977,52 +885,9 @@
}
}
- final ScanResult scanResult = scanPackageNewLI(parsedPackage, parseFlags, scanFlags
- | SCAN_UPDATE_SIGNATURE, currentTime, user, null);
- if (scanResult.mSuccess) {
- synchronized (mPm.mLock) {
- boolean appIdCreated = false;
- try {
- final String pkgName = scanResult.mPkgSetting.getPackageName();
- final Map<String, ReconciledPackage> reconcileResult =
- mInstallPackageHelper.reconcilePackagesLocked(
- new ReconcileRequest(
- Collections.singletonMap(pkgName, scanResult),
- mPm.mSharedLibraries,
- mPm.mPackages,
- Collections.singletonMap(
- pkgName,
- mPm.getSettingsVersionForPackage(
- parsedPackage)),
- Collections.singletonMap(pkgName,
- mPm.getSharedLibLatestVersionSetting(
- scanResult))),
- mPm.mSettings.getKeySetManagerService(), mInjector);
- appIdCreated = optimisticallyRegisterAppId(scanResult);
- mInstallPackageHelper.commitReconciledScanResultLocked(
- reconcileResult.get(pkgName), mPm.mUserManager.getUserIds());
- } catch (PackageManagerException e) {
- if (appIdCreated) {
- cleanUpAppIdCreation(scanResult);
- }
- throw e;
- }
- }
- }
-
- if (shouldHideSystemApp) {
- synchronized (mPm.mLock) {
- mPm.mSettings.disableSystemPackageLPw(parsedPackage.getPackageName(), true);
- }
- }
- if (mPm.mIncrementalManager != null && isIncrementalPath(parsedPackage.getPath())) {
- if (pkgSetting != null && pkgSetting.isPackageLoading()) {
- // Continue monitoring loading progress of active incremental packages
- mPm.mIncrementalManager.registerLoadingProgressCallback(parsedPackage.getPath(),
- new IncrementalProgressListener(parsedPackage.getPackageName(), mPm));
- }
- }
- return scanResult.mPkgSetting.getPkg();
+ final ScanResult scanResult = scanPackageNewLI(parsedPackage, parseFlags,
+ scanFlags | SCAN_UPDATE_SIGNATURE, currentTime, user, null);
+ return new Pair<>(scanResult, shouldHideSystemApp);
}
/**
@@ -1085,7 +950,7 @@
&& ps.getLastModifiedTime() == lastModifiedTime
&& !InstallPackageHelper.isCompatSignatureUpdateNeeded(settingsVersionForPackage)
&& !InstallPackageHelper.isRecoverSignatureUpdateNeeded(
- settingsVersionForPackage)) {
+ settingsVersionForPackage)) {
if (ps.getSigningDetails().getSignatures() != null
&& ps.getSigningDetails().getSignatures().length != 0
&& ps.getSigningDetails().getSignatureSchemeVersion()
@@ -1191,7 +1056,7 @@
@GuardedBy("mPm.mLock")
private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, AndroidPackage newPkg) {
- if ((oldPkg.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ if ((oldPkg.getFlags() & ApplicationInfo.FLAG_SYSTEM) == 0) {
Slog.w(TAG, "Unable to update from " + oldPkg.getPackageName()
+ " to " + newPkg.getPackageName()
+ ": old package not in system partition");
diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java
index 22e6ece..ed85ff9 100644
--- a/services/core/java/com/android/server/pm/SettingBase.java
+++ b/services/core/java/com/android/server/pm/SettingBase.java
@@ -29,15 +29,11 @@
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public abstract class SettingBase implements Watchable, Snappable {
- // TODO: make this variable protected, or even private with a getter and setter.
- // Simply making it protected or private requires that the name be changed to conformm
- // to the Android naming convention, and that touches quite a few files.
- int pkgFlags;
- // TODO: make this variable protected, or even private with a getter and setter.
- // Simply making it protected or private requires that the name be changed to conformm
- // to the Android naming convention, and that touches quite a few files.
- int pkgPrivateFlags;
+ // TODO: Remove in favor of individual boolean APIs. It's not clear what flag values are saved
+ // and bugs exist where callers query for an unsaved flag.
+ private int mPkgFlags;
+ private int mPkgPrivateFlags;
/**
* Watchable machinery
@@ -115,8 +111,8 @@
}
public final void copySettingBase(SettingBase orig) {
- pkgFlags = orig.pkgFlags;
- pkgPrivateFlags = orig.pkgPrivateFlags;
+ mPkgFlags = orig.mPkgFlags;
+ mPkgPrivateFlags = orig.mPkgPrivateFlags;
mLegacyPermissionsState.copyFrom(orig.mLegacyPermissionsState);
onChanged();
}
@@ -126,15 +122,17 @@
return mLegacyPermissionsState;
}
- void setFlags(int pkgFlags) {
- this.pkgFlags = pkgFlags
+ SettingBase setFlags(int pkgFlags) {
+ this.mPkgFlags = pkgFlags
& (ApplicationInfo.FLAG_SYSTEM
- | ApplicationInfo.FLAG_EXTERNAL_STORAGE);
+ | ApplicationInfo.FLAG_EXTERNAL_STORAGE
+ | ApplicationInfo.FLAG_TEST_ONLY);
onChanged();
+ return this;
}
- void setPrivateFlags(int pkgPrivateFlags) {
- this.pkgPrivateFlags = pkgPrivateFlags
+ SettingBase setPrivateFlags(int pkgPrivateFlags) {
+ this.mPkgPrivateFlags = pkgPrivateFlags
& (ApplicationInfo.PRIVATE_FLAG_PRIVILEGED
| ApplicationInfo.PRIVATE_FLAG_OEM
| ApplicationInfo.PRIVATE_FLAG_VENDOR
@@ -143,5 +141,14 @@
| ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER
| ApplicationInfo.PRIVATE_FLAG_ODM);
onChanged();
+ return this;
+ }
+
+ public int getFlags() {
+ return mPkgFlags;
+ }
+
+ public int getPrivateFlags() {
+ return mPkgPrivateFlags;
}
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 3d6467e..7eec791 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -154,6 +154,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
@@ -825,8 +826,8 @@
PackageSetting ret = addPackageLPw(name, p.getRealName(), p.getPath(),
p.getLegacyNativeLibraryPath(), p.getPrimaryCpuAbi(),
p.getSecondaryCpuAbi(), p.getCpuAbiOverride(),
- p.getAppId(), p.getVersionCode(), p.pkgFlags, p.pkgPrivateFlags,
- p.usesStaticLibraries, p.usesStaticLibrariesVersions, p.mimeGroups,
+ p.getAppId(), p.getVersionCode(), p.getFlags(), p.getPrivateFlags(),
+ p.getUsesStaticLibraries(), p.getUsesStaticLibrariesVersions(), p.getMimeGroups(),
mDomainVerificationManager.generateNewId());
if (ret != null) {
ret.getPkgState().setUpdatedSystemApp(false);
@@ -851,7 +852,7 @@
String legacyNativeLibraryPathString, String primaryCpuAbiString,
String secondaryCpuAbiString, String cpuAbiOverrideString, int uid, long vc, int
pkgFlags, int pkgPrivateFlags, String[] usesStaticLibraries,
- long[] usesStaticLibraryNames, Map<String, ArraySet<String>> mimeGroups,
+ long[] usesStaticLibraryNames, Map<String, Set<String>> mimeGroups,
@NonNull UUID domainSetId) {
PackageSetting p = mPackages.get(name);
if (p != null) {
@@ -931,22 +932,22 @@
if (originalPkg != null) {
if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package "
+ pkgName + " is adopting original package " + originalPkg.getPackageName());
- pkgSetting = new PackageSetting(originalPkg, pkgName /*realPkgName*/);
- pkgSetting.setPath(codePath);
- pkgSetting.setLegacyNativeLibraryPath(legacyNativeLibraryPath);
- pkgSetting.pkgFlags = pkgFlags;
- pkgSetting.pkgPrivateFlags = pkgPrivateFlags;
- pkgSetting.setPrimaryCpuAbi(primaryCpuAbi);
- pkgSetting.setSecondaryCpuAbi(secondaryCpuAbi);
- // NOTE: Create a deeper copy of the package signatures so we don't
- // overwrite the signatures in the original package setting.
- pkgSetting.setSignatures(new PackageSignatures());
- pkgSetting.setLongVersionCode(versionCode);
- pkgSetting.usesStaticLibraries = usesStaticLibraries;
- pkgSetting.usesStaticLibrariesVersions = usesStaticLibrariesVersions;
- // Update new package state.
- pkgSetting.setLastModifiedTime(codePath.lastModified());
- pkgSetting.setDomainSetId(domainSetId);
+ pkgSetting = new PackageSetting(originalPkg, pkgName /*realPkgName*/)
+ .setPath(codePath)
+ .setLegacyNativeLibraryPath(legacyNativeLibraryPath)
+ .setPrimaryCpuAbi(primaryCpuAbi)
+ .setSecondaryCpuAbi(secondaryCpuAbi)
+ // NOTE: Create a deeper copy of the package signatures so we don't
+ // overwrite the signatures in the original package setting.
+ .setSignatures(new PackageSignatures())
+ .setLongVersionCode(versionCode)
+ .setUsesStaticLibraries(usesStaticLibraries)
+ .setUsesStaticLibrariesVersions(usesStaticLibrariesVersions)
+ // Update new package state.
+ .setLastModifiedTime(codePath.lastModified())
+ .setDomainSetId(domainSetId);
+ pkgSetting.setFlags(pkgFlags)
+ .setPrivateFlags(pkgPrivateFlags);
} else {
pkgSetting = new PackageSetting(pkgName, realPkgName, codePath,
legacyNativeLibraryPath, primaryCpuAbi, secondaryCpuAbi,
@@ -1028,7 +1029,7 @@
return pkgSetting;
}
- private static Map<String, ArraySet<String>> createMimeGroups(Set<String> mimeGroupNames) {
+ private static Map<String, Set<String>> createMimeGroups(Set<String> mimeGroupNames) {
if (mimeGroupNames == null) {
return null;
}
@@ -1090,42 +1091,40 @@
}
pkgSetting.setPath(codePath);
}
- // If what we are scanning is a system (and possibly privileged) package,
- // then make it so, regardless of whether it was previously installed only
- // in the data partition. Reset first.
- pkgSetting.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM;
- pkgSetting.pkgPrivateFlags &= ~(ApplicationInfo.PRIVATE_FLAG_PRIVILEGED
- | ApplicationInfo.PRIVATE_FLAG_OEM
- | ApplicationInfo.PRIVATE_FLAG_VENDOR
- | ApplicationInfo.PRIVATE_FLAG_PRODUCT
- | ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT
- | ApplicationInfo.PRIVATE_FLAG_ODM);
- pkgSetting.pkgFlags |= pkgFlags & ApplicationInfo.FLAG_SYSTEM;
- pkgSetting.pkgPrivateFlags |=
- pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
- pkgSetting.pkgPrivateFlags |=
- pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_OEM;
- pkgSetting.pkgPrivateFlags |=
- pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR;
- pkgSetting.pkgPrivateFlags |=
- pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT;
- pkgSetting.pkgPrivateFlags |=
- pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT;
- pkgSetting.pkgPrivateFlags |=
- pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_ODM;
- pkgSetting.setPrimaryCpuAbi(primaryCpuAbi);
- pkgSetting.setSecondaryCpuAbi(secondaryCpuAbi);
+
+ pkgSetting.setPrimaryCpuAbi(primaryCpuAbi)
+ .setSecondaryCpuAbi(secondaryCpuAbi)
+ .updateMimeGroups(mimeGroupNames)
+ .setDomainSetId(domainSetId);
// Update static shared library dependencies if needed
if (usesStaticLibraries != null && usesStaticLibrariesVersions != null
&& usesStaticLibraries.length == usesStaticLibrariesVersions.length) {
- pkgSetting.usesStaticLibraries = usesStaticLibraries;
- pkgSetting.usesStaticLibrariesVersions = usesStaticLibrariesVersions;
+ pkgSetting.setUsesStaticLibraries(usesStaticLibraries)
+ .setUsesStaticLibrariesVersions(usesStaticLibrariesVersions);
} else {
- pkgSetting.usesStaticLibraries = null;
- pkgSetting.usesStaticLibrariesVersions = null;
+ pkgSetting.setUsesStaticLibraries(null)
+ .setUsesStaticLibrariesVersions(null);
}
- pkgSetting.updateMimeGroups(mimeGroupNames);
- pkgSetting.setDomainSetId(domainSetId);
+
+ // These two flags are preserved from the existing PackageSetting. Copied from prior code,
+ // unclear if this is actually necessary.
+ boolean wasExternalStorage = (pkgSetting.getFlags()
+ & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
+ if (wasExternalStorage) {
+ pkgFlags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE;
+ } else {
+ pkgFlags &= ~ApplicationInfo.FLAG_EXTERNAL_STORAGE;
+ }
+ boolean wasRequiredForSystemUser = (pkgSetting.getPrivateFlags()
+ & ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER) != 0;
+ if (wasRequiredForSystemUser) {
+ pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER;
+ } else {
+ pkgPrivateFlags &= ~ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER;
+ }
+
+ pkgSetting.setFlags(pkgFlags)
+ .setPrivateFlags(pkgPrivateFlags);
}
/**
@@ -2175,10 +2174,10 @@
long libVersion = parser.getAttributeLong(null, ATTR_VERSION, -1);
if (libName != null && libVersion >= 0) {
- outPs.usesStaticLibraries = ArrayUtils.appendElement(String.class,
- outPs.usesStaticLibraries, libName);
- outPs.usesStaticLibrariesVersions = ArrayUtils.appendLong(
- outPs.usesStaticLibrariesVersions, libVersion);
+ outPs.setUsesStaticLibraries(ArrayUtils.appendElement(String.class,
+ outPs.getUsesStaticLibraries(), libName));
+ outPs.setUsesStaticLibrariesVersions(ArrayUtils.appendLong(
+ outPs.getUsesStaticLibrariesVersions(), libVersion));
}
XmlUtils.skipCurrentTag(parser);
@@ -2709,7 +2708,8 @@
}
serializer.attributeFloat(null, "loadingProgress", pkg.getLoadingProgress());
- writeUsesStaticLibLPw(serializer, pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions);
+ writeUsesStaticLibLPw(serializer, pkg.getUsesStaticLibraries(),
+ pkg.getUsesStaticLibrariesVersions());
serializer.endTag(null, "updated-package");
}
@@ -2736,8 +2736,8 @@
serializer.attribute(null, "cpuAbiOverride", pkg.getCpuAbiOverride());
}
- serializer.attributeInt(null, "publicFlags", pkg.pkgFlags);
- serializer.attributeInt(null, "privateFlags", pkg.pkgPrivateFlags);
+ serializer.attributeInt(null, "publicFlags", pkg.getFlags());
+ serializer.attributeInt(null, "privateFlags", pkg.getPrivateFlags());
serializer.attributeLongHex(null, "ft", pkg.getLastModifiedTime());
serializer.attributeLongHex(null, "it", pkg.getFirstInstallTime());
serializer.attributeLongHex(null, "ut", pkg.getLastUpdateTime());
@@ -2786,7 +2786,8 @@
serializer.attribute(null, "domainSetId", pkg.getDomainSetId().toString());
- writeUsesStaticLibLPw(serializer, pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions);
+ writeUsesStaticLibLPw(serializer, pkg.getUsesStaticLibraries(),
+ pkg.getUsesStaticLibrariesVersions());
pkg.getSignatures().writeXml(serializer, "sigs", mPastSignatures.untrackedStorage());
@@ -2798,7 +2799,7 @@
writeSigningKeySetLPr(serializer, pkg.getKeySetData());
writeUpgradeKeySetsLPr(serializer, pkg.getKeySetData());
writeKeySetAliasesLPr(serializer, pkg.getKeySetData());
- writeMimeGroupLPr(serializer, pkg.mimeGroups);
+ writeMimeGroupLPr(serializer, pkg.getMimeGroups());
serializer.endTag(null, "package");
}
@@ -3071,7 +3072,7 @@
final PackageManagerInternal pmInternal =
LocalServices.getService(PackageManagerInternal.class);
for (PackageSetting ps : mPackages.values()) {
- if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0 && ps.getPkg() != null
+ if ((ps.getFlags() & ApplicationInfo.FLAG_SYSTEM) != 0 && ps.getPkg() != null
&& !ps.getPkg().getPreferredActivityFilters().isEmpty()) {
List<Pair<String, ParsedIntentInfo>> intents
= ps.getPkg().getPreferredActivityFilters();
@@ -3786,7 +3787,11 @@
Log.d(TAG, "Read domain verification for package: " + ivi.getPackageName());
}
} else if (tagName.equals(TAG_MIME_GROUP)) {
- packageSetting.mimeGroups = readMimeGroupLPw(parser, packageSetting.mimeGroups);
+ final Pair<String, Set<String>> groupToMimeTypes = readMimeGroupLPw(parser);
+ if (groupToMimeTypes != null) {
+ packageSetting.addMimeTypes(groupToMimeTypes.first,
+ groupToMimeTypes.second);
+ }
} else if (tagName.equals(TAG_USES_STATIC_LIB)) {
readUsesStaticLibLPw(parser, packageSetting);
} else {
@@ -3812,23 +3817,16 @@
}
}
- private Map<String, ArraySet<String>> readMimeGroupLPw(TypedXmlPullParser parser,
- Map<String, ArraySet<String>> mimeGroups) throws XmlPullParserException, IOException {
+ @Nullable
+ private Pair<String, Set<String>> readMimeGroupLPw(TypedXmlPullParser parser)
+ throws XmlPullParserException, IOException {
String groupName = parser.getAttributeValue(null, ATTR_NAME);
if (groupName == null) {
XmlUtils.skipCurrentTag(parser);
- return mimeGroups;
+ return null;
}
- if (mimeGroups == null) {
- mimeGroups = new ArrayMap<>();
- }
-
- ArraySet<String> mimeTypes = mimeGroups.get(groupName);
- if (mimeTypes == null) {
- mimeTypes = new ArraySet<>();
- mimeGroups.put(groupName, mimeTypes);
- }
+ Set<String> mimeTypes = new ArraySet<>();
int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -3850,11 +3848,11 @@
}
}
- return mimeGroups;
+ return Pair.create(groupName, mimeTypes);
}
private void writeMimeGroupLPr(TypedXmlSerializer serializer,
- Map<String, ArraySet<String>> mimeGroups) throws IOException {
+ Map<String, Set<String>> mimeGroups) throws IOException {
if (mimeGroups == null) {
return;
}
@@ -4477,7 +4475,7 @@
pw.print(prefix); pw.print(" legacyNativeLibraryDir=");
pw.println(ps.getLegacyNativeLibraryPath());
pw.print(prefix); pw.print(" extractNativeLibs=");
- pw.println((ps.pkgFlags & ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS) != 0
+ pw.println((ps.getFlags() & ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS) != 0
? "true" : "false");
pw.print(prefix); pw.print(" primaryCpuAbi="); pw.println(ps.getPrimaryCpuAbi());
pw.print(prefix); pw.print(" secondaryCpuAbi="); pw.println(ps.getSecondaryCpuAbi());
@@ -4687,7 +4685,7 @@
pw.print(prefix); pw.print(" installPermissionsFixed=");
pw.print(ps.isInstallPermissionsFixed());
pw.println();
- pw.print(prefix); pw.print(" pkgFlags="); printFlags(pw, ps.pkgFlags, FLAG_DUMP_SPEC);
+ pw.print(prefix); pw.print(" pkgFlags="); printFlags(pw, ps.getFlags(), FLAG_DUMP_SPEC);
pw.println();
if (pkg != null && pkg.getOverlayTarget() != null) {
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index 3387737..7487e81 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -147,17 +147,17 @@
return false;
}
// recalculate the pkgFlags for this shared user if needed
- if ((this.pkgFlags & packageSetting.pkgFlags) != 0) {
+ if ((this.getFlags() & packageSetting.getFlags()) != 0) {
int aggregatedFlags = uidFlags;
for (PackageSetting ps : packages) {
- aggregatedFlags |= ps.pkgFlags;
+ aggregatedFlags |= ps.getFlags();
}
setFlags(aggregatedFlags);
}
- if ((this.pkgPrivateFlags & packageSetting.pkgPrivateFlags) != 0) {
+ if ((this.getPrivateFlags() & packageSetting.getPrivateFlags()) != 0) {
int aggregatedPrivateFlags = uidPrivateFlags;
for (PackageSetting ps : packages) {
- aggregatedPrivateFlags |= ps.pkgPrivateFlags;
+ aggregatedPrivateFlags |= ps.getPrivateFlags();
}
setPrivateFlags(aggregatedPrivateFlags);
}
@@ -174,8 +174,8 @@
seInfoTargetSdkVersion = packageSetting.getPkg().getTargetSdkVersion();
}
if (packages.add(packageSetting)) {
- setFlags(this.pkgFlags | packageSetting.pkgFlags);
- setPrivateFlags(this.pkgPrivateFlags | packageSetting.pkgPrivateFlags);
+ setFlags(this.getFlags() | packageSetting.getFlags());
+ setPrivateFlags(this.getPrivateFlags() | packageSetting.getPrivateFlags());
onChanged();
}
if (packageSetting.getPkg() != null) {
@@ -201,7 +201,7 @@
}
public boolean isPrivileged() {
- return (this.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
+ return (this.getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
}
/**
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index b4bd086..42c88b3 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -22,8 +22,6 @@
import android.app.appsearch.AppSearchManager;
import android.app.appsearch.AppSearchResult;
import android.app.appsearch.AppSearchSession;
-import android.app.appsearch.GenericDocument;
-import android.app.appsearch.GetByDocumentIdRequest;
import android.app.appsearch.PackageIdentifier;
import android.app.appsearch.PutDocumentsRequest;
import android.app.appsearch.RemoveByDocumentIdRequest;
@@ -56,11 +54,12 @@
import android.util.TypedXmlSerializer;
import android.util.Xml;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.infra.AndroidFuture;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
-import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.server.pm.ShortcutService.DumpFilter;
@@ -88,7 +87,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
-import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -160,6 +159,8 @@
private final Object mLock = new Object();
+ private final Executor mExecutor;
+
/**
* An temp in-memory copy of shortcuts for this package that was loaded from xml, keyed on IDs.
*/
@@ -189,11 +190,8 @@
private long mLastKnownForegroundElapsedTime;
- private boolean mIsInitilized;
-
- private boolean mRescanRequired;
- private boolean mIsNewApp;
- private List<ShortcutInfo> mManifestShortcuts;
+ @GuardedBy("mLock")
+ private boolean mIsAppSearchSchemaUpToDate;
private ShortcutPackage(ShortcutUser shortcutUser,
int packageUserId, String packageName, ShortcutPackageInfo spi) {
@@ -201,6 +199,7 @@
spi != null ? spi : ShortcutPackageInfo.newEmpty());
mPackageUid = shortcutUser.mService.injectGetPackageUid(packageName, packageUserId);
+ mExecutor = BackgroundThread.getExecutor();
}
public ShortcutPackage(ShortcutUser shortcutUser, int packageUserId, String packageName) {
@@ -245,11 +244,11 @@
final String query = String.format("%s:-%s AND %s:%s",
AppSearchShortcutInfo.KEY_FLAGS, ShortcutInfo.FLAG_SHADOW,
AppSearchShortcutInfo.KEY_DISABLED_REASON, restoreBlockReason);
- forEachShortcutMutateIf(query, si -> {
+ forEachShortcutMutate(si -> {
if (restoreBlockReason == ShortcutInfo.DISABLED_REASON_NOT_DISABLED
&& !si.hasFlags(ShortcutInfo.FLAG_SHADOW)
&& si.getDisabledReason() == restoreBlockReason) {
- return false;
+ return;
}
si.clearFlags(ShortcutInfo.FLAG_SHADOW);
@@ -257,7 +256,6 @@
if (restoreBlockReason != ShortcutInfo.DISABLED_REASON_NOT_DISABLED) {
si.addFlags(ShortcutInfo.FLAG_DISABLED);
}
- return true;
});
// Because some launchers may not have been restored (e.g. allowBackup=false),
// we need to re-calculate the pinned shortcuts.
@@ -270,8 +268,7 @@
@Nullable
public ShortcutInfo findShortcutById(@Nullable final String id) {
if (id == null) return null;
- final List<ShortcutInfo> ret = getShortcutById(Collections.singleton(id));
- return (ret == null || ret.isEmpty()) ? null : ret.get(0);
+ return mShortcuts.get(id);
}
public boolean isShortcutExistsAndInvisibleToPublisher(String id) {
@@ -337,9 +334,8 @@
* Delete a shortcut by ID. This will *always* remove it even if it's immutable or invisible.
*/
private ShortcutInfo forceDeleteShortcutInner(@NonNull String id) {
- final ShortcutInfo shortcut = findShortcutById(id);
+ final ShortcutInfo shortcut = mShortcuts.remove(id);
if (shortcut != null) {
- removeShortcut(id);
mShortcutUser.mService.removeIconLocked(shortcut);
shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_PINNED
| ShortcutInfo.FLAG_MANIFEST | ShortcutInfo.FLAG_CACHED_ALL);
@@ -435,7 +431,8 @@
}
changedShortcuts.add(shortcut);
- deleted = deleteDynamicWithId(shortcut.getId(), /*ignoreInvisible=*/ true) != null;
+ deleted = deleteDynamicWithId(shortcut.getId(), /* ignoreInvisible =*/ true,
+ /*ignorePersistedShortcuts=*/ true) != null;
}
} else {
// It's an update case.
@@ -449,16 +446,14 @@
forceReplaceShortcutInner(newShortcut);
if (isAppSearchEnabled()) {
- mShortcutUser.mService.injectPostToHandler(() -> awaitInAppSearch("reportUsage",
- session -> {
- final AndroidFuture<Boolean> future = new AndroidFuture<>();
- session.reportUsage(
- new ReportUsageRequest.Builder(
- getPackageName(), newShortcut.getId()).build(),
- mShortcutUser.mExecutor,
- result -> future.complete(result.isSuccess()));
- return future;
- }));
+ runAsSystem(() -> fromAppSearch().thenAccept(session ->
+ session.reportUsage(new ReportUsageRequest.Builder(
+ getPackageName(), newShortcut.getId()).build(), mExecutor, result -> {
+ if (!result.isSuccess()) {
+ Slog.e(TAG, "Failed to report usage via AppSearch. "
+ + result.getErrorMessage());
+ }
+ })));
}
return deleted;
}
@@ -470,12 +465,7 @@
*/
private List<ShortcutInfo> removeOrphans() {
final List<ShortcutInfo> removeList = new ArrayList<>(1);
- final String query = String.format("%s OR %s OR %s OR %s",
- AppSearchShortcutInfo.QUERY_IS_PINNED,
- AppSearchShortcutInfo.QUERY_IS_DYNAMIC,
- AppSearchShortcutInfo.QUERY_IS_MANIFEST,
- AppSearchShortcutInfo.QUERY_IS_CACHED);
- forEachShortcut(query, si -> {
+ forEachShortcut(si -> {
if (si.isAlive()) return;
removeList.add(si);
});
@@ -484,7 +474,6 @@
forceDeleteShortcutInner(removeList.get(i).getId());
}
}
-
return removeList;
}
@@ -493,29 +482,21 @@
*
* @return List of shortcuts that actually got removed.
*/
- public List<ShortcutInfo> deleteAllDynamicShortcuts(boolean ignoreInvisible) {
+ public List<ShortcutInfo> deleteAllDynamicShortcuts() {
final long now = mShortcutUser.mService.injectCurrentTimeMillis();
- final String query;
- if (!ignoreInvisible) {
- query = AppSearchShortcutInfo.QUERY_IS_DYNAMIC;
- } else {
- query = String.format("%s %s",
- AppSearchShortcutInfo.QUERY_IS_DYNAMIC,
- AppSearchShortcutInfo.QUERY_IS_VISIBLE_TO_PUBLISHER);
- }
- final boolean[] changed = new boolean[1];
- forEachShortcutMutateIf(query, si -> {
- if (si.isDynamic() && (!ignoreInvisible || si.isVisibleToPublisher())) {
- changed[0] = true;
+ boolean changed = false;
+ for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+ ShortcutInfo si = mShortcuts.valueAt(i);
+ if (si.isDynamic() && si.isVisibleToPublisher()) {
+ changed = true;
si.setTimestamp(now);
si.clearFlags(ShortcutInfo.FLAG_DYNAMIC);
si.setRank(0); // It may still be pinned, so clear the rank.
- return true;
}
- return false;
- });
- if (changed[0]) {
+ }
+ removeAllShortcutsAsync();
+ if (changed) {
return removeOrphans();
}
return null;
@@ -528,10 +509,11 @@
* @return The deleted shortcut, or null if it was not actually removed because it is either
* pinned or cached.
*/
- public ShortcutInfo deleteDynamicWithId(@NonNull String shortcutId, boolean ignoreInvisible) {
+ public ShortcutInfo deleteDynamicWithId(@NonNull String shortcutId, boolean ignoreInvisible,
+ boolean ignorePersistedShortcuts) {
return deleteOrDisableWithId(
shortcutId, /* disable =*/ false, /* overrideImmutable=*/ false, ignoreInvisible,
- ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
+ ShortcutInfo.DISABLED_REASON_NOT_DISABLED, ignorePersistedShortcuts);
}
/**
@@ -542,9 +524,9 @@
* it's still pinned.
*/
private ShortcutInfo disableDynamicWithId(@NonNull String shortcutId, boolean ignoreInvisible,
- int disabledReason) {
+ int disabledReason, boolean ignorePersistedShortcuts) {
return deleteOrDisableWithId(shortcutId, /* disable =*/ true, /* overrideImmutable=*/ false,
- ignoreInvisible, disabledReason);
+ ignoreInvisible, disabledReason, ignorePersistedShortcuts);
}
/**
@@ -560,7 +542,7 @@
}
return deleteOrDisableWithId(
shortcutId, /* disable =*/ false, /* overrideImmutable=*/ false, ignoreInvisible,
- ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
+ ShortcutInfo.DISABLED_REASON_NOT_DISABLED, /*ignorePersistedShortcuts=*/ false);
}
/**
@@ -574,7 +556,8 @@
int disabledMessageResId, boolean overrideImmutable, boolean ignoreInvisible,
int disabledReason) {
final ShortcutInfo deleted = deleteOrDisableWithId(shortcutId, /* disable =*/ true,
- overrideImmutable, ignoreInvisible, disabledReason);
+ overrideImmutable, ignoreInvisible, disabledReason,
+ /*ignorePersistedShortcuts=*/ false);
// If disabled id still exists, it is pinned and we need to update the disabled message.
mutateShortcut(shortcutId, null, disabled -> {
@@ -593,7 +576,8 @@
@Nullable
private ShortcutInfo deleteOrDisableWithId(@NonNull String shortcutId, boolean disable,
- boolean overrideImmutable, boolean ignoreInvisible, int disabledReason) {
+ boolean overrideImmutable, boolean ignoreInvisible, int disabledReason,
+ boolean ignorePersistedShortcuts) {
Preconditions.checkState(
(disable == (disabledReason != ShortcutInfo.DISABLED_REASON_NOT_DISABLED)),
"disable and disabledReason disagree: " + disable + " vs " + disabledReason);
@@ -606,8 +590,10 @@
if (!overrideImmutable) {
ensureNotImmutable(oldShortcut, /*ignoreInvisible=*/ true);
}
+ if (!ignorePersistedShortcuts) {
+ removeShortcutAsync(shortcutId);
+ }
if (oldShortcut.isPinned() || oldShortcut.isCached()) {
-
mutateShortcut(oldShortcut.getId(), oldShortcut, si -> {
si.setRank(0);
si.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_MANIFEST);
@@ -672,21 +658,19 @@
pinnedShortcuts.addAll(pinned);
});
// Then, update the pinned state if necessary.
- final List<ShortcutInfo> pinned = getShortcutById(pinnedShortcuts);
+ final List<ShortcutInfo> pinned = findAll(pinnedShortcuts);
if (pinned != null) {
pinned.forEach(si -> {
if (!si.isPinned()) {
si.addFlags(ShortcutInfo.FLAG_PINNED);
}
});
- saveShortcut(pinned);
}
- forEachShortcutMutateIf(AppSearchShortcutInfo.QUERY_IS_PINNED, si -> {
+ forEachShortcutMutate(si -> {
if (!pinnedShortcuts.contains(si.getId()) && si.isPinned()) {
si.clearFlags(ShortcutInfo.FLAG_PINNED);
- return true;
+ return;
}
- return false;
});
// Lastly, remove the ones that are no longer pinned, cached nor dynamic.
@@ -777,9 +761,9 @@
/**
* Find all shortcuts that match {@code query}.
*/
- public void findAll(@NonNull List<ShortcutInfo> result, @Nullable String query,
+ public void findAll(@NonNull List<ShortcutInfo> result,
@Nullable Predicate<ShortcutInfo> filter, int cloneFlag) {
- findAll(result, query, filter, cloneFlag, null, 0, /*getPinnedByAnyLauncher=*/ false);
+ findAll(result, filter, cloneFlag, null, 0, /*getPinnedByAnyLauncher=*/ false);
}
/**
@@ -790,7 +774,7 @@
* adjusted for the caller too.
*/
public void findAll(@NonNull List<ShortcutInfo> result,
- @Nullable String query, @Nullable Predicate<ShortcutInfo> filter, int cloneFlag,
+ @Nullable Predicate<ShortcutInfo> filter, int cloneFlag,
@Nullable String callingLauncher, int launcherUserId, boolean getPinnedByAnyLauncher) {
if (getPackageInfo().isShadow()) {
// Restored and the app not installed yet, so don't return any.
@@ -802,9 +786,8 @@
final ArraySet<String> pinnedByCallerSet = (callingLauncher == null) ? null
: s.getLauncherShortcutsLocked(callingLauncher, getPackageUserId(), launcherUserId)
.getPinnedShortcutIds(getPackageName(), getPackageUserId());
- forEachShortcut(query == null ? "" : query, si ->
- filter(result, filter, cloneFlag, callingLauncher, pinnedByCallerSet,
- getPinnedByAnyLauncher, si));
+ forEachShortcut(si -> filter(result, filter, cloneFlag, callingLauncher, pinnedByCallerSet,
+ getPinnedByAnyLauncher, si));
}
/**
@@ -837,35 +820,12 @@
final ArraySet<String> pinnedByCallerSet = (callingLauncher == null) ? null
: s.getLauncherShortcutsLocked(callingLauncher, getPackageUserId(), launcherUserId)
.getPinnedShortcutIds(getPackageName(), getPackageUserId());
- final List<ShortcutInfo> shortcuts = getShortcutById(ids);
- if (shortcuts != null) {
- for (ShortcutInfo si : shortcuts) {
- filter(result, query, cloneFlag, callingLauncher, pinnedByCallerSet,
- getPinnedByAnyLauncher, si);
- }
+ for (ShortcutInfo si : mShortcuts.values()) {
+ filter(result, query, cloneFlag, callingLauncher, pinnedByCallerSet,
+ getPinnedByAnyLauncher, si);
}
}
- /**
- * Find all pinned shortcuts that match {@code query}.
- */
- public void findAllPinned(@NonNull List<ShortcutInfo> result,
- @Nullable Predicate<ShortcutInfo> query, int cloneFlag,
- @Nullable String callingLauncher, int launcherUserId, boolean getPinnedByAnyLauncher) {
- if (getPackageInfo().isShadow()) {
- // Restored and the app not installed yet, so don't return any.
- return;
- }
- final ShortcutService s = mShortcutUser.mService;
-
- // Set of pinned shortcuts by the calling launcher.
- final ArraySet<String> pinnedByCallerSet = (callingLauncher == null) ? null
- : s.getLauncherShortcutsLocked(callingLauncher, getPackageUserId(), launcherUserId)
- .getPinnedShortcutIds(getPackageName(), getPackageUserId());
- mShortcuts.values().forEach(si -> filter(result, query, cloneFlag, callingLauncher,
- pinnedByCallerSet, getPinnedByAnyLauncher, si));
- }
-
private void filter(@NonNull final List<ShortcutInfo> result,
@Nullable final Predicate<ShortcutInfo> query, final int cloneFlag,
@Nullable final String callingLauncher,
@@ -930,8 +890,8 @@
// Get the list of all dynamic shortcuts in this package.
final ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
- findAll(shortcuts, AppSearchShortcutInfo.QUERY_IS_NON_MANIFEST_VISIBLE,
- ShortcutInfo::isNonManifestVisible, ShortcutInfo.CLONE_REMOVE_FOR_APP_PREDICTION);
+ findAll(shortcuts, ShortcutInfo::isNonManifestVisible,
+ ShortcutInfo.CLONE_REMOVE_FOR_APP_PREDICTION);
final List<ShortcutManager.ShareShortcutInfo> result = new ArrayList<>();
for (int i = 0; i < shortcuts.size(); i++) {
@@ -975,8 +935,8 @@
// Get the list of all dynamic shortcuts in this package
final ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
- findAll(shortcuts, AppSearchShortcutInfo.QUERY_IS_NON_MANIFEST_VISIBLE,
- ShortcutInfo::isNonManifestVisible, ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
+ findAll(shortcuts, ShortcutInfo::isNonManifestVisible,
+ ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
int sharingShortcutCount = 0;
for (int i = 0; i < shortcuts.size(); i++) {
@@ -1129,53 +1089,37 @@
getPackageInfo().getVersionCode(), pi.getLongVersionCode()));
}
getPackageInfo().updateFromPackageInfo(pi);
- if (isAppSearchEnabled()) {
- // Save the states in memory and resume package rescan when needed
- mRescanRequired = true;
- mIsNewApp = isNewApp;
- mManifestShortcuts = newManifestShortcutList;
- } else {
- rescanPackage(isNewApp, newManifestShortcutList);
- }
- return true; // true means changed.
- }
-
- private void rescanPackage(
- final boolean isNewApp, @NonNull final List<ShortcutInfo> newManifestShortcutList) {
- final ShortcutService s = mShortcutUser.mService;
final long newVersionCode = getPackageInfo().getVersionCode();
// See if there are any shortcuts that were prevented restoring because the app was of a
// lower version, and re-enable them.
{
- forEachShortcutMutateIf(
- AppSearchShortcutInfo.QUERY_DISABLED_REASON_VERSION_LOWER, si -> {
- if (si.getDisabledReason() != ShortcutInfo.DISABLED_REASON_VERSION_LOWER) {
- return false;
+ forEachShortcutMutate(si -> {
+ if (si.getDisabledReason() != ShortcutInfo.DISABLED_REASON_VERSION_LOWER) {
+ return;
+ }
+ if (getPackageInfo().getBackupSourceVersionCode() > newVersionCode) {
+ if (ShortcutService.DEBUG) {
+ Slog.d(TAG,
+ String.format(
+ "Shortcut %s require version %s, still not restored.",
+ si.getId(),
+ getPackageInfo().getBackupSourceVersionCode()));
}
- if (getPackageInfo().getBackupSourceVersionCode() > newVersionCode) {
- if (ShortcutService.DEBUG) {
- Slog.d(TAG,
- String.format(
- "Shortcut %s require version %s, still not restored.",
- si.getId(),
- getPackageInfo().getBackupSourceVersionCode()));
- }
- return false;
- }
- Slog.i(TAG, String.format("Restoring shortcut: %s", si.getId()));
- si.clearFlags(ShortcutInfo.FLAG_DISABLED);
- si.setDisabledReason(ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
- return true;
- });
+ return;
+ }
+ Slog.i(TAG, String.format("Restoring shortcut: %s", si.getId()));
+ si.clearFlags(ShortcutInfo.FLAG_DISABLED);
+ si.setDisabledReason(ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
+ });
}
// For existing shortcuts, update timestamps if they have any resources.
// Also check if shortcuts' activities are still main activities. Otherwise, disable them.
if (!isNewApp) {
final Resources publisherRes = getPackageResources();
- forEachShortcutMutateIf(si -> {
+ forEachShortcutMutate(si -> {
// Disable dynamic shortcuts whose target activity is gone.
if (si.isDynamic()) {
if (si.getActivity() == null) {
@@ -1187,15 +1131,16 @@
"%s is no longer main activity. Disabling shorcut %s.",
getPackageName(), si.getId()));
if (disableDynamicWithId(si.getId(), /*ignoreInvisible*/ false,
- ShortcutInfo.DISABLED_REASON_APP_CHANGED) != null) {
- return false; // Actually removed.
+ ShortcutInfo.DISABLED_REASON_APP_CHANGED,
+ /*ignorePersistedShortcuts*/ false) != null) {
+ return;
}
// Still pinned, so fall-through and possibly update the resources.
}
}
if (!si.hasAnyResources() || publisherRes == null) {
- return false;
+ return;
}
if (!si.isOriginallyFromManifest()) {
@@ -1206,7 +1151,6 @@
// from resource names. (We don't allow resource strings for
// non-manifest at the moment, but icons can still be resources.)
si.setTimestamp(s.injectCurrentTimeMillis());
- return true;
});
}
@@ -1222,7 +1166,8 @@
// This will send a notification to the launcher, and also save .
// TODO: List changed and removed manifest shortcuts and pass to packageShortcutsChanged()
s.packageShortcutsChanged(getPackageName(), getPackageUserId(), null, null);
- mManifestShortcuts = null;
+
+ return true;
}
private boolean publishManifestShortcuts(List<ShortcutInfo> newManifestShortcutList) {
@@ -1297,7 +1242,8 @@
final String id = toDisableList.valueAt(i);
- disableWithId(id, /* disable message =*/ null, /* disable message resid */ 0,
+ disableWithId(id, /* disable message =*/ null,
+ /* disable message resid */ 0,
/* overrideImmutable=*/ true, /*ignoreInvisible=*/ false,
ShortcutInfo.DISABLED_REASON_APP_CHANGED);
}
@@ -1338,7 +1284,8 @@
service.wtf("Found manifest shortcuts in excess list.");
continue;
}
- deleteDynamicWithId(shortcut.getId(), /*ignoreInvisible=*/ true);
+ deleteDynamicWithId(shortcut.getId(), /*ignoreInvisible=*/ true,
+ /*ignorePersistedShortcuts=*/ true);
}
}
@@ -1494,12 +1441,11 @@
final List<ShortcutInfo> changedShortcuts = new ArrayList<>(1);
if (publisherRes != null) {
- forEachShortcutMutateIf(AppSearchShortcutInfo.QUERY_HAS_STRING_RESOURCE, si -> {
- if (!si.hasStringResources()) return false;
+ forEachShortcutMutate(si -> {
+ if (!si.hasStringResources()) return;
si.resolveResourceStrings(publisherRes);
si.setTimestamp(s.injectCurrentTimeMillis());
changedShortcuts.add(si);
- return true;
});
}
if (!CollectionUtils.isEmpty(changedShortcuts)) {
@@ -1549,13 +1495,11 @@
final long now = s.injectCurrentTimeMillis();
// First, clear ranks for floating shortcuts.
- forEachShortcutMutateIf(AppSearchShortcutInfo.QUERY_IS_FLOATING_AND_HAS_RANK, si -> {
+ forEachShortcutMutate(si -> {
if (si.isFloating() && si.getRank() != 0) {
si.setTimestamp(now);
si.setRank(0);
- return true;
}
- return false;
});
// Then adjust ranks. Ranks are unique for each activity, so we first need to sort
@@ -1581,7 +1525,7 @@
}
// At this point, it must be dynamic.
if (!si.isDynamic()) {
- s.wtf("Non-dynamic shortcut found.");
+ s.wtf("Non-dynamic shortcut found. " + si.toInsecureString());
continue;
}
final int thisRank = rank++;
@@ -1745,13 +1689,10 @@
ShortcutService.writeAttr(out, ATTR_CALL_COUNT, mApiCallCount);
ShortcutService.writeAttr(out, ATTR_LAST_RESET, mLastResetTime);
if (!forBackup) {
- /**
- * Schema version should not be included in the backup because:
- * 1. Schemas in AppSearch are created from scratch on new device
- * 2. Shortcuts are restored from xml file (as opposed to from AppSearch) on new device
- */
- ShortcutService.writeAttr(out, ATTR_SCHEMA_VERSON, (mIsInitilized)
- ? AppSearchShortcutInfo.SCHEMA_VERSION : 0);
+ synchronized (mLock) {
+ ShortcutService.writeAttr(out, ATTR_SCHEMA_VERSON, (mIsAppSearchSchemaUpToDate)
+ ? AppSearchShortcutInfo.SCHEMA_VERSION : 0);
+ }
}
getPackageInfo().saveToXml(mShortcutUser.mService, out, forBackup);
@@ -1763,6 +1704,8 @@
for (int j = 0; j < shareTargetSize; j++) {
mShareTargets.get(j).saveToXml(out);
}
+ saveShortcutsAsync(mShortcuts.values().stream().filter(ShortcutInfo::usesQuota)
+ .collect(Collectors.toList()));
}
out.endTag(null, TAG_ROOT);
@@ -1943,8 +1886,10 @@
final ShortcutPackage ret = new ShortcutPackage(shortcutUser,
shortcutUser.getUserId(), packageName);
- ret.mIsInitilized = ShortcutService.parseIntAttribute(parser, ATTR_SCHEMA_VERSON, 0)
- == AppSearchShortcutInfo.SCHEMA_VERSION;
+ synchronized (ret.mLock) {
+ ret.mIsAppSearchSchemaUpToDate = ShortcutService.parseIntAttribute(
+ parser, ATTR_SCHEMA_VERSON, 0) == AppSearchShortcutInfo.SCHEMA_VERSION;
+ }
ret.mApiCallCount =
ShortcutService.parseIntAttribute(parser, ATTR_CALL_COUNT);
ret.mLastResetTime =
@@ -2297,8 +2242,15 @@
} else {
mPackageIdentifiers.remove(packageName);
}
- awaitInAppSearch(true, "Update visibility",
- session -> AndroidFuture.completedFuture(true));
+ synchronized (mLock) {
+ mIsAppSearchSchemaUpToDate = false;
+ }
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ fromAppSearch();
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
}
void mutateShortcut(@NonNull final String id, @Nullable final ShortcutInfo shortcut,
@@ -2325,148 +2277,15 @@
private void saveShortcut(@NonNull final Collection<ShortcutInfo> shortcuts) {
Objects.requireNonNull(shortcuts);
- if (!isAppSearchEnabled()) {
- // If AppSearch isn't enabled, save it in memory and we are done.
- for (ShortcutInfo si : shortcuts) {
- mShortcuts.put(si.getId(), si);
- }
- return;
+ for (ShortcutInfo si : shortcuts) {
+ mShortcuts.put(si.getId(), si);
}
- // Otherwise, save pinned shortcuts in memory.
- shortcuts.forEach(si -> {
- if (si.isPinned()) {
- mShortcuts.put(si.getId(), si);
- } else {
- mShortcuts.remove(si.getId());
- }
- });
- // Then proceed to app search.
- saveToAppSearch(shortcuts);
- }
-
- private void saveToAppSearch(@NonNull final Collection<ShortcutInfo> shortcuts) {
- Objects.requireNonNull(shortcuts);
- if (!isAppSearchEnabled() || shortcuts.isEmpty()) {
- // No need to invoke AppSearch when there's nothing to save.
- return;
- }
- if (ShortcutService.DEBUG_REBOOT) {
- Slog.d(TAG, "Saving shortcuts for user=" + mShortcutUser.getUserId()
- + " pkg=" + getPackageName() + " ids=["
- + shortcuts.stream().map(ShortcutInfo::getId)
- .collect(Collectors.joining(",")) + "]");
- }
- awaitInAppSearch("Saving shortcuts", session -> {
- final AndroidFuture<Boolean> future = new AndroidFuture<>();
- session.put(new PutDocumentsRequest.Builder()
- .addGenericDocuments(
- AppSearchShortcutInfo.toGenericDocuments(shortcuts))
- .build(),
- mShortcutUser.mExecutor,
- result -> {
- if (!result.isSuccess()) {
- for (AppSearchResult<Void> k : result.getFailures().values()) {
- Slog.e(TAG, k.getErrorMessage());
- }
- future.completeExceptionally(new RuntimeException(
- "Failed to save shortcuts"));
- return;
- }
- future.complete(true);
- });
- return future;
- });
- }
-
- /**
- * Removes shortcuts from AppSearch.
- */
- void removeShortcuts() {
- if (!isAppSearchEnabled()) {
- return;
- }
- awaitInAppSearch("Removing all shortcuts from " + getPackageName(), session -> {
- final AndroidFuture<Boolean> future = new AndroidFuture<>();
- session.remove("", getSearchSpec(), mShortcutUser.mExecutor, result -> {
- if (!result.isSuccess()) {
- future.completeExceptionally(
- new RuntimeException(result.getErrorMessage()));
- return;
- }
- future.complete(true);
- });
- return future;
- });
- }
-
- private void removeShortcut(@NonNull final String id) {
- Objects.requireNonNull(id);
- mShortcuts.remove(id);
- if (!isAppSearchEnabled()) {
- return;
- }
- awaitInAppSearch("Removing shortcut with id=" + id, session -> {
- final AndroidFuture<Boolean> future = new AndroidFuture<>();
- session.remove(
- new RemoveByDocumentIdRequest.Builder(getPackageName()).addIds(id).build(),
- mShortcutUser.mExecutor, result -> {
- if (!result.isSuccess()) {
- final Map<String, AppSearchResult<Void>> failures =
- result.getFailures();
- for (String key : failures.keySet()) {
- Slog.e(TAG, "Failed deleting " + key + ", error message:"
- + failures.get(key).getErrorMessage());
- }
- future.completeExceptionally(new RuntimeException(
- "Failed to delete shortcut: " + id));
- return;
- }
- future.complete(true);
- });
- return future;
- });
}
@Nullable
- private List<ShortcutInfo> getShortcutById(@NonNull final Collection<String> ids) {
- final List<String> shortcutIds = new ArrayList<>(1);
- for (String id : ids) {
- if (id != null) {
- shortcutIds.add(id);
- }
- }
- if (!isAppSearchEnabled()) {
- final List<ShortcutInfo> ret = new ArrayList<>(1);
- for (int i = mShortcuts.size() - 1; i >= 0; i--) {
- ShortcutInfo si = mShortcuts.valueAt(i);
- if (shortcutIds.contains(si.getId())) {
- ret.add(si);
- }
- }
- return ret;
- }
- if (ShortcutService.DEBUG_REBOOT) {
- Slog.d(TAG, "Getting shortcuts for user=" + mShortcutUser.getUserId()
- + " pkg=" + getPackageName() + " ids: [" + String.join(",", ids) + "]");
- }
- return awaitInAppSearch("Getting shortcut by id", session -> {
- final AndroidFuture<List<ShortcutInfo>> future = new AndroidFuture<>();
- session.getByDocumentId(
- new GetByDocumentIdRequest.Builder(getPackageName())
- .addIds(shortcutIds).build(),
- mShortcutUser.mExecutor,
- results -> {
- final List<ShortcutInfo> ret = new ArrayList<>(1);
- Map<String, GenericDocument> documents = results.getSuccesses();
- for (GenericDocument doc : documents.values()) {
- final ShortcutInfo info = new AppSearchShortcutInfo(doc)
- .toShortcutInfo(mShortcutUser.getUserId());
- ret.add(info);
- }
- future.complete(ret);
- });
- return future;
- });
+ List<ShortcutInfo> findAll(@NonNull final Collection<String> ids) {
+ return ids.stream().map(mShortcuts::get)
+ .filter(Objects::nonNull).collect(Collectors.toList());
}
private void forEachShortcut(@NonNull final Consumer<ShortcutInfo> cb) {
@@ -2482,40 +2301,9 @@
}
private void forEachShortcutMutate(@NonNull final Consumer<ShortcutInfo> cb) {
- forEachShortcutMutateIf(si -> {
+ for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+ ShortcutInfo si = mShortcuts.valueAt(i);
cb.accept(si);
- return true;
- });
- }
-
- private void forEachShortcutMutateIf(@NonNull final Function<ShortcutInfo, Boolean> cb) {
- forEachShortcutMutateIf("", cb);
- }
-
- private void forEachShortcutMutateIf(@NonNull final String query,
- @NonNull final Function<ShortcutInfo, Boolean> cb) {
- if (!isAppSearchEnabled()) {
- for (int i = mShortcuts.size() - 1; i >= 0; i--) {
- ShortcutInfo si = mShortcuts.valueAt(i);
- cb.apply(si);
- }
- return;
- }
- if (ShortcutService.DEBUG_REBOOT) {
- Slog.d(TAG, "Changing shortcuts for user=" + mShortcutUser.getUserId()
- + " pkg=" + getPackageName());
- }
- final SearchResults res = awaitInAppSearch("Mutating shortcuts", session ->
- AndroidFuture.completedFuture(session.search(query, getSearchSpec())));
- if (res == null) return;
- List<ShortcutInfo> shortcuts = getNextPage(res);
- while (!shortcuts.isEmpty()) {
- final List<ShortcutInfo> changed = new ArrayList<>(1);
- for (ShortcutInfo si : shortcuts) {
- if (cb.apply(si)) changed.add(si);
- }
- saveShortcut(changed);
- shortcuts = getNextPage(res);
}
}
@@ -2526,114 +2314,10 @@
private void forEachShortcutStopWhen(
@NonNull final String query, @NonNull final Function<ShortcutInfo, Boolean> cb) {
- if (!isAppSearchEnabled()) {
- for (int i = mShortcuts.size() - 1; i >= 0; i--) {
- final ShortcutInfo si = mShortcuts.valueAt(i);
- if (cb.apply(si)) {
- return;
- }
- }
- return;
- }
- if (ShortcutService.DEBUG_REBOOT) {
- Slog.d(TAG, "Iterating shortcuts for user=" + mShortcutUser.getUserId()
- + " pkg=" + getPackageName());
- }
- final SearchResults res = awaitInAppSearch("Iterating shortcuts", session ->
- AndroidFuture.completedFuture(session.search(query, getSearchSpec())));
- if (res == null) return;
- List<ShortcutInfo> shortcuts = getNextPage(res);
- while (!shortcuts.isEmpty()) {
- for (ShortcutInfo si : shortcuts) {
- if (cb.apply(si)) return;
- }
- shortcuts = getNextPage(res);
- }
- }
-
- private List<ShortcutInfo> getNextPage(@NonNull final SearchResults res) {
- if (ShortcutService.DEBUG_REBOOT) {
- Slog.d(TAG, "Get next page for search result for user=" + mShortcutUser.getUserId()
- + " pkg=" + getPackageName());
- }
- final AndroidFuture<List<ShortcutInfo>> future = new AndroidFuture<>();
- final List<ShortcutInfo> ret = new ArrayList<>();
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- res.getNextPage(mShortcutUser.mExecutor, nextPage -> {
- if (!nextPage.isSuccess()) {
- future.complete(ret);
- return;
- }
- final List<SearchResult> results = nextPage.getResultValue();
- if (results.isEmpty()) {
- future.complete(ret);
- return;
- }
- final List<ShortcutInfo> page = new ArrayList<>(results.size());
- for (SearchResult result : results) {
- final ShortcutInfo si = new AppSearchShortcutInfo(result.getGenericDocument())
- .toShortcutInfo(mShortcutUser.getUserId());
- page.add(si);
- }
- ret.addAll(page);
- future.complete(ret);
- });
- return ConcurrentUtils.waitForFutureNoInterrupt(future,
- "Getting next batch of shortcuts");
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
- }
- }
-
- @Nullable
- private <T> T awaitInAppSearch(
- @NonNull final String description,
- @NonNull final Function<AppSearchSession, CompletableFuture<T>> cb) {
- return awaitInAppSearch(false, description, cb);
- }
-
- @Nullable
- private <T> T awaitInAppSearch(
- final boolean forceReset,
- @NonNull final String description,
- @NonNull final Function<AppSearchSession, CompletableFuture<T>> cb) {
- if (!isAppSearchEnabled()) {
- throw new IllegalStateException(
- "awaitInAppSearch called when app search integration is disabled");
- }
- synchronized (mLock) {
- final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
- final long callingIdentity = Binder.clearCallingIdentity();
- final AppSearchManager.SearchContext searchContext =
- new AppSearchManager.SearchContext.Builder(getPackageName()).build();
- try (AppSearchSession session = ConcurrentUtils.waitForFutureNoInterrupt(
- mShortcutUser.getAppSearch(searchContext), "Resetting app search")) {
- StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
- .detectAll()
- .penaltyLog() // TODO: change this to penaltyDeath to fix the call-site
- .build());
- final boolean wasInitialized = mIsInitilized;
- if (!wasInitialized || forceReset) {
- ConcurrentUtils.waitForFutureNoInterrupt(
- setupSchema(session), "Setting up schema");
- }
- mIsInitilized = true;
- if (!wasInitialized) {
- restoreParsedShortcuts(false);
- }
- if (mRescanRequired) {
- mRescanRequired = false;
- rescanPackage(mIsNewApp, mManifestShortcuts);
- }
- return ConcurrentUtils.waitForFutureNoInterrupt(cb.apply(session), description);
- } catch (Exception e) {
- Slog.e(TAG, "Failed to initiate app search for shortcut package "
- + getPackageName() + " user " + mShortcutUser.getUserId(), e);
- return null;
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
- StrictMode.setThreadPolicy(oldPolicy);
+ for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+ final ShortcutInfo si = mShortcuts.valueAt(i);
+ if (cb.apply(si)) {
+ return;
}
}
}
@@ -2657,7 +2341,7 @@
}
final AndroidFuture<AppSearchSession> future = new AndroidFuture<>();
session.setSchema(
- schemaBuilder.build(), mShortcutUser.mExecutor, mShortcutUser.mExecutor, result -> {
+ schemaBuilder.build(), mExecutor, mShortcutUser.mExecutor, result -> {
if (!result.isSuccess()) {
future.completeExceptionally(
new IllegalArgumentException(result.getErrorMessage()));
@@ -2677,29 +2361,6 @@
.build();
}
- /**
- * Replace shortcuts parsed from xml file.
- */
- void restoreParsedShortcuts() {
- restoreParsedShortcuts(true);
- }
-
- private void restoreParsedShortcuts(final boolean replace) {
- if (ShortcutService.DEBUG_REBOOT) {
- if (replace) {
- Slog.d(TAG, "Replacing all shortcuts with the ones parsed from xml for user="
- + mShortcutUser.getUserId() + " pkg=" + getPackageName());
- } else {
- Slog.d(TAG, "Restoring pinned shortcuts from xml for user="
- + mShortcutUser.getUserId() + " pkg=" + getPackageName());
- }
- }
- if (replace) {
- removeShortcuts();
- }
- saveToAppSearch(mShortcuts.values());
- }
-
private boolean verifyRanksSequential(List<ShortcutInfo> list) {
boolean failed = false;
@@ -2713,4 +2374,133 @@
}
return failed;
}
+
+ // Async Operations
+
+ /**
+ * Removes all shortcuts from AppSearch.
+ */
+ void removeAllShortcutsAsync() {
+ if (!isAppSearchEnabled()) {
+ return;
+ }
+ runAsSystem(() -> fromAppSearch().thenAccept(session ->
+ session.remove("", getSearchSpec(), mShortcutUser.mExecutor, result -> {
+ if (!result.isSuccess()) {
+ Slog.e(TAG, "Failed to remove shortcuts from AppSearch. "
+ + result.getErrorMessage());
+ }
+ })));
+ }
+
+ private void removeShortcutAsync(@NonNull final String... id) {
+ Objects.requireNonNull(id);
+ removeShortcutAsync(Arrays.asList(id));
+ }
+
+ private void removeShortcutAsync(@NonNull final Collection<String> ids) {
+ if (!isAppSearchEnabled()) {
+ return;
+ }
+ runAsSystem(() -> fromAppSearch().thenAccept(session ->
+ session.remove(
+ new RemoveByDocumentIdRequest.Builder(getPackageName()).addIds(ids).build(),
+ mShortcutUser.mExecutor, result -> {
+ if (!result.isSuccess()) {
+ final Map<String, AppSearchResult<Void>> failures =
+ result.getFailures();
+ for (String key : failures.keySet()) {
+ Slog.e(TAG, "Failed deleting " + key + ", error message:"
+ + failures.get(key).getErrorMessage());
+ }
+ }
+ })));
+ }
+
+ private void saveShortcutsAsync(
+ @NonNull final Collection<ShortcutInfo> shortcuts) {
+ Objects.requireNonNull(shortcuts);
+ if (!isAppSearchEnabled() || shortcuts.isEmpty()) {
+ // No need to invoke AppSearch when there's nothing to save.
+ return;
+ }
+ if (ShortcutService.DEBUG_REBOOT) {
+ Slog.d(TAG, "Saving shortcuts async for user=" + mShortcutUser.getUserId()
+ + " pkg=" + getPackageName() + " ids=["
+ + shortcuts.stream().map(ShortcutInfo::getId)
+ .collect(Collectors.joining(",")) + "]");
+ }
+ runAsSystem(() -> fromAppSearch().thenAccept(session -> {
+ if (shortcuts.isEmpty()) {
+ return;
+ }
+ session.put(new PutDocumentsRequest.Builder()
+ .addGenericDocuments(
+ AppSearchShortcutInfo.toGenericDocuments(shortcuts))
+ .build(),
+ mShortcutUser.mExecutor,
+ result -> {
+ if (!result.isSuccess()) {
+ for (AppSearchResult<Void> k : result.getFailures().values()) {
+ Slog.e(TAG, k.getErrorMessage());
+ }
+ }
+ });
+ }));
+ }
+
+ @VisibleForTesting
+ void getTopShortcutsFromPersistence(AndroidFuture<List<ShortcutInfo>> cb) {
+ runAsSystem(() -> fromAppSearch().thenAccept(session -> {
+ SearchResults res = session.search("", getSearchSpec());
+ res.getNextPage(mShortcutUser.mExecutor, results -> {
+ if (!results.isSuccess()) {
+ cb.completeExceptionally(new IllegalStateException(results.getErrorMessage()));
+ return;
+ }
+ cb.complete(results.getResultValue().stream()
+ .map(SearchResult::getGenericDocument)
+ .map(AppSearchShortcutInfo::new)
+ .map(si -> si.toShortcutInfo(mShortcutUser.getUserId()))
+ .collect(Collectors.toList()));
+ });
+ }));
+ }
+
+ @NonNull
+ private AndroidFuture<AppSearchSession> fromAppSearch() {
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
+ final AppSearchManager.SearchContext searchContext =
+ new AppSearchManager.SearchContext.Builder(getPackageName()).build();
+ AndroidFuture<AppSearchSession> future = null;
+ try {
+ StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
+ .detectAll()
+ .penaltyLog() // TODO: change this to penaltyDeath to fix the call-site
+ .build());
+ future = mShortcutUser.getAppSearch(searchContext);
+ synchronized (mLock) {
+ if (!mIsAppSearchSchemaUpToDate) {
+ future = future.thenCompose(this::setupSchema);
+ }
+ mIsAppSearchSchemaUpToDate = true;
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to invoke app search pkg="
+ + getPackageName() + " user=" + mShortcutUser.getUserId(), e);
+ Objects.requireNonNull(future).completeExceptionally(e);
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
+ return Objects.requireNonNull(future);
+ }
+
+ private void runAsSystem(@NonNull final Runnable fn) {
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ fn.run();
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
index e21c9c2..c1f57f9 100644
--- a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
+++ b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
@@ -467,7 +467,7 @@
launcher.attemptToRestoreIfNeededAndSave();
if (launcher.hasPinned(original)) {
if (DEBUG) {
- Slog.d(TAG, "Shortcut " + original + " already pinned."); // This too.
+ Slog.d(TAG, "Shortcut " + original + " already pinned."); // This too.
}
return true;
}
@@ -517,7 +517,8 @@
if (DEBUG) {
Slog.d(TAG, "Removing " + shortcutId + " as dynamic");
}
- ps.deleteDynamicWithId(shortcutId, /*ignoreInvisible=*/ false);
+ ps.deleteDynamicWithId(shortcutId, /*ignoreInvisible=*/ false,
+ /*wasPushedOut=*/ false);
}
ps.adjustRanks(); // Shouldn't be needed, but just in case.
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 687a165..85b7435 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1965,13 +1965,12 @@
ArrayList<ShortcutInfo> cachedOrPinned = new ArrayList<>();
ps.findAll(cachedOrPinned,
- AppSearchShortcutInfo.QUERY_IS_VISIBLE_CACHED_OR_PINNED,
(ShortcutInfo si) -> si.isVisibleToPublisher()
&& si.isDynamic() && (si.isCached() || si.isPinned()),
ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
// First, remove all un-pinned and non-cached; dynamic shortcuts
- removedShortcuts = ps.deleteAllDynamicShortcuts(/*ignoreInvisible=*/ true);
+ removedShortcuts = ps.deleteAllDynamicShortcuts();
// Then, add/update all. We need to make sure to take over "pinned" flag.
for (int i = 0; i < size; i++) {
@@ -2381,7 +2380,8 @@
if (!ps.isShortcutExistsAndVisibleToPublisher(id)) {
continue;
}
- ShortcutInfo removed = ps.deleteDynamicWithId(id, /*ignoreInvisible=*/ true);
+ ShortcutInfo removed = ps.deleteDynamicWithId(id, /*ignoreInvisible=*/ true,
+ /*wasPushedOut*/ false);
if (removed == null) {
if (changedShortcuts == null) {
changedShortcuts = new ArrayList<>(1);
@@ -2412,11 +2412,10 @@
userId);
// Dynamic shortcuts that are either cached or pinned will not get deleted.
ps.findAll(changedShortcuts,
- AppSearchShortcutInfo.QUERY_IS_VISIBLE_CACHED_OR_PINNED,
(ShortcutInfo si) -> si.isVisibleToPublisher()
&& si.isDynamic() && (si.isCached() || si.isPinned()),
ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
- removedShortcuts = ps.deleteAllDynamicShortcuts(/*ignoreInvisible=*/ true);
+ removedShortcuts = ps.deleteAllDynamicShortcuts();
changedShortcuts = prepareChangedShortcuts(
changedShortcuts, null, removedShortcuts, ps);
}
@@ -2475,10 +2474,8 @@
| (matchPinned ? ShortcutInfo.FLAG_PINNED : 0)
| (matchManifest ? ShortcutInfo.FLAG_MANIFEST : 0)
| (matchCached ? ShortcutInfo.FLAG_CACHED_ALL : 0);
- final String query = AppSearchShortcutInfo.QUERY_IS_VISIBLE_TO_PUBLISHER + " "
- + createQuery(matchDynamic, matchPinned, matchManifest, matchCached);
return getShortcutsWithQueryLocked(
- packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR, query,
+ packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
(ShortcutInfo si) ->
si.isVisibleToPublisher()
&& (si.getFlags() & shortcutFlags) != 0);
@@ -2542,13 +2539,12 @@
@GuardedBy("mLock")
private ParceledListSlice<ShortcutInfo> getShortcutsWithQueryLocked(@NonNull String packageName,
- @UserIdInt int userId, int cloneFlags, @NonNull final String query,
- @NonNull Predicate<ShortcutInfo> filter) {
+ @UserIdInt int userId, int cloneFlags, @NonNull Predicate<ShortcutInfo> filter) {
final ArrayList<ShortcutInfo> ret = new ArrayList<>();
final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
- ps.findAll(ret, query, filter, cloneFlags);
+ ps.findAll(ret, filter, cloneFlags);
return new ParceledListSlice<>(setReturnedByServer(ret));
}
@@ -2955,27 +2951,14 @@
((queryFlags & ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER) != 0);
queryFlags |= (getPinnedByAnyLauncher ? ShortcutQuery.FLAG_MATCH_PINNED : 0);
- final boolean matchPinnedOnly =
- ((queryFlags & ShortcutQuery.FLAG_MATCH_PINNED) != 0)
- && ((queryFlags & ShortcutQuery.FLAG_MATCH_CACHED) == 0)
- && ((queryFlags & ShortcutQuery.FLAG_MATCH_DYNAMIC) == 0)
- && ((queryFlags & ShortcutQuery.FLAG_MATCH_MANIFEST) == 0);
-
final Predicate<ShortcutInfo> filter = getFilterFromQuery(ids, locusIds, changedSince,
componentName, queryFlags, getPinnedByAnyLauncher);
- if (matchPinnedOnly) {
- p.findAllPinned(ret, filter, cloneFlag, callingPackage, launcherUserId,
- getPinnedByAnyLauncher);
- } else if (ids != null && !ids.isEmpty()) {
+ if (ids != null && !ids.isEmpty()) {
p.findAllByIds(ret, ids, filter, cloneFlag, callingPackage, launcherUserId,
getPinnedByAnyLauncher);
} else {
- final boolean matchDynamic = (queryFlags & ShortcutQuery.FLAG_MATCH_DYNAMIC) != 0;
- final boolean matchPinned = (queryFlags & ShortcutQuery.FLAG_MATCH_PINNED) != 0;
- final boolean matchManifest = (queryFlags & ShortcutQuery.FLAG_MATCH_MANIFEST) != 0;
- final boolean matchCached = (queryFlags & ShortcutQuery.FLAG_MATCH_CACHED) != 0;
- p.findAll(ret, createQuery(matchDynamic, matchPinned, matchManifest, matchCached),
- filter, cloneFlag, callingPackage, launcherUserId, getPinnedByAnyLauncher);
+ p.findAll(ret, filter, cloneFlag, callingPackage, launcherUserId,
+ getPinnedByAnyLauncher);
}
}
@@ -3090,8 +3073,7 @@
if (sp != null) {
// List the shortcuts that are pinned only, these will get removed.
removedShortcuts = new ArrayList<>();
- sp.findAll(removedShortcuts, AppSearchShortcutInfo.QUERY_IS_VISIBLE_PINNED_ONLY,
- (ShortcutInfo si) -> si.isVisibleToPublisher()
+ sp.findAll(removedShortcuts, (ShortcutInfo si) -> si.isVisibleToPublisher()
&& si.isPinned() && !si.isCached() && !si.isDynamic()
&& !si.isDeclaredInManifest(),
ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO,
@@ -3183,8 +3165,7 @@
if (doCache) {
if (si.isLongLived()) {
- sp.mutateShortcut(si.getId(), si,
- shortcut -> shortcut.addFlags(cacheFlags));
+ si.addFlags(cacheFlags);
if (changedShortcuts == null) {
changedShortcuts = new ArrayList<>(1);
}
@@ -3195,21 +3176,20 @@
}
} else {
ShortcutInfo removed = null;
- sp.mutateShortcut(si.getId(), si, shortcut ->
- shortcut.clearFlags(cacheFlags));
+ si.clearFlags(cacheFlags);
if (!si.isDynamic() && !si.isCached()) {
removed = sp.deleteLongLivedWithId(id, /*ignoreInvisible=*/ true);
}
- if (removed != null) {
- if (removedShortcuts == null) {
- removedShortcuts = new ArrayList<>(1);
- }
- removedShortcuts.add(removed);
- } else {
+ if (removed == null) {
if (changedShortcuts == null) {
changedShortcuts = new ArrayList<>(1);
}
changedShortcuts.add(si);
+ } else {
+ if (removedShortcuts == null) {
+ removedShortcuts = new ArrayList<>(1);
+ }
+ removedShortcuts.add(removed);
}
}
}
@@ -5084,8 +5064,7 @@
synchronized (mLock) {
final ShortcutPackage pkg = getPackageShortcutForTest(packageName, userId);
if (pkg == null) return;
-
- pkg.mutateShortcut(shortcutId, null, cb);
+ cb.accept(pkg.findShortcutById(shortcutId));
}
}
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index e66cb03..408f045 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -186,7 +186,7 @@
final ShortcutPackage removed = mPackages.remove(packageName);
if (removed != null) {
- removed.removeShortcuts();
+ removed.removeAllShortcutsAsync();
}
mService.cleanupBitmapsForPackage(mUserId, packageName);
@@ -577,7 +577,7 @@
Log.w(TAG, "Shortcuts for package " + sp.getPackageName() + " are being restored."
+ " Existing non-manifeset shortcuts will be overwritten.");
}
- sp.restoreParsedShortcuts();
+ sp.removeAllShortcutsAsync();
addPackage(sp);
restoredPackages[0]++;
restoredShortcuts[0] += sp.getShortcutCount();
@@ -714,6 +714,7 @@
.setSubtype(totalSharingShortcutCount));
}
+ @NonNull
AndroidFuture<AppSearchSession> getAppSearch(
@NonNull final AppSearchManager.SearchContext searchContext) {
final AndroidFuture<AppSearchSession> future = new AndroidFuture<>();
diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java
index b49c8da..12e644d 100644
--- a/services/core/java/com/android/server/pm/StorageEventHelper.java
+++ b/services/core/java/com/android/server/pm/StorageEventHelper.java
@@ -136,7 +136,7 @@
final Settings.VersionInfo ver;
final List<PackageSetting> packages;
- final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(mPm);
+ final InstallPackageHelper installPackageHelper = new InstallPackageHelper(mPm);
synchronized (mPm.mLock) {
ver = mPm.mSettings.findOrCreateVersion(volumeUuid);
packages = mPm.mSettings.getVolumePackagesLPr(volumeUuid);
@@ -147,7 +147,7 @@
synchronized (mPm.mInstallLock) {
final AndroidPackage pkg;
try {
- pkg = scanPackageHelper.scanPackageTracedLI(
+ pkg = installPackageHelper.scanSystemPackageTracedLI(
ps.getPath(), parseFlags, SCAN_INITIAL, 0, null);
loaded.add(pkg);
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java b/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
index 1155132..ce5a2e8 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
@@ -53,8 +53,8 @@
SharedUserSetting getSharedUser();
// TODO: Remove this in favor of boolean APIs
- int getPkgFlags();
- int getPkgPrivateFlags();
+ int getFlags();
+ int getPrivateFlags();
@NonNull
SparseArray<? extends PackageUserStateInternal> getUserStates();
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index ab61ef4..0394d4c 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -3992,7 +3992,7 @@
if (mHealthService == null) {
return StatsManager.PULL_SKIP;
}
- android.hardware.health.V1_0.HealthInfo healthInfo;
+ android.hardware.health.HealthInfo healthInfo;
try {
healthInfo = mHealthService.getHealthInfo();
} catch (RemoteException | IllegalStateException e) {
@@ -4008,13 +4008,13 @@
pulledValue = healthInfo.batteryLevel;
break;
case FrameworkStatsLog.REMAINING_BATTERY_CAPACITY:
- pulledValue = healthInfo.batteryChargeCounter;
+ pulledValue = healthInfo.batteryChargeCounterUah;
break;
case FrameworkStatsLog.FULL_BATTERY_CAPACITY:
- pulledValue = healthInfo.batteryFullCharge;
+ pulledValue = healthInfo.batteryFullChargeUah;
break;
case FrameworkStatsLog.BATTERY_VOLTAGE:
- pulledValue = healthInfo.batteryVoltage;
+ pulledValue = healthInfo.batteryVoltageMillivolts;
break;
case FrameworkStatsLog.BATTERY_CYCLE_COUNT:
pulledValue = healthInfo.batteryCycleCount;
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index a0f0367..232ea09 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -1065,8 +1065,7 @@
@Override
public void setImeWindowStatus(int displayId, final IBinder token, final int vis,
- final int backDisposition, final boolean showImeSwitcher,
- boolean isMultiClientImeEnabled) {
+ final int backDisposition, final boolean showImeSwitcher) {
enforceStatusBar();
if (SPEW) {
@@ -1083,8 +1082,7 @@
if (mBar == null) return;
try {
mBar.setImeWindowStatus(
- displayId, token, vis, backDisposition, showImeSwitcher,
- isMultiClientImeEnabled);
+ displayId, token, vis, backDisposition, showImeSwitcher);
} catch (RemoteException ex) { }
});
}
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 87ce1ce..e9d5ad6 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -317,6 +317,7 @@
PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
userId);
List<TvInputInfo> inputList = new ArrayList<>();
+ List<ComponentName> hardwareComponents = new ArrayList<>();
for (ResolveInfo ri : services) {
ServiceInfo si = ri.serviceInfo;
if (!android.Manifest.permission.BIND_TV_INPUT.equals(si.permission)) {
@@ -327,6 +328,7 @@
ComponentName component = new ComponentName(si.packageName, si.name);
if (hasHardwarePermission(pm, component)) {
+ hardwareComponents.add(component);
ServiceState serviceState = userState.serviceStateMap.get(component);
if (serviceState == null) {
// New hardware input found. Create a new ServiceState and connect to the
@@ -399,6 +401,15 @@
}
}
+ // Clean up ServiceState corresponding to the removed hardware inputs
+ Iterator<ServiceState> it = userState.serviceStateMap.values().iterator();
+ while (it.hasNext()) {
+ ServiceState serviceState = it.next();
+ if (serviceState.isHardware && !hardwareComponents.contains(serviceState.component)) {
+ it.remove();
+ }
+ }
+
userState.inputMap.clear();
userState.inputMap = inputMap;
}
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index 3177fac..0436460 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -457,17 +457,18 @@
if (!validateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_LNB, lnbHandle)) {
throw new RemoteException("lnbHandle can't be invalid");
}
- if (!checkClientExists(clientId)) {
- throw new RemoteException("Release lnb from unregistered client:" + clientId);
- }
- LnbResource lnb = getLnbResource(lnbHandle);
- if (lnb == null) {
- throw new RemoteException("Releasing lnb does not exist.");
- }
- if (lnb.getOwnerClientId() != clientId) {
- throw new RemoteException("Client is not the current owner of the releasing lnb.");
- }
synchronized (mLock) {
+ if (!checkClientExists(clientId)) {
+ throw new RemoteException("Release lnb from unregistered client:" + clientId);
+ }
+ LnbResource lnb = getLnbResource(lnbHandle);
+ if (lnb == null) {
+ throw new RemoteException("Releasing lnb does not exist.");
+ }
+ if (lnb.getOwnerClientId() != clientId) {
+ throw new RemoteException("Client is not the current owner "
+ + "of the releasing lnb.");
+ }
releaseLnbInternal(lnb);
}
}
@@ -869,6 +870,7 @@
frontendHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
ClientProfile requestClient = getClientProfile(request.clientId);
+ // TODO: check if this is really needed
if (requestClient == null) {
return false;
}
@@ -1205,7 +1207,9 @@
@Override
public void binderDied() {
synchronized (mLock) {
- removeClientProfile(mClientId);
+ if (checkClientExists(mClientId)) {
+ removeClientProfile(mClientId);
+ }
}
}
@@ -1246,6 +1250,7 @@
// Reclaim all the resources of the share owners of the frontend that is used by the current
// resource reclaimed client.
ClientProfile profile = getClientProfile(reclaimingClientId);
+ // TODO: check if this check is really needed.
if (profile == null) {
return true;
}
@@ -1553,6 +1558,7 @@
}
private void clearFrontendAndClientMapping(ClientProfile profile) {
+ // TODO: check if this check is really needed
if (profile == null) {
return;
}
@@ -1573,6 +1579,7 @@
}
private void clearAllResourcesAndClientMapping(ClientProfile profile) {
+ // TODO: check if this check is really needed. Maybe needed for reclaimResource path.
if (profile == null) {
return;
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index e190b1e..26e39c6 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -19,6 +19,7 @@
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.QUERY_ALL_PACKAGES;
import static android.Manifest.permission.READ_WALLPAPER_INTERNAL;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.app.WallpaperManager.COMMAND_REAPPLY;
import static android.app.WallpaperManager.FLAG_LOCK;
import static android.app.WallpaperManager.FLAG_SYSTEM;
@@ -794,6 +795,7 @@
private final Context mContext;
private final WindowManagerInternal mWindowManagerInternal;
private final IPackageManager mIPackageManager;
+ private final ActivityManager mActivityManager;
private final MyPackageMonitor mMonitor;
private final AppOpsManager mAppOpsManager;
@@ -942,6 +944,11 @@
*/
WallpaperColors primaryColors;
+ /**
+ * If the wallpaper was set from a foreground app (instead of from a background service).
+ */
+ public boolean fromForegroundApp;
+
WallpaperConnection connection;
long lastDiedTime;
boolean wallpaperUpdating;
@@ -1691,6 +1698,7 @@
mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
mDisplayManager = mContext.getSystemService(DisplayManager.class);
mDisplayManager.registerDisplayListener(mDisplayListener, null /* handler */);
+ mActivityManager = mContext.getSystemService(ActivityManager.class);
mMonitor = new MyPackageMonitor();
mColorsChangedListeners = new SparseArray<>();
@@ -2659,6 +2667,9 @@
}
}
+ final boolean fromForegroundApp = Binder.withCleanCallingIdentity(() ->
+ mActivityManager.getPackageImportance(callingPackage) == IMPORTANCE_FOREGROUND);
+
synchronized (mLock) {
if (DEBUG) Slog.v(TAG, "setWallpaper which=0x" + Integer.toHexString(which));
WallpaperData wallpaper;
@@ -2681,6 +2692,7 @@
wallpaper.imageWallpaperPending = true;
wallpaper.whichPending = which;
wallpaper.setComplete = completion;
+ wallpaper.fromForegroundApp = fromForegroundApp;
wallpaper.cropHint.set(cropHint);
wallpaper.allowBackup = allowBackup;
}
@@ -3063,6 +3075,7 @@
wallpaper.callbacks.finishBroadcast();
final Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);
+ intent.putExtra(WallpaperManager.EXTRA_FROM_FOREGROUND_APP, wallpaper.fromForegroundApp);
mContext.sendBroadcastAsUser(intent, new UserHandle(mCurrentUserId));
}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 15db790..2f9c138 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -767,10 +767,6 @@
if (compatStateInfo.mLastLoggedActivity == r) {
compatStateInfo.mLastLoggedActivity = null;
}
- if (compatStateInfo.mVisibleActivities.isEmpty()) {
- // No need to keep the entry if there are no visible activities.
- mPackageUidToCompatStateInfo.remove(packageUid);
- }
}
/**
@@ -1266,13 +1262,14 @@
* activity.
* <li>If the current state is NOT_VISIBLE, there is a previously logged state for the
* package UID and there are no other visible activities with the same package UID.
- * <li>The last logged activity with the same package UID is either {@code activity} or the
- * last logged state is NOT_VISIBLE or NOT_LETTERBOXED.
+ * <li>The last logged activity with the same package UID is either {@code activity} (or an
+ * activity that has been removed) or the last logged state is NOT_VISIBLE or NOT_LETTERBOXED.
* </ul>
*
* <p>If the current state is NOT_VISIBLE and the previous state which was logged by {@code
- * activity} wasn't, looks for the first visible activity with the same package UID that has
- * a letterboxed state, or a non-letterboxed state if there isn't one, and logs that state.
+ * activity} (or an activity that has been removed) wasn't, looks for the first visible activity
+ * with the same package UID that has a letterboxed state, or a non-letterboxed state if
+ * there isn't one, and logs that state.
*
* <p>This method assumes that the caller is wrapping the call with a synchronized block so
* that there won't be a race condition between two activities with the same package.
@@ -1308,14 +1305,14 @@
if (!isVisible && !visibleActivities.isEmpty()) {
// There is another visible activity for this package UID.
- if (activity == lastLoggedActivity) {
+ if (lastLoggedActivity == null || activity == lastLoggedActivity) {
// Make sure a new visible state is logged if needed.
findAppCompatStateToLog(compatStateInfo, packageUid);
}
return;
}
- if (activity != lastLoggedActivity
+ if (lastLoggedActivity != null && activity != lastLoggedActivity
&& lastLoggedState != APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE
&& lastLoggedState != APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED) {
// Another visible activity for this package UID has logged a letterboxed state.
@@ -1329,15 +1326,25 @@
* Looks for the first visible activity in {@code compatStateInfo} that has a letterboxed
* state, or a non-letterboxed state if there isn't one, and logs that state for the given
* {@code packageUid}.
+ *
+ * <p>If there is a visible activity in {@code compatStateInfo} with the same state as the
+ * last logged state for the given {@code packageUid}, changes the last logged activity to
+ * reference the first such activity without actually logging the same state twice.
*/
private void findAppCompatStateToLog(PackageCompatStateInfo compatStateInfo, int packageUid) {
final ArrayList<ActivityRecord> visibleActivities = compatStateInfo.mVisibleActivities;
+ final int lastLoggedState = compatStateInfo.mLastLoggedState;
ActivityRecord activityToLog = null;
int stateToLog = APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
for (int i = 0; i < visibleActivities.size(); i++) {
ActivityRecord activity = visibleActivities.get(i);
int state = activity.getAppCompatState();
+ if (state == lastLoggedState) {
+ // Change last logged activity without logging the same state twice.
+ compatStateInfo.mLastLoggedActivity = activity;
+ return;
+ }
if (state == APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE) {
// This shouldn't happen.
Slog.w(TAG, "Visible activity with NOT_VISIBLE App Compat state for package UID: "
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index b6552cb..427bbeb 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -27,6 +27,7 @@
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
import static com.android.server.wm.DisplayRotationProto.FIXED_TO_USER_ROTATION_MODE;
import static com.android.server.wm.DisplayRotationProto.FROZEN_TO_USER_ROTATION;
+import static com.android.server.wm.DisplayRotationProto.IS_FIXED_TO_USER_ROTATION;
import static com.android.server.wm.DisplayRotationProto.LAST_ORIENTATION;
import static com.android.server.wm.DisplayRotationProto.ROTATION;
import static com.android.server.wm.DisplayRotationProto.USER_ROTATION;
@@ -1527,6 +1528,7 @@
proto.write(USER_ROTATION, getUserRotation());
proto.write(FIXED_TO_USER_ROTATION_MODE, mFixedToUserRotation);
proto.write(LAST_ORIENTATION, mLastOrientation);
+ proto.write(IS_FIXED_TO_USER_ROTATION, isFixedToUserRotation());
proto.end(token);
}
diff --git a/services/core/java/com/android/server/wm/PackageConfigPersister.java b/services/core/java/com/android/server/wm/PackageConfigPersister.java
index 5145e8e..3de98f1 100644
--- a/services/core/java/com/android/server/wm/PackageConfigPersister.java
+++ b/services/core/java/com/android/server/wm/PackageConfigPersister.java
@@ -16,10 +16,8 @@
package com.android.server.wm;
-import static android.app.UiModeManager.MODE_NIGHT_AUTO;
-import static android.app.UiModeManager.MODE_NIGHT_CUSTOM;
-
import android.annotation.NonNull;
+import android.content.res.Configuration;
import android.os.Environment;
import android.os.LocaleList;
import android.util.AtomicFile;
@@ -308,7 +306,7 @@
}
boolean isResetNightMode() {
- return mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM;
+ return mNightMode == Configuration.UI_MODE_NIGHT_UNDEFINED;
}
@Override
diff --git a/services/core/java/com/android/server/wm/PinnedTaskController.java b/services/core/java/com/android/server/wm/PinnedTaskController.java
index b4963c5..b54208d 100644
--- a/services/core/java/com/android/server/wm/PinnedTaskController.java
+++ b/services/core/java/com/android/server/wm/PinnedTaskController.java
@@ -17,7 +17,6 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -173,10 +172,8 @@
* to avoid flickering when running PiP animation across different orientations.
*/
void deferOrientationChangeForEnteringPipFromFullScreenIfNeeded() {
- final Task topFullscreenTask = mDisplayContent.getDefaultTaskDisplayArea()
- .getTopRootTaskInWindowingMode(WINDOWING_MODE_FULLSCREEN);
- final ActivityRecord topFullscreen = topFullscreenTask != null
- ? topFullscreenTask.topRunningActivity() : null;
+ final ActivityRecord topFullscreen = mDisplayContent.getActivity(
+ a -> a.fillsParent() && !a.getTask().inMultiWindowMode());
if (topFullscreen == null || topFullscreen.hasFixedRotationTransform()) {
return;
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 4a8f362..35d93f6 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1112,7 +1112,11 @@
if (inMultiWindowMode() || !hasChild()) return false;
if (intent != null) {
final int returnHomeFlags = FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME;
- return intent != null && (intent.getFlags() & returnHomeFlags) == returnHomeFlags;
+ final Task task = getDisplayArea() != null ? getDisplayArea().getRootHomeTask() : null;
+ final boolean isLockTaskModeViolation = task != null
+ && mAtmService.getLockTaskController().isLockTaskModeViolation(task);
+ return (intent.getFlags() & returnHomeFlags) == returnHomeFlags
+ && !isLockTaskModeViolation;
}
final Task bottomTask = getBottomMostTask();
return bottomTask != this && bottomTask.returnsToHomeRootTask();
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index 6bb127a..48a8b1b 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -29,7 +29,10 @@
android_test {
name: "FrameworksMockingServicesTests",
- defaults: ["FrameworkMockingServicesTests-jni-defaults"],
+ defaults: [
+ "FrameworkMockingServicesTests-jni-defaults",
+ "modules-utils-testable-device-config-defaults",
+ ],
srcs: [
"src/**/*.java",
@@ -67,9 +70,7 @@
],
jni_libs: [
- "libdexmakerjvmtiagent",
"libpsi",
- "libstaticjvmtiagent",
],
certificate: "platform",
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 61cd444..a9099ae 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -621,12 +621,8 @@
}
private void setTareEnabled(boolean enabled) {
- doReturn(enabled ? 1 : 0).when(
- () -> Settings.Global.getInt(mContentResolver, Settings.Global.ENABLE_TARE));
- doReturn(enabled ? 1 : 0).when(
- () -> Settings.Global.getInt(mContentResolver,
- Settings.Global.ENABLE_TARE, Settings.Global.DEFAULT_ENABLE_TARE));
- mService.mConstants.onChange(true);
+ when(mEconomyManagerInternal.isEnabled()).thenReturn(enabled);
+ mService.mConstants.onTareEnabledStateChanged(enabled);
}
/**
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
index 609768c..766ba72 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
@@ -38,10 +38,10 @@
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.modules.utils.testing.TestableDeviceConfig;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.appop.AppOpsService;
-import com.android.server.testables.TestableDeviceConfig;
import com.android.server.wm.ActivityTaskManagerService;
import org.junit.Before;
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
index a883293..303f955 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
@@ -40,10 +40,10 @@
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.modules.utils.testing.TestableDeviceConfig;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.appop.AppOpsService;
-import com.android.server.testables.TestableDeviceConfig;
import com.android.server.wm.ActivityTaskManagerService;
import org.junit.After;
diff --git a/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreConfigTest.java b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreConfigTest.java
index ad19a48..d747914c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreConfigTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreConfigTest.java
@@ -29,7 +29,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.server.testables.TestableDeviceConfig;
+import com.android.modules.utils.testing.TestableDeviceConfig;
import org.junit.Before;
import org.junit.Rule;
diff --git a/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java
index 3aeaa42..41e1237 100644
--- a/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java
@@ -259,8 +259,7 @@
mAInfo.flags = 0;
allowPackages(TEST_PACKAGE_NAME);
- // TODO(b/191994709): Fix this assertion once we start checking showWhenLocked
- assertDoesNotIntercept();
+ assertDoesIntercept();
}
@Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java
index 007191f..349da03 100644
--- a/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java
@@ -59,7 +59,7 @@
import com.android.internal.compat.CompatibilityOverrideConfig;
import com.android.internal.compat.CompatibilityOverridesToRemoveConfig;
import com.android.internal.compat.IPlatformCompat;
-import com.android.server.testables.TestableDeviceConfig.TestableDeviceConfigRule;
+import com.android.modules.utils.testing.TestableDeviceConfig.TestableDeviceConfigRule;
import org.junit.Before;
import org.junit.Rule;
diff --git a/services/tests/mockingservicestests/src/com/android/server/power/FaceDownDetectorTest.java b/services/tests/mockingservicestests/src/com/android/server/power/FaceDownDetectorTest.java
index e093f13..9fac0c0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/power/FaceDownDetectorTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/power/FaceDownDetectorTest.java
@@ -34,7 +34,7 @@
import androidx.test.platform.app.InstrumentationRegistry;
-import com.android.server.testables.TestableDeviceConfig;
+import com.android.modules.utils.testing.TestableDeviceConfig;
import org.junit.Before;
import org.junit.ClassRule;
diff --git a/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java b/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java
index f94377f..6a35481 100644
--- a/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java
@@ -36,7 +36,7 @@
import androidx.test.platform.app.InstrumentationRegistry;
-import com.android.server.testables.TestableDeviceConfig;
+import com.android.modules.utils.testing.TestableDeviceConfig;
import org.junit.Before;
import org.junit.ClassRule;
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/MultipleStaticMocksTest.java b/services/tests/mockingservicestests/src/com/android/server/testables/MultipleStaticMocksTest.java
deleted file mode 100644
index c0ab70a..0000000
--- a/services/tests/mockingservicestests/src/com/android/server/testables/MultipleStaticMocksTest.java
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.testables;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.dx.mockito.inline.extended.ExtendedMockito;
-import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class MultipleStaticMocksTest {
- @Rule
- public StaticMockFixtureRule mStaticMockFixtureRule =
- new StaticMockFixtureRule(AB::new, CD::new);
-
- private List<String> mCollected;
-
- @Test
- public void testMultipleStaticMocks() throws Exception {
- mCollected = new ArrayList<>();
- int n = 0;
-
- A.a();
- n = verifyCollected(n, "A.a");
-
- D.b();
- n = verifyCollected(n, "D.b");
-
- C.b();
- n = verifyCollected(n, "C.b");
-
- B.a();
- n = verifyCollected(n, "B.a");
- }
-
- private int verifyCollected(int n, String... last) {
- assertThat(mCollected).hasSize(n + last.length);
- assertThat(mCollected.subList(n, mCollected.size()))
- .containsExactlyElementsIn(last).inOrder();
- return n + last.length;
- }
-
- private static class A {
- /* package */ static void a() {}
- /* package */ static void b() {}
- }
-
- private static class B {
- /* package */ static void a() {}
- /* package */ static void b() {}
- }
-
- private static class C {
- /* package */ static void a() {}
- /* package */ static void b() {}
- }
-
- private static class D {
- /* package */ static void a() {}
- /* package */ static void b() {}
- }
-
- /**
- * AB StaticMockFixture class that handles two mocked classes, {@link A} and {@link B}.
- */
- private class AB implements StaticMockFixture {
- @Override
- public StaticMockitoSessionBuilder setUpMockedClasses(
- StaticMockitoSessionBuilder sessionBuilder) {
- sessionBuilder.spyStatic(A.class);
- sessionBuilder.spyStatic(B.class);
- return sessionBuilder;
- }
-
- @Override
- public void setUpMockBehaviors() {
- ExtendedMockito.doAnswer(invocation -> {
- mCollected.add("A.a");
- return null;
- }).when(A::a);
- ExtendedMockito.doAnswer(invocation -> {
- mCollected.add("A.b");
- return null;
- }).when(A::b);
- ExtendedMockito.doAnswer(invocation -> {
- mCollected.add("B.a");
- return null;
- }).when(B::a);
- ExtendedMockito.doAnswer(invocation -> {
- mCollected.add("B.b");
- return null;
- }).when(B::b);
- }
-
- @Override
- public void tearDown() {
-
- }
- }
-
- /**
- * AB StaticMockFixture class that handles two mocked classes, {@link C} and {@link D}.
- */
- private class CD implements StaticMockFixture {
- @Override
- public StaticMockitoSessionBuilder setUpMockedClasses(
- StaticMockitoSessionBuilder sessionBuilder) {
- sessionBuilder.spyStatic(C.class);
- sessionBuilder.spyStatic(D.class);
- return sessionBuilder;
- }
-
- @Override
- public void setUpMockBehaviors() {
- ExtendedMockito.doAnswer(invocation -> {
- mCollected.add("C.a");
- return null;
- }).when(C::a);
- ExtendedMockito.doAnswer(invocation -> {
- mCollected.add("C.b");
- return null;
- }).when(C::b);
- ExtendedMockito.doAnswer(invocation -> {
- mCollected.add("D.a");
- return null;
- }).when(D::a);
- ExtendedMockito.doAnswer(invocation -> {
- mCollected.add("D.b");
- return null;
- }).when(D::b);
- }
-
- @Override
- public void tearDown() {
-
- }
- }
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixture.java b/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixture.java
deleted file mode 100644
index 0303fe1..0000000
--- a/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixture.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.testables;
-
-
-import com.android.dx.mockito.inline.extended.StaticMockitoSession;
-import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
-
-/**
- * Provides support for a set of static mocks for use within a single shared
- * {@link StaticMockitoSession}.
- */
-public interface StaticMockFixture {
- /**
- * Adds any required mock or spy classes managed by this {@link StaticMockFixture} to the
- * {@link StaticMockitoSessionBuilder} provided.
- *
- * Call this to set up the classes that this expects to be mocked, by adding them to the
- * {@link StaticMockitoSessionBuilder} using
- * {@link StaticMockitoSessionBuilder#mockStatic(Class)},
- * {@link StaticMockitoSessionBuilder#spyStatic(Class)} or similar as appropriate.
- *
- * @param sessionBuilder the {@link StaticMockitoSessionBuilder} to which the classes should be
- * added to mock, spy, or otherwise as required
- * @return sessionBuilder, to allow for fluent programming
- */
- StaticMockitoSessionBuilder setUpMockedClasses(StaticMockitoSessionBuilder sessionBuilder);
-
- /**
- * Configures the behaviours of any mock or spy classes managed by this
- * {@link StaticMockFixture}.
- *
- * Call this after {@link StaticMockitoSessionBuilder#startMocking()} has been called.
- * This sets up any default behaviors for the mocks, spys, etc.
- */
- void setUpMockBehaviors();
-
- /**
- * Tear everything down.
- */
- void tearDown();
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRule.java b/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRule.java
deleted file mode 100644
index 3566aee..0000000
--- a/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRule.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.testables;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-
-import com.android.dx.mockito.inline.extended.StaticMockitoSession;
-import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
-
-import org.junit.AssumptionViolatedException;
-import org.junit.rules.TestRule;
-import org.junit.rules.TestWatcher;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-import org.mockito.quality.Strictness;
-
-import java.util.function.Supplier;
-
-/**
- * <p>StaticMockFixtureRule is a {@link TestRule} that wraps one or more {@link StaticMockFixture}s
- * to set them up and tear it down automatically. This works well when you have no other static
- * mocks than the ones supported by their respective {@link StaticMockFixture}s.</p>
- *
- * <p>StaticMockFixtureRule should be defined as a rule on your test so it can clean up after
- * itself. Like the following:</p>
- * <pre class="prettyprint">
-* public final StaticMockFixture mStaticMockFixtures = ...;
- * @Rule
- * public final StaticMockFixtureRule mStaticMockFixtureRule =
- * new StaticMockFixtureRule(mStaticMockFixtures);
- * </pre>
- */
-public class StaticMockFixtureRule implements TestRule {
- private StaticMockitoSession mMockitoSession;
- private StaticMockFixture[] mStaticMockFixtures;
- private Supplier<? extends StaticMockFixture>[] mSupplier;
-
- /**
- * Constructs a StaticMockFixtureRule that always uses the same {@link StaticMockFixture}
- * instance(s).
- *
- * @param staticMockFixtures the {@link StaticMockFixture}(s) to use.
- */
- public StaticMockFixtureRule(StaticMockFixture... staticMockFixtures) {
- mStaticMockFixtures = staticMockFixtures;
- mSupplier = null;
- }
-
- /**
- * Constructs a StaticMockFixtureRule that retrieves a new {@link StaticMockFixture} instance
- * from one or more {@link Supplier<? extends StaticMockFixture >}s for each test invocation.
- *
- * @param supplier the {@link Supplier<? extends StaticMockFixture >}(s) that will supply the
- * {@link StaticMockFixture}(s).
- */
- @SafeVarargs
- public StaticMockFixtureRule(Supplier<? extends StaticMockFixture>... supplier) {
- mStaticMockFixtures = null;
- mSupplier = supplier;
- }
-
- @Override
- public Statement apply(Statement base, Description description) {
- StaticMockitoSessionBuilder sessionBuilder = getSessionBuilder();
-
- if (mSupplier != null) {
- mStaticMockFixtures = new StaticMockFixture[mSupplier.length];
- for (int i = 0; i < mSupplier.length; i++) {
- mStaticMockFixtures[i] = mSupplier[i].get();
- }
- }
-
- for (int i = 0; i < mStaticMockFixtures.length; i++) {
- sessionBuilder = mStaticMockFixtures[i].setUpMockedClasses(sessionBuilder);
- }
-
- mMockitoSession = sessionBuilder.startMocking();
-
- for (int i = 0; i < mStaticMockFixtures.length; i++) {
- mStaticMockFixtures[i].setUpMockBehaviors();
- }
-
- return new TestWatcher() {
- @Override
- protected void succeeded(Description description) {
- tearDown(null);
- }
-
- @Override
- protected void skipped(AssumptionViolatedException e, Description description) {
- tearDown(e);
- }
-
- @Override
- protected void failed(Throwable e, Description description) {
- tearDown(e);
- }
- }.apply(base, description);
- }
-
- /**
- * This allows overriding the creation of the builder for a new {@link StaticMockitoSession}.
- * Mainly for testing, but also useful if you have other requirements for the session.
- *
- * @return a new {@link StaticMockitoSessionBuilder}.
- */
- public StaticMockitoSessionBuilder getSessionBuilder() {
- return mockitoSession().strictness(Strictness.LENIENT);
- }
-
- private void tearDown(Throwable e) {
- mMockitoSession.finishMocking(e);
-
- for (int i = mStaticMockFixtures.length - 1; i >= 0; i--) {
- mStaticMockFixtures[i].tearDown();
- if (mSupplier != null) {
- mStaticMockFixtures[i] = null;
- }
- }
-
- if (mSupplier != null) {
- mStaticMockFixtures = null;
- }
-
- mMockitoSession = null;
- }
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRuleTest.java b/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRuleTest.java
deleted file mode 100644
index 8e0ccf0..0000000
--- a/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRuleTest.java
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.testables;
-
-import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-import static org.mockito.quality.Strictness.LENIENT;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.dx.mockito.inline.extended.StaticMockitoSession;
-import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
-
-import org.junit.After;
-import org.junit.AssumptionViolatedException;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.Description;
-import org.junit.runner.RunWith;
-import org.junit.runners.model.Statement;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoSession;
-
-import java.util.function.Supplier;
-
-/** Tests that StaticMockFixture manages fixtures and suppliers correctly. */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class StaticMockFixtureRuleTest {
- private MockitoSession mMockitoSession;
-
- @Mock private StaticMockitoSessionBuilder mSessionBuilder;
- @Mock private StaticMockitoSession mSession;
- @Mock private StaticMockFixture mA1;
- @Mock private StaticMockFixture mB1;
- @Mock private StaticMockFixture mA2;
- @Mock private StaticMockFixture mB2;
- @Mock private Supplier<StaticMockFixture> mSupplyA;
- @Mock private Supplier<StaticMockFixture> mSupplyB;
- @Mock private Statement mStatement;
- @Mock private Statement mSkipStatement;
- @Mock private Statement mThrowStatement;
- @Mock private Description mDescription;
-
- @Before
- public void setUp() throws Throwable {
- mMockitoSession = Mockito.mockitoSession()
- .strictness(LENIENT)
- .initMocks(this)
- .startMocking();
- prepareMockBehaviours();
- }
-
- @After
- public void tearDown() {
- mMockitoSession.finishMocking();
- }
-
- private void prepareFixtureMocks(StaticMockFixture... mocks) {
- for (StaticMockFixture mock : mocks) {
- when(mock.setUpMockedClasses(any())).thenAnswer(
- invocation -> invocation.getArgument(0));
- doNothing().when(mock).setUpMockBehaviors();
- }
- }
-
- private void prepareMockBehaviours() throws Throwable {
- when(mSessionBuilder.startMocking()).thenReturn(mSession);
- when(mSupplyA.get()).thenReturn(mA1, mA2);
- when(mSupplyB.get()).thenReturn(mB1, mB2);
- prepareFixtureMocks(mA1, mA2, mB1, mB2);
- when(mA1.setUpMockedClasses(any())).thenAnswer(invocation -> invocation.getArgument(0));
- doNothing().when(mA1).setUpMockBehaviors();
- when(mB1.setUpMockedClasses(any())).thenAnswer(invocation -> invocation.getArgument(0));
- doNothing().when(mB1).setUpMockBehaviors();
- doNothing().when(mStatement).evaluate();
- doThrow(new AssumptionViolatedException("bad assumption, test should be skipped"))
- .when(mSkipStatement).evaluate();
- doThrow(new IllegalArgumentException("bad argument, test should be failed"))
- .when(mThrowStatement).evaluate();
- doNothing().when(mA1).tearDown();
- doNothing().when(mB1).tearDown();
- }
-
- private InOrder mocksInOrder() {
- return inOrder(mSessionBuilder, mSession, mSupplyA, mSupplyB, mA1, mA2, mB1, mB2,
- mStatement, mSkipStatement, mThrowStatement, mDescription);
- }
-
- private void verifyNoMoreImportantMockInteractions() {
- verifyNoMoreInteractions(mSupplyA, mSupplyB, mA1, mA2, mB1, mB2, mStatement,
- mSkipStatement, mThrowStatement);
- }
-
- @Test
- public void testRuleWorksWithExplicitFixtures() throws Throwable {
- InOrder inOrder = mocksInOrder();
-
- StaticMockFixtureRule rule = new StaticMockFixtureRule(mA1, mB1) {
- @Override public StaticMockitoSessionBuilder getSessionBuilder() {
- return mSessionBuilder;
- }
- };
- Statement runMe = rule.apply(mStatement, mDescription);
-
- inOrder.verify(mA1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
- inOrder.verify(mB1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
- inOrder.verify(mA1).setUpMockBehaviors();
- inOrder.verify(mB1).setUpMockBehaviors();
-
- runMe.evaluate();
-
- inOrder.verify(mStatement).evaluate();
- // note: tearDown in reverse order
- inOrder.verify(mB1).tearDown();
- inOrder.verify(mA1).tearDown();
-
- // Round two: use the same fixtures again.
- rule.apply(mStatement, mDescription).evaluate();
-
- inOrder.verify(mA1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
- inOrder.verify(mB1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
- inOrder.verify(mA1).setUpMockBehaviors();
- inOrder.verify(mB1).setUpMockBehaviors();
- inOrder.verify(mStatement).evaluate();
- // note: tearDown in reverse order
- inOrder.verify(mB1).tearDown();
- inOrder.verify(mA1).tearDown();
-
- verifyNoMoreImportantMockInteractions();
- }
-
- @Test
- public void testRuleWorksWithFixtureSuppliers() throws Throwable {
- InOrder inOrder = mocksInOrder();
-
- StaticMockFixtureRule rule = new StaticMockFixtureRule(mSupplyA, mSupplyB) {
- @Override public StaticMockitoSessionBuilder getSessionBuilder() {
- return mSessionBuilder;
- }
- };
- Statement runMe = rule.apply(mStatement, mDescription);
-
- inOrder.verify(mSupplyA).get();
- inOrder.verify(mSupplyB).get();
- inOrder.verify(mA1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
- inOrder.verify(mB1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
- inOrder.verify(mA1).setUpMockBehaviors();
- inOrder.verify(mB1).setUpMockBehaviors();
-
- runMe.evaluate();
-
- inOrder.verify(mStatement).evaluate();
- // note: tearDown in reverse order
- inOrder.verify(mB1).tearDown();
- inOrder.verify(mA1).tearDown();
-
- // Round two: use the same suppliers again to retrieve different fixtures: mA2 and mB2
- rule.apply(mStatement, mDescription).evaluate();
-
- inOrder.verify(mSupplyA).get();
- inOrder.verify(mSupplyB).get();
- inOrder.verify(mA2).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
- inOrder.verify(mB2).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
- inOrder.verify(mA2).setUpMockBehaviors();
- inOrder.verify(mB2).setUpMockBehaviors();
- inOrder.verify(mStatement).evaluate();
- // note: tearDown in reverse order
- inOrder.verify(mB2).tearDown();
- inOrder.verify(mA2).tearDown();
-
- verifyNoMoreImportantMockInteractions();
- }
-
- @Test
- public void testTearDownOnSkippedTests() throws Throwable {
- InOrder inOrder = mocksInOrder();
-
- StaticMockFixtureRule rule = new StaticMockFixtureRule(mA1, mB1) {
- @Override public StaticMockitoSessionBuilder getSessionBuilder() {
- return mSessionBuilder;
- }
- };
- Statement skipStatement = rule.apply(mSkipStatement, mDescription);
-
- inOrder.verify(mA1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
- inOrder.verify(mB1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
- inOrder.verify(mA1).setUpMockBehaviors();
- inOrder.verify(mB1).setUpMockBehaviors();
-
- try {
- skipStatement.evaluate();
- fail("AssumptionViolatedException should have been thrown");
- } catch (AssumptionViolatedException e) {
- // expected
- }
-
- inOrder.verify(mSkipStatement).evaluate();
- // note: tearDown in reverse order
- inOrder.verify(mB1).tearDown();
- inOrder.verify(mA1).tearDown();
-
- verifyNoMoreImportantMockInteractions();
- }
-
- @Test
- public void testTearDownOnFailedTests() throws Throwable {
- InOrder inOrder = mocksInOrder();
-
- StaticMockFixtureRule rule = new StaticMockFixtureRule(mA1, mB1) {
- @Override public StaticMockitoSessionBuilder getSessionBuilder() {
- return mSessionBuilder;
- }
- };
- Statement failStatement = rule.apply(mThrowStatement, mDescription);
-
- inOrder.verify(mA1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
- inOrder.verify(mB1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
- inOrder.verify(mA1).setUpMockBehaviors();
- inOrder.verify(mB1).setUpMockBehaviors();
-
- try {
- failStatement.evaluate();
- fail("IllegalArgumentException should have been thrown");
- } catch (IllegalArgumentException e) {
- // expected
- }
-
- inOrder.verify(mThrowStatement).evaluate();
- // note: tearDown in reverse order
- inOrder.verify(mB1).tearDown();
- inOrder.verify(mA1).tearDown();
-
- verifyNoMoreImportantMockInteractions();
- }
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java
deleted file mode 100644
index 60a7f78..0000000
--- a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.testables;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyFloat;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Mockito.when;
-
-import android.provider.DeviceConfig;
-import android.provider.DeviceConfig.Properties;
-import android.util.ArrayMap;
-import android.util.Pair;
-
-import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
-
-import org.junit.rules.TestRule;
-import org.mockito.ArgumentMatchers;
-import org.mockito.Mockito;
-import org.mockito.stubbing.Answer;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.Executor;
-
-/**
- * TestableDeviceConfig is a {@link StaticMockFixture} that uses ExtendedMockito to replace the real
- * implementation of DeviceConfig with essentially a local HashMap in the callers process. This
- * allows for unit testing that do not modify the real DeviceConfig on the device at all.
- */
-public final class TestableDeviceConfig implements StaticMockFixture {
-
- private Map<DeviceConfig.OnPropertiesChangedListener, Pair<String, Executor>>
- mOnPropertiesChangedListenerMap = new HashMap<>();
- private Map<String, String> mKeyValueMap = new ConcurrentHashMap<>();
-
- /**
- * Clears out all local overrides.
- */
- public void clearDeviceConfig() {
- mKeyValueMap.clear();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public StaticMockitoSessionBuilder setUpMockedClasses(
- StaticMockitoSessionBuilder sessionBuilder) {
- sessionBuilder.spyStatic(DeviceConfig.class);
- return sessionBuilder;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void setUpMockBehaviors() {
- doAnswer((Answer<Void>) invocationOnMock -> {
- String namespace = invocationOnMock.getArgument(0);
- Executor executor = invocationOnMock.getArgument(1);
- DeviceConfig.OnPropertiesChangedListener onPropertiesChangedListener =
- invocationOnMock.getArgument(2);
- mOnPropertiesChangedListenerMap.put(
- onPropertiesChangedListener, new Pair<>(namespace, executor));
- return null;
- }).when(() -> DeviceConfig.addOnPropertiesChangedListener(
- anyString(), any(Executor.class),
- any(DeviceConfig.OnPropertiesChangedListener.class)));
-
- doAnswer((Answer<Boolean>) invocationOnMock -> {
- String namespace = invocationOnMock.getArgument(0);
- String name = invocationOnMock.getArgument(1);
- String value = invocationOnMock.getArgument(2);
- mKeyValueMap.put(getKey(namespace, name), value);
- invokeListeners(namespace, getProperties(namespace, name, value));
- return true;
- }
- ).when(() -> DeviceConfig.setProperty(anyString(), anyString(), anyString(), anyBoolean()));
-
- doAnswer((Answer<Boolean>) invocationOnMock -> {
- String namespace = invocationOnMock.getArgument(0);
- String name = invocationOnMock.getArgument(1);
- mKeyValueMap.remove(getKey(namespace, name));
- invokeListeners(namespace, getProperties(namespace, name, null));
- return true;
- }
- ).when(() -> DeviceConfig.deleteProperty(anyString(), anyString()));
-
- doAnswer((Answer<Boolean>) invocationOnMock -> {
- Properties properties = invocationOnMock.getArgument(0);
- String namespace = properties.getNamespace();
- Map<String, String> keyValues = new ArrayMap<>();
- for (String name : properties.getKeyset()) {
- String value = properties.getString(name, /* defaultValue= */ "");
- mKeyValueMap.put(getKey(namespace, name), value);
- keyValues.put(name.toLowerCase(), value);
- }
- invokeListeners(namespace, getProperties(namespace, keyValues));
- return true;
- }
- ).when(() -> DeviceConfig.setProperties(any(Properties.class)));
-
- doAnswer((Answer<String>) invocationOnMock -> {
- String namespace = invocationOnMock.getArgument(0);
- String name = invocationOnMock.getArgument(1);
- return mKeyValueMap.get(getKey(namespace, name));
- }).when(() -> DeviceConfig.getProperty(anyString(), anyString()));
-
- doAnswer((Answer<Properties>) invocationOnMock -> {
- String namespace = invocationOnMock.getArgument(0);
- final int varargStartIdx = 1;
- Map<String, String> keyValues = new ArrayMap<>();
- if (invocationOnMock.getArguments().length == varargStartIdx) {
- mKeyValueMap.entrySet().forEach(entry -> {
- Pair<String, String> nameSpaceAndName = getNameSpaceAndName(entry.getKey());
- if (!nameSpaceAndName.first.equals(namespace)) {
- return;
- }
- keyValues.put(nameSpaceAndName.second.toLowerCase(), entry.getValue());
- });
- } else {
- for (int i = varargStartIdx; i < invocationOnMock.getArguments().length; ++i) {
- String name = invocationOnMock.getArgument(i);
- keyValues.put(name.toLowerCase(), mKeyValueMap.get(getKey(namespace, name)));
- }
- }
- return getProperties(namespace, keyValues);
- }).when(() -> DeviceConfig.getProperties(anyString(), ArgumentMatchers.<String>any()));
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void tearDown() {
- clearDeviceConfig();
- mOnPropertiesChangedListenerMap.clear();
- }
-
- private static String getKey(String namespace, String name) {
- return namespace + "/" + name;
- }
-
- private Pair<String, String> getNameSpaceAndName(String key) {
- final String[] values = key.split("/");
- return Pair.create(values[0], values[1]);
- }
-
- private void invokeListeners(String namespace, Properties properties) {
- for (DeviceConfig.OnPropertiesChangedListener listener :
- mOnPropertiesChangedListenerMap.keySet()) {
- if (namespace.equals(mOnPropertiesChangedListenerMap.get(listener).first)) {
- mOnPropertiesChangedListenerMap.get(listener).second.execute(
- () -> listener.onPropertiesChanged(properties));
- }
- }
- }
-
- private Properties getProperties(String namespace, String name, String value) {
- return getProperties(namespace, Collections.singletonMap(name.toLowerCase(), value));
- }
-
- private Properties getProperties(String namespace, Map<String, String> keyValues) {
- Properties properties = Mockito.mock(Properties.class);
- when(properties.getNamespace()).thenReturn(namespace);
- when(properties.getKeyset()).thenReturn(keyValues.keySet());
- when(properties.getBoolean(anyString(), anyBoolean())).thenAnswer(
- invocation -> {
- String key = invocation.getArgument(0);
- boolean defaultValue = invocation.getArgument(1);
- final String value = keyValues.get(key.toLowerCase());
- if (value != null) {
- return Boolean.parseBoolean(value);
- } else {
- return defaultValue;
- }
- }
- );
- when(properties.getFloat(anyString(), anyFloat())).thenAnswer(
- invocation -> {
- String key = invocation.getArgument(0);
- float defaultValue = invocation.getArgument(1);
- final String value = keyValues.get(key.toLowerCase());
- if (value != null) {
- try {
- return Float.parseFloat(value);
- } catch (NumberFormatException e) {
- return defaultValue;
- }
- } else {
- return defaultValue;
- }
- }
- );
- when(properties.getInt(anyString(), anyInt())).thenAnswer(
- invocation -> {
- String key = invocation.getArgument(0);
- int defaultValue = invocation.getArgument(1);
- final String value = keyValues.get(key.toLowerCase());
- if (value != null) {
- try {
- return Integer.parseInt(value);
- } catch (NumberFormatException e) {
- return defaultValue;
- }
- } else {
- return defaultValue;
- }
- }
- );
- when(properties.getLong(anyString(), anyLong())).thenAnswer(
- invocation -> {
- String key = invocation.getArgument(0);
- long defaultValue = invocation.getArgument(1);
- final String value = keyValues.get(key.toLowerCase());
- if (value != null) {
- try {
- return Long.parseLong(value);
- } catch (NumberFormatException e) {
- return defaultValue;
- }
- } else {
- return defaultValue;
- }
- }
- );
- when(properties.getString(anyString(), nullable(String.class))).thenAnswer(
- invocation -> {
- String key = invocation.getArgument(0);
- String defaultValue = invocation.getArgument(1);
- final String value = keyValues.get(key.toLowerCase());
- if (value != null) {
- return value;
- } else {
- return defaultValue;
- }
- }
- );
-
- return properties;
- }
-
- /**
- * <p>TestableDeviceConfigRule is a {@link TestRule} that wraps a {@link TestableDeviceConfig}
- * to set it up and tear it down automatically. This works well when you have no other static
- * mocks.</p>
- *
- * <p>TestableDeviceConfigRule should be defined as a rule on your test so it can clean up after
- * itself. Like the following:</p>
- * <pre class="prettyprint">
- * @Rule
- * public final TestableDeviceConfigRule mTestableDeviceConfigRule =
- * new TestableDeviceConfigRule();
- * </pre>
- */
- public static class TestableDeviceConfigRule extends StaticMockFixtureRule {
- public TestableDeviceConfigRule() {
- super(TestableDeviceConfig::new);
- }
- }
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigAndOtherStaticMocksTest.java b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigAndOtherStaticMocksTest.java
deleted file mode 100644
index 9616d13..0000000
--- a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigAndOtherStaticMocksTest.java
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.testables;
-
-import android.provider.DeviceConfig;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.dx.mockito.inline.extended.ExtendedMockito;
-import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
-
-import org.junit.Assert;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class TestableDeviceConfigAndOtherStaticMocksTest {
- @Rule
- public StaticMockFixtureRule mStaticMockFixtureRule =
- new StaticMockFixtureRule(TestableDeviceConfig::new, AB::new, CD::new);
-
- private List<String> mCollected;
-
- @Test
- public void testDeviceConfigAndOtherStaticMocks() throws Exception {
- mCollected = new ArrayList<>();
- int n = 0;
-
- String namespace = "foo";
- String flag = "bar";
- String flagValue = "new value";
-
- Assert.assertNull(DeviceConfig.getProperty(namespace, flag));
-
- A.a();
- verifyCollected(++n, "A.a");
-
- DeviceConfig.setProperty(namespace, flag, flagValue, false);
-
- D.b();
- verifyCollected(++n, "D.b");
-
- Assert.assertEquals(flagValue, DeviceConfig.getProperty(namespace, flag));
-
- C.b();
- verifyCollected(++n, "C.b");
-
- B.a();
- verifyCollected(++n, "B.a");
- }
-
- private void verifyCollected(int n, String last) {
- Assert.assertEquals(n, mCollected.size());
- Assert.assertEquals(last, mCollected.get(n - 1));
- }
-
- private static class A {
- /* package */ static void a() {}
- /* package */ static void b() {}
- }
-
- private static class B {
- /* package */ static void a() {}
- /* package */ static void b() {}
- }
-
- private static class C {
- /* package */ static void a() {}
- /* package */ static void b() {}
- }
-
- private static class D {
- /* package */ static void a() {}
- /* package */ static void b() {}
- }
-
- /**
- * AB StaticMockFixture class that handles two mocked classes, {@link A} and {@link B}.
- */
- private class AB implements StaticMockFixture {
- @Override
- public StaticMockitoSessionBuilder setUpMockedClasses(
- StaticMockitoSessionBuilder sessionBuilder) {
- sessionBuilder.spyStatic(A.class);
- sessionBuilder.spyStatic(B.class);
- return sessionBuilder;
- }
-
- @Override
- public void setUpMockBehaviors() {
- ExtendedMockito.doAnswer(invocation -> {
- mCollected.add("A.a");
- return null;
- }).when(A::a);
- ExtendedMockito.doAnswer(invocation -> {
- mCollected.add("A.b");
- return null;
- }).when(A::b);
- ExtendedMockito.doAnswer(invocation -> {
- mCollected.add("B.a");
- return null;
- }).when(B::a);
- ExtendedMockito.doAnswer(invocation -> {
- mCollected.add("B.b");
- return null;
- }).when(B::b);
- }
-
- @Override
- public void tearDown() {
-
- }
- }
-
- /**
- * AB StaticMockFixture class that handles two mocked classes, {@link C} and {@link D}.
- */
- private class CD implements StaticMockFixture {
- @Override
- public StaticMockitoSessionBuilder setUpMockedClasses(
- StaticMockitoSessionBuilder sessionBuilder) {
- sessionBuilder.spyStatic(C.class);
- sessionBuilder.spyStatic(D.class);
- return sessionBuilder;
- }
-
- @Override
- public void setUpMockBehaviors() {
- ExtendedMockito.doAnswer(invocation -> {
- mCollected.add("C.a");
- return null;
- }).when(C::a);
- ExtendedMockito.doAnswer(invocation -> {
- mCollected.add("C.b");
- return null;
- }).when(C::b);
- ExtendedMockito.doAnswer(invocation -> {
- mCollected.add("D.a");
- return null;
- }).when(D::a);
- ExtendedMockito.doAnswer(invocation -> {
- mCollected.add("D.b");
- return null;
- }).when(D::b);
- }
-
- @Override
- public void tearDown() {
-
- }
- }
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java
deleted file mode 100644
index f9f4387..0000000
--- a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.testables;
-
-import static android.provider.DeviceConfig.OnPropertiesChangedListener;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.app.ActivityThread;
-import android.platform.test.annotations.Presubmit;
-import android.provider.DeviceConfig;
-import android.provider.DeviceConfig.BadConfigException;
-import android.provider.DeviceConfig.Properties;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/** Tests that ensure appropriate settings are backed up. */
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class TestableDeviceConfigTest {
- private static final String sNamespace = "namespace1";
- private static final String sKey = "key1";
- private static final String sValue = "value1";
- private static final long WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS = 2000; // 2 sec
-
- @Rule
- public TestableDeviceConfig.TestableDeviceConfigRule
- mTestableDeviceConfig = new TestableDeviceConfig.TestableDeviceConfigRule();
-
- @Test
- public void getProperty_empty() {
- String result = DeviceConfig.getProperty(sNamespace, sKey);
- assertThat(result).isNull();
- }
-
- @Test
- public void setAndGetProperty_sameNamespace() {
- DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
- String result = DeviceConfig.getProperty(sNamespace, sKey);
- assertThat(result).isEqualTo(sValue);
- }
-
- @Test
- public void setAndGetProperty_differentNamespace() {
- String newNamespace = "namespace2";
- DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
- String result = DeviceConfig.getProperty(newNamespace, sKey);
- assertThat(result).isNull();
- }
-
- @Test
- public void setAndGetProperty_multipleNamespaces() {
- String newNamespace = "namespace2";
- String newValue = "value2";
- DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
- DeviceConfig.setProperty(newNamespace, sKey, newValue, false);
- String result = DeviceConfig.getProperty(sNamespace, sKey);
- assertThat(result).isEqualTo(sValue);
- result = DeviceConfig.getProperty(newNamespace, sKey);
- assertThat(result).isEqualTo(newValue);
- }
-
- @Test
- public void setAndGetProperty_overrideValue() {
- String newValue = "value2";
- DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
- DeviceConfig.setProperty(sNamespace, sKey, newValue, false);
- String result = DeviceConfig.getProperty(sNamespace, sKey);
- assertThat(result).isEqualTo(newValue);
- }
-
- @Test
- public void setProperties() throws BadConfigException {
- String newKey = "key2";
- String newValue = "value2";
- DeviceConfig.setProperties(new Properties.Builder(sNamespace).setString(sKey,
- sValue).setString(newKey, newValue).build());
- assertThat(DeviceConfig.getProperty(sNamespace, sKey)).isEqualTo(sValue);
- assertThat(DeviceConfig.getProperty(sNamespace, newKey)).isEqualTo(newValue);
- }
-
- @Test
- public void deleteProperty() {
- DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
- assertThat(DeviceConfig.getProperty(sNamespace, sKey)).isEqualTo(sValue);
- DeviceConfig.deleteProperty(sNamespace, sKey);
- assertThat(DeviceConfig.getProperty(sNamespace, sKey)).isNull();
- String newNamespace = "namespace2";
- String newValue = "value2";
- DeviceConfig.setProperty(newNamespace, sKey, newValue, false);
- assertThat(DeviceConfig.getProperty(newNamespace, sKey)).isEqualTo(newValue);
- DeviceConfig.deleteProperty(newNamespace, sKey);
- assertThat(DeviceConfig.getProperty(newNamespace, sKey)).isNull();
- }
-
- @Test
- public void getProperties_empty() {
- String newKey = "key2";
- String newValue = "value2";
- DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
- Properties properties = DeviceConfig.getProperties(sNamespace);
- assertThat(properties.getString(sKey, null)).isEqualTo(sValue);
- assertThat(properties.getString(newKey, null)).isNull();
-
- DeviceConfig.setProperty(sNamespace, newKey, newValue, false);
- properties = DeviceConfig.getProperties(sNamespace);
- assertThat(properties.getString(sKey, null)).isEqualTo(sValue);
- assertThat(properties.getString(newKey, null)).isEqualTo(newValue);
-
- }
-
- @Test
- public void getProperties() {
- Properties properties = DeviceConfig.getProperties(sNamespace, sKey);
- assertThat(properties.getString(sKey, null)).isNull();
-
- DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
- properties = DeviceConfig.getProperties(sNamespace, sKey);
- assertThat(properties.getString(sKey, null)).isEqualTo(sValue);
-
- String newKey = "key2";
- String newValue = "value2";
- DeviceConfig.setProperty(sNamespace, newKey, newValue, false);
- properties = DeviceConfig.getProperties(sNamespace, sKey, newKey);
- assertThat(properties.getString(sKey, null)).isEqualTo(sValue);
- assertThat(properties.getString(newKey, null)).isEqualTo(newValue);
-
- String unsetKey = "key3";
- properties = DeviceConfig.getProperties(sNamespace, newKey, unsetKey);
- assertThat(properties.getKeyset()).containsExactly(newKey, unsetKey);
- assertThat(properties.getString(newKey, null)).isEqualTo(newValue);
- assertThat(properties.getString(unsetKey, null)).isNull();
- }
-
- @Test
- public void testListener_setProperty() throws InterruptedException {
- CountDownLatch countDownLatch = new CountDownLatch(1);
-
- OnPropertiesChangedListener changeListener = (properties) -> {
- assertThat(properties.getNamespace()).isEqualTo(sNamespace);
- assertThat(properties.getKeyset()).containsExactly(sKey);
- assertThat(properties.getString(sKey, "bogus_value")).isEqualTo(sValue);
- assertThat(properties.getString("bogus_key", "bogus_value")).isEqualTo("bogus_value");
- countDownLatch.countDown();
- };
- try {
- DeviceConfig.addOnPropertiesChangedListener(sNamespace,
- ActivityThread.currentApplication().getMainExecutor(), changeListener);
- DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
- assertThat(countDownLatch.await(
- WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
- } finally {
- DeviceConfig.removeOnPropertiesChangedListener(changeListener);
- }
- }
-
- @Test
- public void testListener_setProperties() throws BadConfigException, InterruptedException {
- CountDownLatch countDownLatch = new CountDownLatch(1);
- String newKey = "key2";
- String newValue = "value2";
-
- OnPropertiesChangedListener changeListener = (properties) -> {
- assertThat(properties.getNamespace()).isEqualTo(sNamespace);
- assertThat(properties.getKeyset()).containsExactly(sKey, newKey);
- assertThat(properties.getString(sKey, "bogus_value")).isEqualTo(sValue);
- assertThat(properties.getString(newKey, "bogus_value")).isEqualTo(newValue);
- assertThat(properties.getString("bogus_key", "bogus_value")).isEqualTo("bogus_value");
- countDownLatch.countDown();
- };
- try {
- DeviceConfig.addOnPropertiesChangedListener(sNamespace,
- ActivityThread.currentApplication().getMainExecutor(), changeListener);
- DeviceConfig.setProperties(new Properties.Builder(sNamespace).setString(sKey,
- sValue).setString(newKey, newValue).build());
- assertThat(countDownLatch.await(
- WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
- } finally {
- DeviceConfig.removeOnPropertiesChangedListener(changeListener);
- }
- }
-
- @Test
- public void testListener_deleteProperty() throws InterruptedException {
- CountDownLatch countDownLatch = new CountDownLatch(1);
-
- OnPropertiesChangedListener changeListener = (properties) -> {
- assertThat(properties.getNamespace()).isEqualTo(sNamespace);
- assertThat(properties.getKeyset()).containsExactly(sKey);
- assertThat(properties.getString(sKey, "bogus_value")).isEqualTo("bogus_value");
- assertThat(properties.getString("bogus_key", "bogus_value")).isEqualTo("bogus_value");
- countDownLatch.countDown();
- };
- try {
- DeviceConfig.addOnPropertiesChangedListener(sNamespace,
- ActivityThread.currentApplication().getMainExecutor(), changeListener);
- DeviceConfig.deleteProperty(sNamespace, sKey);
- assertThat(countDownLatch.await(
- WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
- } finally {
- DeviceConfig.removeOnPropertiesChangedListener(changeListener);
- }
- }
-}
-
-
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
index da881c4..15ba358 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -19,6 +19,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -169,13 +170,14 @@
}
@Test
- public void enable_hasConnection_enableWindowMagnification() throws RemoteException {
+ public void enableWithAnimation_hasConnection_enableWindowMagnification()
+ throws RemoteException {
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f);
- verify(mMockConnection.getConnection()).enableWindowMagnification(TEST_DISPLAY, 2f,
- 200f, 300f, null);
+ verify(mMockConnection.getConnection()).enableWindowMagnification(eq(TEST_DISPLAY), eq(2f),
+ eq(200f), eq(300f), notNull());
}
@Test
@@ -199,7 +201,8 @@
mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
- verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY, null);
+ verify(mMockConnection.getConnection()).disableWindowMagnification(eq(TEST_DISPLAY),
+ notNull());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/criticalevents/CriticalEventLogTest.java b/services/tests/servicestests/src/com/android/server/criticalevents/CriticalEventLogTest.java
index 54e75aa..bf7c587 100644
--- a/services/tests/servicestests/src/com/android/server/criticalevents/CriticalEventLogTest.java
+++ b/services/tests/servicestests/src/com/android/server/criticalevents/CriticalEventLogTest.java
@@ -29,6 +29,8 @@
import com.android.server.criticalevents.nano.CriticalEventProto;
import com.android.server.criticalevents.nano.CriticalEventProto.AppNotResponding;
import com.android.server.criticalevents.nano.CriticalEventProto.HalfWatchdog;
+import com.android.server.criticalevents.nano.CriticalEventProto.JavaCrash;
+import com.android.server.criticalevents.nano.CriticalEventProto.NativeCrash;
import com.android.server.criticalevents.nano.CriticalEventProto.Watchdog;
import org.junit.Before;
@@ -255,24 +257,39 @@
}
@Test
- public void privacyRedaction_anr() {
+ public void logJavaCrash() {
mCriticalEventLog.incTimeSeconds(1);
- mCriticalEventLog.logAnr("Subject 1", ServerProtoEnums.SYSTEM_SERVER, "AID_SYSTEM",
- SYSTEM_SERVER_UID, 0);
- mCriticalEventLog.incTimeSeconds(1);
- mCriticalEventLog.logAnr("Subject 2", ServerProtoEnums.SYSTEM_APP, "AID_RADIO",
- SYSTEM_APP_UID, 1);
- mCriticalEventLog.incTimeSeconds(1);
- mCriticalEventLog.logAnr("Subject 3", ServerProtoEnums.DATA_APP, "com.foo",
- DATA_APP_UID, 2);
- mCriticalEventLog.incTimeSeconds(1);
- mCriticalEventLog.logAnr("Subject 4", ServerProtoEnums.DATA_APP, "com.foo",
- DATA_APP_UID_2, 3);
- mCriticalEventLog.incTimeSeconds(1);
- mCriticalEventLog.logAnr("Subject 5", ServerProtoEnums.DATA_APP, "com.bar",
- DATA_APP_UID_3, 4);
+ mCriticalEventLog.logJavaCrash("com.android.MyClass", ServerProtoEnums.SYSTEM_APP,
+ "AID_RADIO", SYSTEM_APP_UID, 1);
mCriticalEventLog.incTimeSeconds(1);
+ CriticalEventLogProto logProto = getLogOutput();
+
+ assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS + 2000);
+ assertProtoArrayEquals(logProto.events, new CriticalEventProto[]{
+ javaCrash(START_TIME_MS + 1000, "com.android.MyClass", ServerProtoEnums.SYSTEM_APP,
+ "AID_RADIO", SYSTEM_APP_UID, 1)
+ });
+ }
+
+ @Test
+ public void logNativeCrash() {
+ mCriticalEventLog.incTimeSeconds(1);
+ mCriticalEventLog.logNativeCrash(ServerProtoEnums.SYSTEM_APP, "AID_RADIO", SYSTEM_APP_UID,
+ 1);
+ mCriticalEventLog.incTimeSeconds(1);
+
+ CriticalEventLogProto logProto = getLogOutput();
+
+ assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS + 2000);
+ assertProtoArrayEquals(logProto.events, new CriticalEventProto[]{
+ nativeCrash(START_TIME_MS + 1000, ServerProtoEnums.SYSTEM_APP, "AID_RADIO",
+ SYSTEM_APP_UID, 1)
+ });
+ }
+
+ @Test
+ public void privacyRedaction_anr() {
CriticalEventProto systemServerAnr = anr(START_TIME_MS + 1000, "Subject 1",
CriticalEventProto.SYSTEM_SERVER, "AID_SYSTEM", SYSTEM_SERVER_UID, 0);
CriticalEventProto systemAppAnr = anr(START_TIME_MS + 2000, "Subject 2",
@@ -289,6 +306,8 @@
CriticalEventProto barAppAnrRedacted = anr(START_TIME_MS + 5000, "",
CriticalEventProto.DATA_APP, "", DATA_APP_UID_3, 4);
+ addToLog(systemServerAnr, systemAppAnr, fooAppAnr, fooAppAnrUid2, barAppAnr);
+
assertProtoArrayEquals(
getLogOutput(ServerProtoEnums.DATA_APP, "com.foo", DATA_APP_UID).events,
new CriticalEventProto[]{
@@ -325,9 +344,123 @@
}
@Test
- public void privacyRedaction_anr_doesNotMutateLogState() {
- mCriticalEventLog.logAnr("Subject", ServerProtoEnums.DATA_APP, "com.foo",
+ public void privacyRedaction_javaCrash() {
+ CriticalEventProto systemServerCrash = javaCrash(START_TIME_MS + 1000, "Exception class 1",
+ CriticalEventProto.SYSTEM_SERVER, "AID_SYSTEM",
+ SYSTEM_SERVER_UID, 0);
+ CriticalEventProto systemAppCrash = javaCrash(START_TIME_MS + 2000, "Exception class 2",
+ CriticalEventProto.SYSTEM_APP, "AID_RADIO", SYSTEM_APP_UID, 1);
+ CriticalEventProto fooAppCrash = javaCrash(START_TIME_MS + 3000, "Exception class 3",
+ CriticalEventProto.DATA_APP, "com.foo", DATA_APP_UID, 2);
+ CriticalEventProto fooAppCrashUid2 = javaCrash(START_TIME_MS + 4000, "Exception class 4",
+ CriticalEventProto.DATA_APP, "com.foo", DATA_APP_UID_2, 3);
+ CriticalEventProto fooAppCrashUid2Redacted = javaCrash(START_TIME_MS + 4000, "",
+ CriticalEventProto.DATA_APP, "", DATA_APP_UID_2, 3);
+ CriticalEventProto barAppCrash = javaCrash(START_TIME_MS + 5000, "Exception class 5",
+ CriticalEventProto.DATA_APP, "com.bar", DATA_APP_UID_3, 4);
+ CriticalEventProto barAppCrashRedacted = javaCrash(START_TIME_MS + 5000, "",
+ CriticalEventProto.DATA_APP, "", DATA_APP_UID_3, 4);
+
+ addToLog(systemServerCrash, systemAppCrash, fooAppCrash, fooAppCrashUid2, barAppCrash);
+
+ assertProtoArrayEquals(
+ getLogOutput(ServerProtoEnums.DATA_APP, "com.foo", DATA_APP_UID).events,
+ new CriticalEventProto[]{
+ systemServerCrash,
+ systemAppCrash,
+ fooAppCrash,
+ // Redacted since the trace file and crash are for different uids.
+ fooAppCrashUid2Redacted,
+ // Redacted since the trace file and crash are for different data apps.
+ barAppCrashRedacted
+ });
+
+ assertProtoArrayEquals(
+ getLogOutput(ServerProtoEnums.SYSTEM_SERVER, "AID_SYSTEM",
+ SYSTEM_SERVER_UID).events,
+ new CriticalEventProto[]{
+ systemServerCrash,
+ systemAppCrash,
+ fooAppCrash,
+ fooAppCrashUid2,
+ barAppCrash
+ });
+
+ assertProtoArrayEquals(
+ getLogOutput(ServerProtoEnums.SYSTEM_APP, "AID_RADIO",
+ SYSTEM_APP_UID).events,
+ new CriticalEventProto[]{
+ systemServerCrash,
+ systemAppCrash,
+ fooAppCrash,
+ fooAppCrashUid2,
+ barAppCrash
+ });
+ }
+
+ @Test
+ public void privacyRedaction_nativeCrash() {
+ CriticalEventProto systemServerCrash = nativeCrash(START_TIME_MS + 1000,
+ CriticalEventProto.SYSTEM_SERVER, "AID_SYSTEM",
+ SYSTEM_SERVER_UID, 0);
+ CriticalEventProto systemAppCrash = nativeCrash(START_TIME_MS + 2000,
+ CriticalEventProto.SYSTEM_APP, "AID_RADIO", SYSTEM_APP_UID, 1);
+ CriticalEventProto fooAppCrash = nativeCrash(START_TIME_MS + 3000,
+ CriticalEventProto.DATA_APP, "com.foo", DATA_APP_UID, 2);
+ CriticalEventProto fooAppCrashUid2 = nativeCrash(START_TIME_MS + 4000,
+ CriticalEventProto.DATA_APP, "com.foo", DATA_APP_UID_2, 3);
+ CriticalEventProto fooAppCrashUid2Redacted = nativeCrash(START_TIME_MS + 4000,
+ CriticalEventProto.DATA_APP, "", DATA_APP_UID_2, 3);
+ CriticalEventProto barAppCrash = nativeCrash(START_TIME_MS + 5000,
+ CriticalEventProto.DATA_APP, "com.bar", DATA_APP_UID_3, 4);
+ CriticalEventProto barAppCrashRedacted = nativeCrash(START_TIME_MS + 5000,
+ CriticalEventProto.DATA_APP, "", DATA_APP_UID_3, 4);
+
+ addToLog(systemServerCrash, systemAppCrash, fooAppCrash, fooAppCrashUid2, barAppCrash);
+
+ assertProtoArrayEquals(
+ getLogOutput(ServerProtoEnums.DATA_APP, "com.foo", DATA_APP_UID).events,
+ new CriticalEventProto[]{
+ systemServerCrash,
+ systemAppCrash,
+ fooAppCrash,
+ // Redacted since the trace file and crash are for different uids.
+ fooAppCrashUid2Redacted,
+ // Redacted since the trace file and crash are for different data apps.
+ barAppCrashRedacted
+ });
+
+ assertProtoArrayEquals(
+ getLogOutput(ServerProtoEnums.SYSTEM_SERVER, "AID_SYSTEM",
+ SYSTEM_SERVER_UID).events,
+ new CriticalEventProto[]{
+ systemServerCrash,
+ systemAppCrash,
+ fooAppCrash,
+ fooAppCrashUid2,
+ barAppCrash
+ });
+
+ assertProtoArrayEquals(
+ getLogOutput(ServerProtoEnums.SYSTEM_APP, "AID_RADIO",
+ SYSTEM_APP_UID).events,
+ new CriticalEventProto[]{
+ systemServerCrash,
+ systemAppCrash,
+ fooAppCrash,
+ fooAppCrashUid2,
+ barAppCrash
+ });
+ }
+
+ @Test
+ public void privacyRedaction_doesNotMutateLogState() {
+ mCriticalEventLog.logAnr("ANR Subject", ServerProtoEnums.DATA_APP, "com.foo",
10_001, DATA_APP_UID);
+ mCriticalEventLog.logJavaCrash("com.foo.MyClass", ServerProtoEnums.DATA_APP, "com.foo",
+ 10_001, DATA_APP_UID);
+ mCriticalEventLog.logNativeCrash(ServerProtoEnums.DATA_APP, "com.foo", 10_001,
+ DATA_APP_UID);
CriticalEventLogProto unredactedLogBefore = getLogOutput(ServerProtoEnums.SYSTEM_SERVER,
"AID_SYSTEM", SYSTEM_SERVER_UID);
@@ -488,6 +621,12 @@
ServerProtoEnums.SYSTEM_SERVER);
}
+ private void addToLog(CriticalEventProto... events) {
+ for (CriticalEventProto event : events) {
+ mCriticalEventLog.appendAndSave(event);
+ }
+ }
+
private CriticalEventLogProto getLogOutput() {
return getLogOutput(mCriticalEventLog);
}
@@ -533,9 +672,29 @@
}
}
+ private static CriticalEventProto watchdog(long timestampMs, String subject) {
+ return watchdog(timestampMs, subject, "A UUID");
+ }
+
+ private static CriticalEventProto watchdog(long timestampMs, String subject, String uuid) {
+ CriticalEventProto event = new CriticalEventProto();
+ event.timestampMs = timestampMs;
+ event.setWatchdog(new Watchdog());
+ event.getWatchdog().subject = subject;
+ event.getWatchdog().uuid = uuid;
+ return event;
+ }
+
+ private static CriticalEventProto halfWatchdog(long timestampMs, String subject) {
+ CriticalEventProto event = new CriticalEventProto();
+ event.timestampMs = timestampMs;
+ event.setHalfWatchdog(new HalfWatchdog());
+ event.getHalfWatchdog().subject = subject;
+ return event;
+ }
+
private static CriticalEventProto anr(long timestampMs, String subject, int processClass,
- String processName,
- int uid, int pid) {
+ String processName, int uid, int pid) {
CriticalEventProto event = new CriticalEventProto();
event.timestampMs = timestampMs;
event.setAnr(new AppNotResponding());
@@ -547,24 +706,28 @@
return event;
}
- private CriticalEventProto watchdog(long timestampMs, String subject) {
- return watchdog(timestampMs, subject, "A UUID");
- }
-
- private CriticalEventProto watchdog(long timestampMs, String subject, String uuid) {
+ private static CriticalEventProto javaCrash(long timestampMs, String exceptionClass,
+ int processClass, String processName, int uid, int pid) {
CriticalEventProto event = new CriticalEventProto();
event.timestampMs = timestampMs;
- event.setWatchdog(new Watchdog());
- event.getWatchdog().subject = subject;
- event.getWatchdog().uuid = uuid;
+ event.setJavaCrash(new JavaCrash());
+ event.getJavaCrash().exceptionClass = exceptionClass;
+ event.getJavaCrash().processClass = processClass;
+ event.getJavaCrash().process = processName;
+ event.getJavaCrash().uid = uid;
+ event.getJavaCrash().pid = pid;
return event;
}
- private CriticalEventProto halfWatchdog(long timestampMs, String subject) {
+ private static CriticalEventProto nativeCrash(long timestampMs, int processClass,
+ String processName, int uid, int pid) {
CriticalEventProto event = new CriticalEventProto();
event.timestampMs = timestampMs;
- event.setHalfWatchdog(new HalfWatchdog());
- event.getHalfWatchdog().subject = subject;
+ event.setNativeCrash(new NativeCrash());
+ event.getNativeCrash().processClass = processClass;
+ event.getNativeCrash().process = processName;
+ event.getNativeCrash().uid = uid;
+ event.getNativeCrash().pid = pid;
return event;
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index f91661b..0f6dfda 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -36,11 +36,8 @@
import android.content.pm.parsing.ParsingPackage;
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.ParsedInstrumentationImpl;
-import android.content.pm.parsing.component.ParsedIntentInfo;
import android.content.pm.parsing.component.ParsedIntentInfoImpl;
-import android.content.pm.parsing.component.ParsedProvider;
import android.content.pm.parsing.component.ParsedProviderImpl;
import android.os.Build;
import android.os.Process;
@@ -958,7 +955,7 @@
PackageSetting targetSetting = simulateAddPackage(appsFilter, target, DUMMY_TARGET_APPID);
SharedUserSetting actorSharedSetting = new SharedUserSetting("actorSharedUser",
- targetSetting.pkgFlags, targetSetting.pkgPrivateFlags);
+ targetSetting.getFlags(), targetSetting.getPrivateFlags());
PackageSetting overlaySetting =
simulateAddPackage(appsFilter, overlay, DUMMY_OVERLAY_APPID);
simulateAddPackage(appsFilter, actorOne, DUMMY_ACTOR_APPID,
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 4f77afb..f45c869 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -46,18 +46,6 @@
import android.app.PendingIntent;
import android.app.Person;
import android.app.admin.DevicePolicyManager;
-import android.app.appsearch.AppSearchBatchResult;
-import android.app.appsearch.AppSearchManager;
-import android.app.appsearch.AppSearchResult;
-import android.app.appsearch.GenericDocument;
-import android.app.appsearch.PackageIdentifier;
-import android.app.appsearch.SearchResultPage;
-import android.app.appsearch.SetSchemaResponse;
-import android.app.appsearch.aidl.AppSearchBatchResultParcel;
-import android.app.appsearch.aidl.AppSearchResultParcel;
-import android.app.appsearch.aidl.IAppSearchBatchResultCallback;
-import android.app.appsearch.aidl.IAppSearchManager;
-import android.app.appsearch.aidl.IAppSearchResultCallback;
import android.app.role.OnRoleHoldersChangedListener;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ActivityNotFoundException;
@@ -92,9 +80,7 @@
import android.os.Bundle;
import android.os.FileUtils;
import android.os.Handler;
-import android.os.IBinder;
import android.os.Looper;
-import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
@@ -106,6 +92,7 @@
import android.util.Log;
import android.util.Pair;
+import com.android.internal.infra.AndroidFuture;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.pm.LauncherAppsService.LauncherAppsImpl;
@@ -168,7 +155,6 @@
case Context.DEVICE_POLICY_SERVICE:
return mMockDevicePolicyManager;
case Context.APP_SEARCH_SERVICE:
- return new AppSearchManager(this, mMockAppSearchManager);
case Context.ROLE_SERVICE:
// RoleManager is final and cannot be mocked, so we only override the inject
// accessor methods in ShortcutService.
@@ -647,260 +633,6 @@
}
}
- protected class MockAppSearchManager implements IAppSearchManager {
-
- protected Map<String, List<PackageIdentifier>> mSchemasVisibleToPackages =
- new ArrayMap<>(1);
- private Map<String, Map<String, GenericDocument>> mDocumentMap = new ArrayMap<>(1);
-
- private String getKey(UserHandle userHandle, String databaseName) {
- return userHandle.getIdentifier() + "@" + databaseName;
- }
-
- @Override
- public void setSchema(String packageName, String databaseName, List<Bundle> schemaBundles,
- List<String> schemasNotDisplayedBySystem,
- Map<String, List<Bundle>> schemasVisibleToPackagesBundles, boolean forceOverride,
- int version, UserHandle userHandle, long binderCallStartTimeMillis,
- IAppSearchResultCallback callback) throws RemoteException {
- for (Map.Entry<String, List<Bundle>> entry :
- schemasVisibleToPackagesBundles.entrySet()) {
- final String key = entry.getKey();
- final List<PackageIdentifier> packageIdentifiers;
- if (!mSchemasVisibleToPackages.containsKey(key)) {
- packageIdentifiers = new ArrayList<>(entry.getValue().size());
- mSchemasVisibleToPackages.put(key, packageIdentifiers);
- } else {
- packageIdentifiers = mSchemasVisibleToPackages.get(key);
- }
- for (int i = 0; i < entry.getValue().size(); i++) {
- packageIdentifiers.add(new PackageIdentifier(entry.getValue().get(i)));
- }
- }
- final SetSchemaResponse response = new SetSchemaResponse.Builder().build();
- callback.onResult(
- new AppSearchResultParcel(
- AppSearchResult.newSuccessfulResult(response.getBundle())));
- }
-
- @Override
- public void getSchema(String packageName, String databaseName, UserHandle userHandle,
- IAppSearchResultCallback callback) throws RemoteException {
- ignore(callback);
- }
-
- @Override
- public void getNamespaces(String packageName, String databaseName, UserHandle userHandle,
- IAppSearchResultCallback callback) throws RemoteException {
- ignore(callback);
- }
-
- @Override
- public void putDocuments(String packageName, String databaseName,
- List<Bundle> documentBundles, UserHandle userHandle, long binderCallStartTimeMillis,
- IAppSearchBatchResultCallback callback)
- throws RemoteException {
- final List<GenericDocument> docs = new ArrayList<>(documentBundles.size());
- for (Bundle bundle : documentBundles) {
- docs.add(new GenericDocument(bundle));
- }
- final AppSearchBatchResult.Builder<String, Void> builder =
- new AppSearchBatchResult.Builder<>();
- final String key = getKey(userHandle, databaseName);
- Map<String, GenericDocument> docMap = mDocumentMap.get(key);
- for (GenericDocument doc : docs) {
- builder.setSuccess(doc.getId(), null);
- if (docMap == null) {
- docMap = new ArrayMap<>(1);
- mDocumentMap.put(key, docMap);
- }
- docMap.put(doc.getId(), doc);
- }
- callback.onResult(new AppSearchBatchResultParcel<>(builder.build()));
- }
-
- @Override
- public void getDocuments(String packageName, String databaseName, String namespace,
- List<String> ids, Map<String, List<String>> typePropertyPaths,
- UserHandle userHandle, long binderCallStartTimeMillis,
- IAppSearchBatchResultCallback callback) throws RemoteException {
- final AppSearchBatchResult.Builder<String, Bundle> builder =
- new AppSearchBatchResult.Builder<>();
- final String key = getKey(userHandle, databaseName);
- if (!mDocumentMap.containsKey(key)) {
- for (String id : ids) {
- builder.setFailure(id, AppSearchResult.RESULT_NOT_FOUND,
- key + " not found when getting: " + id);
- }
- } else {
- final Map<String, GenericDocument> docs = mDocumentMap.get(key);
- for (String id : ids) {
- if (docs.containsKey(id)) {
- builder.setSuccess(id, docs.get(id).getBundle());
- } else {
- builder.setFailure(id, AppSearchResult.RESULT_NOT_FOUND,
- "shortcut not found: " + id);
- }
- }
- }
- callback.onResult(new AppSearchBatchResultParcel<>(builder.build()));
- }
-
- @Override
- public void query(String packageName, String databaseName, String queryExpression,
- Bundle searchSpecBundle, UserHandle userHandle, long binderCallStartTimeMillis,
- IAppSearchResultCallback callback)
- throws RemoteException {
- final String key = getKey(userHandle, databaseName);
- if (!mDocumentMap.containsKey(key)) {
- final Bundle page = new Bundle();
- page.putLong(SearchResultPage.NEXT_PAGE_TOKEN_FIELD, 1);
- page.putParcelableArrayList(SearchResultPage.RESULTS_FIELD, new ArrayList<>());
- callback.onResult(
- new AppSearchResultParcel<>(AppSearchResult.newSuccessfulResult(page)));
- return;
- }
- final List<GenericDocument> documents = new ArrayList<>(mDocumentMap.get(key).values());
- final Bundle page = new Bundle();
- page.putLong(SearchResultPage.NEXT_PAGE_TOKEN_FIELD, 0);
- final ArrayList<Bundle> resultBundles = new ArrayList<>();
- for (GenericDocument document : documents) {
- final Bundle resultBundle = new Bundle();
- resultBundle.putBundle("document", document.getBundle());
- resultBundle.putString("packageName", packageName);
- resultBundle.putString("databaseName", databaseName);
- resultBundle.putParcelableArrayList("matches", new ArrayList<>());
- resultBundles.add(resultBundle);
- }
- page.putParcelableArrayList(SearchResultPage.RESULTS_FIELD, resultBundles);
- callback.onResult(
- new AppSearchResultParcel<>(AppSearchResult.newSuccessfulResult(page)));
- }
-
- @Override
- public void globalQuery(String packageName, String queryExpression, Bundle searchSpecBundle,
- UserHandle userHandle, long binderCallStartTimeMillis,
- IAppSearchResultCallback callback) throws RemoteException {
- ignore(callback);
- }
-
- @Override
- public void getNextPage(String packageName, long nextPageToken, UserHandle userHandle,
- IAppSearchResultCallback callback) throws RemoteException {
- final Bundle page = new Bundle();
- page.putLong(SearchResultPage.NEXT_PAGE_TOKEN_FIELD, 1);
- page.putParcelableArrayList(SearchResultPage.RESULTS_FIELD, new ArrayList<>());
- callback.onResult(
- new AppSearchResultParcel<>(AppSearchResult.newSuccessfulResult(page)));
- }
-
- @Override
- public void invalidateNextPageToken(String packageName, long nextPageToken,
- UserHandle userHandle) throws RemoteException {
- }
-
- @Override
- public void writeQueryResultsToFile(String packageName, String databaseName,
- ParcelFileDescriptor fileDescriptor, String queryExpression,
- Bundle searchSpecBundle, UserHandle userHandle, IAppSearchResultCallback callback)
- throws RemoteException {
- ignore(callback);
- }
-
- @Override
- public void putDocumentsFromFile(String packageName, String databaseName,
- ParcelFileDescriptor fileDescriptor, UserHandle userHandle,
- IAppSearchResultCallback callback)
- throws RemoteException {
- ignore(callback);
- }
-
- @Override
- public void reportUsage(String packageName, String databaseName, String namespace,
- String documentId, long usageTimestampMillis, boolean systemUsage,
- UserHandle userHandle,
- IAppSearchResultCallback callback)
- throws RemoteException {
- ignore(callback);
- }
-
- @Override
- public void removeByDocumentId(String packageName, String databaseName, String namespace,
- List<String> ids, UserHandle userHandle, long binderCallStartTimeMillis,
- IAppSearchBatchResultCallback callback)
- throws RemoteException {
- final AppSearchBatchResult.Builder<String, Void> builder =
- new AppSearchBatchResult.Builder<>();
- final String key = getKey(userHandle, databaseName);
- if (!mDocumentMap.containsKey(key)) {
- for (String id : ids) {
- builder.setFailure(id, AppSearchResult.RESULT_NOT_FOUND,
- "package " + key + " not found when removing " + id);
- }
- } else {
- final Map<String, GenericDocument> docs = mDocumentMap.get(key);
- for (String id : ids) {
- if (docs.containsKey(id)) {
- docs.remove(id);
- builder.setSuccess(id, null);
- } else {
- builder.setFailure(id, AppSearchResult.RESULT_NOT_FOUND,
- "shortcut not found when removing " + id);
- }
- }
- }
- callback.onResult(new AppSearchBatchResultParcel<>(builder.build()));
- }
-
- @Override
- public void removeByQuery(String packageName, String databaseName, String queryExpression,
- Bundle searchSpecBundle, UserHandle userHandle, long binderCallStartTimeMillis,
- IAppSearchResultCallback callback)
- throws RemoteException {
- final String key = getKey(userHandle, databaseName);
- if (!mDocumentMap.containsKey(key)) {
- callback.onResult(
- new AppSearchResultParcel<>(AppSearchResult.newSuccessfulResult(null)));
- return;
- }
- mDocumentMap.get(key).clear();
- callback.onResult(
- new AppSearchResultParcel<>(AppSearchResult.newSuccessfulResult(null)));
- }
-
- @Override
- public void getStorageInfo(String packageName, String databaseName, UserHandle userHandle,
- IAppSearchResultCallback callback) throws RemoteException {
- ignore(callback);
- }
-
- @Override
- public void persistToDisk(String packageName, UserHandle userHandle,
- long binderCallStartTimeMillis) throws RemoteException {
- }
-
- @Override
- public void initialize(String packageName, UserHandle userHandle,
- long binderCallStartTimeMillis, IAppSearchResultCallback callback)
- throws RemoteException {
- ignore(callback);
- }
-
- @Override
- public IBinder asBinder() {
- return null;
- }
-
- private void removeShortcuts() {
- mDocumentMap.clear();
- }
-
- private void ignore(IAppSearchResultCallback callback) throws RemoteException {
- callback.onResult(
- new AppSearchResultParcel<>(AppSearchResult.newSuccessfulResult(null)));
- }
- }
-
public static class ShortcutActivity extends Activity {
}
@@ -952,7 +684,6 @@
protected PackageManagerInternal mMockPackageManagerInternal;
protected UserManager mMockUserManager;
protected DevicePolicyManager mMockDevicePolicyManager;
- protected MockAppSearchManager mMockAppSearchManager;
protected UserManagerInternal mMockUserManagerInternal;
protected UsageStatsManagerInternal mMockUsageStatsManagerInternal;
protected ActivityManagerInternal mMockActivityManagerInternal;
@@ -1102,7 +833,6 @@
mMockPackageManagerInternal = mock(PackageManagerInternal.class);
mMockUserManager = mock(UserManager.class);
mMockDevicePolicyManager = mock(DevicePolicyManager.class);
- mMockAppSearchManager = new MockAppSearchManager();
mMockUserManagerInternal = mock(UserManagerInternal.class);
mMockUsageStatsManagerInternal = mock(UsageStatsManagerInternal.class);
mMockActivityManagerInternal = mock(ActivityManagerInternal.class);
@@ -1314,9 +1044,6 @@
shutdownServices();
- mMockAppSearchManager.removeShortcuts();
- mMockAppSearchManager = null;
-
super.tearDown();
}
@@ -2235,6 +1962,18 @@
return p == null ? null : p.getAllShareTargetsForTest();
}
+ protected void resetPersistedShortcuts() {
+ final ShortcutPackage p = mService.getPackageShortcutForTest(
+ getCallingPackage(), getCallingUserId());
+ p.removeAllShortcutsAsync();
+ }
+
+ protected void getPersistedShortcut(AndroidFuture<List<ShortcutInfo>> cb) {
+ final ShortcutPackage p = mService.getPackageShortcutForTest(
+ getCallingPackage(), getCallingUserId());
+ p.getTopShortcutsFromPersistence(cb);
+ }
+
/**
* @return the number of shortcuts stored internally for the caller that can be used as a share
* target in the ShareSheet. Such shortcuts have a matching category with at least one of the
@@ -2425,8 +2164,6 @@
deleteAllSavedFiles();
- mMockAppSearchManager.removeShortcuts();
-
initService();
mService.applyRestore(payload, USER_0);
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index b0f8d40..c1cf007 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -82,7 +82,10 @@
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
import java.util.UUID;
@RunWith(AndroidJUnit4.class)
@@ -439,14 +442,14 @@
.setUid(ps2.getAppId())
.hideAsFinal());
- ps1.usesStaticLibraries = new String[] { "com.example.shared.one" };
- ps1.usesStaticLibrariesVersions = new long[] { 12 };
- ps1.setFlags(ps1.pkgFlags | ApplicationInfo.FLAG_SYSTEM);
+ ps1.setUsesStaticLibraries(new String[] { "com.example.shared.one" });
+ ps1.setUsesStaticLibrariesVersions(new long[] { 12 });
+ ps1.setFlags(ps1.getFlags() | ApplicationInfo.FLAG_SYSTEM);
settingsUnderTest.mPackages.put(PACKAGE_NAME_1, ps1);
assertThat(settingsUnderTest.disableSystemPackageLPw(PACKAGE_NAME_1, false), is(true));
- ps2.usesStaticLibraries = new String[] { "com.example.shared.two" };
- ps2.usesStaticLibrariesVersions = new long[] { 34 };
+ ps2.setUsesStaticLibraries(new String[] { "com.example.shared.two" });
+ ps2.setUsesStaticLibrariesVersions(new long[] { 34 });
settingsUnderTest.mPackages.put(PACKAGE_NAME_2, ps2);
settingsUnderTest.writeLPr();
@@ -460,32 +463,32 @@
PackageSetting readPs2 = settingsUnderTest.getPackageLPr(PACKAGE_NAME_2);
Truth.assertThat(readPs1).isNotNull();
- Truth.assertThat(readPs1.usesStaticLibraries).isNotNull();
- Truth.assertThat(readPs1.usesStaticLibrariesVersions).isNotNull();
+ Truth.assertThat(readPs1.getUsesStaticLibraries()).isNotNull();
+ Truth.assertThat(readPs1.getUsesStaticLibrariesVersions()).isNotNull();
Truth.assertThat(readPs2).isNotNull();
- Truth.assertThat(readPs2.usesStaticLibraries).isNotNull();
- Truth.assertThat(readPs2.usesStaticLibrariesVersions).isNotNull();
+ Truth.assertThat(readPs2.getUsesStaticLibraries()).isNotNull();
+ Truth.assertThat(readPs2.getUsesStaticLibrariesVersions()).isNotNull();
List<Long> ps1VersionsAsList = new ArrayList<>();
- for (long version : ps1.usesStaticLibrariesVersions) {
+ for (long version : ps1.getUsesStaticLibrariesVersions()) {
ps1VersionsAsList.add(version);
}
List<Long> ps2VersionsAsList = new ArrayList<>();
- for (long version : ps2.usesStaticLibrariesVersions) {
+ for (long version : ps2.getUsesStaticLibrariesVersions()) {
ps2VersionsAsList.add(version);
}
- Truth.assertThat(readPs1.usesStaticLibraries).asList()
- .containsExactlyElementsIn(ps1.usesStaticLibraries).inOrder();
+ Truth.assertThat(readPs1.getUsesStaticLibraries()).asList()
+ .containsExactlyElementsIn(ps1.getUsesStaticLibraries()).inOrder();
- Truth.assertThat(readPs1.usesStaticLibrariesVersions).asList()
+ Truth.assertThat(readPs1.getUsesStaticLibrariesVersions()).asList()
.containsExactlyElementsIn(ps1VersionsAsList).inOrder();
- Truth.assertThat(readPs2.usesStaticLibraries).asList()
- .containsExactlyElementsIn(ps2.usesStaticLibraries).inOrder();
+ Truth.assertThat(readPs2.getUsesStaticLibraries()).asList()
+ .containsExactlyElementsIn(ps2.getUsesStaticLibraries()).inOrder();
- Truth.assertThat(readPs2.usesStaticLibrariesVersions).asList()
+ Truth.assertThat(readPs2.getUsesStaticLibrariesVersions()).asList()
.containsExactlyElementsIn(ps2VersionsAsList).inOrder();
}
@@ -615,8 +618,8 @@
final PackageSetting testPkgSetting01 =
createPackageSetting(0 /*sharedUserId*/, 0 /*pkgFlags*/);
testPkgSetting01.setInstalled(false /*installed*/, 0 /*userId*/);
- assertThat(testPkgSetting01.pkgFlags, is(0));
- assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
+ assertThat(testPkgSetting01.getFlags(), is(0));
+ assertThat(testPkgSetting01.getPrivateFlags(), is(0));
final PackageSetting oldPkgSetting01 = new PackageSetting(testPkgSetting01);
Settings.updatePackageSetting(
testPkgSetting01,
@@ -635,8 +638,8 @@
UUID.randomUUID());
assertThat(testPkgSetting01.getPrimaryCpuAbi(), is("arm64-v8a"));
assertThat(testPkgSetting01.getSecondaryCpuAbi(), is("armeabi"));
- assertThat(testPkgSetting01.pkgFlags, is(0));
- assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
+ assertThat(testPkgSetting01.getFlags(), is(0));
+ assertThat(testPkgSetting01.getPrivateFlags(), is(0));
final PackageUserState userState = testPkgSetting01.readUserState(0);
final PackageUserState oldUserState = oldPkgSetting01.readUserState(0);
verifyUserState(userState, oldUserState, false /*userStateChanged*/, false /*notLaunched*/,
@@ -649,8 +652,8 @@
final PackageSetting testPkgSetting01 =
createPackageSetting(0 /*sharedUserId*/, 0 /*pkgFlags*/);
testPkgSetting01.setInstalled(false /*installed*/, 0 /*userId*/);
- assertThat(testPkgSetting01.pkgFlags, is(0));
- assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
+ assertThat(testPkgSetting01.getFlags(), is(0));
+ assertThat(testPkgSetting01.getPrivateFlags(), is(0));
final PackageSetting oldPkgSetting01 = new PackageSetting(testPkgSetting01);
Settings.updatePackageSetting(
testPkgSetting01,
@@ -669,8 +672,8 @@
UUID.randomUUID());
assertThat(testPkgSetting01.getPrimaryCpuAbi(), is("arm64-v8a"));
assertThat(testPkgSetting01.getSecondaryCpuAbi(), is("armeabi"));
- assertThat(testPkgSetting01.pkgFlags, is(ApplicationInfo.FLAG_SYSTEM));
- assertThat(testPkgSetting01.pkgPrivateFlags, is(ApplicationInfo.PRIVATE_FLAG_PRIVILEGED));
+ assertThat(testPkgSetting01.getFlags(), is(ApplicationInfo.FLAG_SYSTEM));
+ assertThat(testPkgSetting01.getPrivateFlags(), is(ApplicationInfo.PRIVATE_FLAG_PRIVILEGED));
final PackageUserState userState = testPkgSetting01.readUserState(0);
final PackageUserState oldUserState = oldPkgSetting01.readUserState(0);
// WARNING: When creating a shallow copy of the PackageSetting we do NOT create
@@ -739,8 +742,8 @@
UUID.randomUUID());
assertThat(testPkgSetting01.getPath(), is(UPDATED_CODE_PATH));
assertThat(testPkgSetting01.getPackageName(), is(PACKAGE_NAME));
- assertThat(testPkgSetting01.pkgFlags, is(ApplicationInfo.FLAG_SYSTEM));
- assertThat(testPkgSetting01.pkgPrivateFlags, is(ApplicationInfo.PRIVATE_FLAG_PRIVILEGED));
+ assertThat(testPkgSetting01.getFlags(), is(ApplicationInfo.FLAG_SYSTEM));
+ assertThat(testPkgSetting01.getPrivateFlags(), is(ApplicationInfo.PRIVATE_FLAG_PRIVILEGED));
assertThat(testPkgSetting01.getPrimaryCpuAbi(), is("arm64-v8a"));
assertThat(testPkgSetting01.getSecondaryCpuAbi(), is("armeabi"));
// signatures object must be different
@@ -779,8 +782,8 @@
assertThat(testPkgSetting01.getAppId(), is(0));
assertThat(testPkgSetting01.getPath(), is(INITIAL_CODE_PATH));
assertThat(testPkgSetting01.getPackageName(), is(PACKAGE_NAME));
- assertThat(testPkgSetting01.pkgFlags, is(0));
- assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
+ assertThat(testPkgSetting01.getFlags(), is(0));
+ assertThat(testPkgSetting01.getPrivateFlags(), is(0));
assertThat(testPkgSetting01.getPrimaryCpuAbi(), is("x86_64"));
assertThat(testPkgSetting01.getSecondaryCpuAbi(), is("x86"));
assertThat(testPkgSetting01.getVersionCode(), is(INITIAL_VERSION_CODE));
@@ -821,8 +824,8 @@
assertThat(testPkgSetting01.getAppId(), is(10064));
assertThat(testPkgSetting01.getPath(), is(INITIAL_CODE_PATH));
assertThat(testPkgSetting01.getPackageName(), is(PACKAGE_NAME));
- assertThat(testPkgSetting01.pkgFlags, is(0));
- assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
+ assertThat(testPkgSetting01.getFlags(), is(0));
+ assertThat(testPkgSetting01.getPrivateFlags(), is(0));
assertThat(testPkgSetting01.getPrimaryCpuAbi(), is("x86_64"));
assertThat(testPkgSetting01.getSecondaryCpuAbi(), is("x86"));
assertThat(testPkgSetting01.getVersionCode(), is(INITIAL_VERSION_CODE));
@@ -863,8 +866,8 @@
assertThat(testPkgSetting01.getAppId(), is(10064));
assertThat(testPkgSetting01.getPath(), is(UPDATED_CODE_PATH));
assertThat(testPkgSetting01.getPackageName(), is(PACKAGE_NAME));
- assertThat(testPkgSetting01.pkgFlags, is(0));
- assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
+ assertThat(testPkgSetting01.getFlags(), is(0));
+ assertThat(testPkgSetting01.getPrivateFlags(), is(0));
assertThat(testPkgSetting01.getPrimaryCpuAbi(), is("arm64-v8a"));
assertThat(testPkgSetting01.getSecondaryCpuAbi(), is("armeabi"));
assertNotSame(testPkgSetting01.getSignatures(), disabledSignatures);
@@ -930,10 +933,11 @@
testPkgSetting.getLegacyNativeLibraryPath());
assertThat(origPkgSetting.getLegacyNativeLibraryPath(),
is(testPkgSetting.getLegacyNativeLibraryPath()));
- if (origPkgSetting.mimeGroups != null) {
- assertNotSame(origPkgSetting.mimeGroups, testPkgSetting.mimeGroups);
+ if (origPkgSetting.getMimeGroups() != null
+ && origPkgSetting.getMimeGroups() != Collections.<String, Set<String>>emptyMap()) {
+ assertNotSame(origPkgSetting.getMimeGroups(), testPkgSetting.getMimeGroups());
}
- assertThat(origPkgSetting.mimeGroups, is(testPkgSetting.mimeGroups));
+ assertThat(origPkgSetting.getMimeGroups(), is(testPkgSetting.getMimeGroups()));
assertNotSame(origPkgSetting.mLegacyPermissionsState,
testPkgSetting.mLegacyPermissionsState);
assertThat(origPkgSetting.mLegacyPermissionsState,
@@ -945,8 +949,8 @@
assertSame(origPkgSetting.getPkg(), testPkgSetting.getPkg());
// No equals() method for this object
// assertThat(origPkgSetting.pkg, is(testPkgSetting.pkg));
- assertThat(origPkgSetting.pkgFlags, is(testPkgSetting.pkgFlags));
- assertThat(origPkgSetting.pkgPrivateFlags, is(testPkgSetting.pkgPrivateFlags));
+ assertThat(origPkgSetting.getFlags(), is(testPkgSetting.getFlags()));
+ assertThat(origPkgSetting.getPrivateFlags(), is(testPkgSetting.getPrivateFlags()));
assertSame(origPkgSetting.getPrimaryCpuAbi(), testPkgSetting.getPrimaryCpuAbi());
assertThat(origPkgSetting.getPrimaryCpuAbi(), is(testPkgSetting.getPrimaryCpuAbi()));
assertThat(origPkgSetting.getRealName(), is(testPkgSetting.getRealName()));
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
index 5593763..2146070 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
@@ -17,7 +17,6 @@
package com.android.server.pm;
import android.content.pm.SigningDetails;
-import android.util.ArraySet;
import android.util.SparseArray;
import com.android.server.pm.parsing.pkg.AndroidPackage;
@@ -25,6 +24,7 @@
import java.io.File;
import java.util.Map;
+import java.util.Set;
import java.util.UUID;
public class PackageSettingBuilder {
@@ -46,7 +46,7 @@
private InstallSource mInstallSource;
private String[] mUsesStaticLibraries;
private long[] mUsesStaticLibrariesVersions;
- private Map<String, ArraySet<String>> mMimeGroups;
+ private Map<String, Set<String>> mMimeGroups;
private SigningDetails mSigningDetails;
private UUID mDomainSetId = UUID.randomUUID();
@@ -127,7 +127,7 @@
return this;
}
- public PackageSettingBuilder setMimeGroups(Map<String, ArraySet<String>> mimeGroups) {
+ public PackageSettingBuilder setMimeGroups(Map<String, Set<String>> mimeGroups) {
this.mMimeGroups = mimeGroups;
return this;
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index 82ee330..cfdbb5b7 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -554,9 +554,9 @@
String packageName, boolean isInstant, PackageSetting pkgSetting) {
assertThat(pkgSetting.getPkg().getPackageName(), is(packageName));
assertThat(pkgSetting.getInstantApp(0), is(isInstant));
- assertThat(pkgSetting.usesStaticLibraries,
+ assertThat(pkgSetting.getUsesStaticLibraries(),
arrayContaining("some.static.library", "some.other.static.library"));
- assertThat(pkgSetting.usesStaticLibrariesVersions, is(new long[]{234L, 456L}));
+ assertThat(pkgSetting.getUsesStaticLibrariesVersions(), is(new long[]{234L, 456L}));
assertThat(pkgSetting.getPkg(), is(scanResult.mRequest.mParsedPackage));
assertThat(pkgSetting.getPath(), is(new File(createCodePath(packageName))));
assertThat(pkgSetting.getVersionCode(),
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
index 9598a00..bc2d256 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
@@ -18,9 +18,20 @@
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
import android.app.PendingIntent;
+import android.content.pm.ShortcutInfo;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.UserHandle;
+import com.android.internal.infra.AndroidFuture;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
/**
* Tests for {@link android.app.appsearch.AppSearchManager} and relevant APIs in ShortcutManager.
*
@@ -28,6 +39,21 @@
*/
public class ShortcutManagerTest12 extends BaseShortcutManagerTest {
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mService.updateConfigurationLocked(
+ ShortcutService.ConfigConstants.KEY_MAX_SHORTCUTS + "=5,"
+ + ShortcutService.ConfigConstants.KEY_SAVE_DELAY_MILLIS + "=1");
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ setCaller(CALLING_PACKAGE_1, USER_0);
+ mService.getPackageShortcutForTest(CALLING_PACKAGE_1, USER_0).removeAllShortcutsAsync();
+ super.tearDown();
+ }
+
public void testGetShortcutIntents_ReturnsMutablePendingIntents() throws RemoteException {
setDefaultLauncher(USER_0, LAUNCHER_1);
@@ -41,4 +67,201 @@
assertNotNull(intent);
});
}
+
+ public void testSetDynamicShortcuts_PersistsShortcutsToDisk() throws RemoteException {
+ if (!mService.isAppSearchEnabled()) {
+ return;
+ }
+ setCaller(CALLING_PACKAGE_1, USER_0);
+ // Verifies setDynamicShortcuts persists shortcuts into AppSearch
+ mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"),
+ makeShortcut("s2"),
+ makeShortcut("s3")
+ ));
+ List<ShortcutInfo> shortcuts = getAllPersistedShortcuts();
+ assertNotNull(shortcuts);
+ assertEquals(3, shortcuts.size());
+ Set<String> shortcutIds =
+ shortcuts.stream().map(ShortcutInfo::getId).collect(Collectors.toSet());
+ assertTrue(shortcutIds.contains("s1"));
+ assertTrue(shortcutIds.contains("s2"));
+ assertTrue(shortcutIds.contains("s3"));
+
+ // Verifies removeAllDynamicShortcuts removes shortcuts from persistence layer
+ mManager.removeAllDynamicShortcuts();
+ shortcuts = getAllPersistedShortcuts();
+ assertNotNull(shortcuts);
+ assertTrue(shortcuts.isEmpty());
+ }
+
+ public void testAddDynamicShortcuts_PersistsShortcutsToDisk() {
+ if (!mService.isAppSearchEnabled()) {
+ return;
+ }
+ setCaller(CALLING_PACKAGE_1, USER_0);
+ mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"),
+ makeShortcut("s2"),
+ makeShortcut("s3")
+ ));
+ // Verifies addDynamicShortcuts persists shortcuts into AppSearch
+ mManager.addDynamicShortcuts(list(makeShortcut("s4"), makeShortcut("s5")));
+ final List<ShortcutInfo> shortcuts = getAllPersistedShortcuts();
+ assertNotNull(shortcuts);
+ assertEquals(5, shortcuts.size());
+ final Set<String> shortcutIds =
+ shortcuts.stream().map(ShortcutInfo::getId).collect(Collectors.toSet());
+ assertTrue(shortcutIds.contains("s1"));
+ assertTrue(shortcutIds.contains("s2"));
+ assertTrue(shortcutIds.contains("s3"));
+ assertTrue(shortcutIds.contains("s4"));
+ assertTrue(shortcutIds.contains("s5"));
+ }
+
+ public void testPushDynamicShortcuts_PersistsShortcutsToDisk() {
+ if (!mService.isAppSearchEnabled()) {
+ return;
+ }
+ setCaller(CALLING_PACKAGE_1, USER_0);
+ mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"),
+ makeShortcut("s2"),
+ makeShortcut("s3"),
+ makeShortcut("s4"),
+ makeShortcut("s5")
+ ));
+ List<ShortcutInfo> shortcuts = getAllPersistedShortcuts();
+ assertNotNull(shortcuts);
+ assertEquals(5, shortcuts.size());
+ Set<String> shortcutIds =
+ shortcuts.stream().map(ShortcutInfo::getId).collect(Collectors.toSet());
+ assertTrue(shortcutIds.contains("s1"));
+ assertTrue(shortcutIds.contains("s2"));
+ assertTrue(shortcutIds.contains("s3"));
+ assertTrue(shortcutIds.contains("s4"));
+ assertTrue(shortcutIds.contains("s5"));
+ // Verifies pushDynamicShortcuts further persists shortcuts into AppSearch without
+ // removing previous shortcuts when max number of shortcuts is reached.
+ mManager.pushDynamicShortcut(makeShortcut("s6"));
+ shortcuts = getAllPersistedShortcuts();
+ assertNotNull(shortcuts);
+ assertEquals(6, shortcuts.size());
+ shortcutIds = shortcuts.stream().map(ShortcutInfo::getId).collect(Collectors.toSet());
+ assertTrue(shortcutIds.contains("s1"));
+ assertTrue(shortcutIds.contains("s2"));
+ assertTrue(shortcutIds.contains("s3"));
+ assertTrue(shortcutIds.contains("s4"));
+ assertTrue(shortcutIds.contains("s5"));
+ assertTrue(shortcutIds.contains("s6"));
+ }
+
+ public void testRemoveDynamicShortcuts_RemovesShortcutsFromDisk() {
+ if (!mService.isAppSearchEnabled()) {
+ return;
+ }
+ setCaller(CALLING_PACKAGE_1, USER_0);
+ mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"),
+ makeShortcut("s2"),
+ makeShortcut("s3"),
+ makeShortcut("s4"),
+ makeShortcut("s5")
+ ));
+
+ // Verifies removeDynamicShortcuts removes shortcuts from persistence layer
+ mManager.removeDynamicShortcuts(list("s1"));
+ final List<ShortcutInfo> shortcuts = getAllPersistedShortcuts();
+ assertNotNull(shortcuts);
+ assertEquals(4, shortcuts.size());
+ final Set<String> shortcutIds =
+ shortcuts.stream().map(ShortcutInfo::getId).collect(Collectors.toSet());
+ assertTrue(shortcutIds.contains("s2"));
+ assertTrue(shortcutIds.contains("s3"));
+ assertTrue(shortcutIds.contains("s4"));
+ assertTrue(shortcutIds.contains("s5"));
+ }
+
+ public void testRemoveLongLivedShortcuts_RemovesShortcutsFromDisk() {
+ if (!mService.isAppSearchEnabled()) {
+ return;
+ }
+ setCaller(CALLING_PACKAGE_1, USER_0);
+ mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"),
+ makeShortcut("s2"),
+ makeShortcut("s3"),
+ makeShortcut("s4"),
+ makeShortcut("s5")
+ ));
+ mManager.removeDynamicShortcuts(list("s2"));
+ final List<ShortcutInfo> shortcuts = getAllPersistedShortcuts();
+ assertNotNull(shortcuts);
+ assertEquals(4, shortcuts.size());
+ final Set<String> shortcutIds =
+ shortcuts.stream().map(ShortcutInfo::getId).collect(Collectors.toSet());
+ assertTrue(shortcutIds.contains("s1"));
+ assertTrue(shortcutIds.contains("s3"));
+ assertTrue(shortcutIds.contains("s4"));
+ assertTrue(shortcutIds.contains("s5"));
+ }
+
+ public void testDisableShortcuts_RemovesShortcutsFromDisk() {
+ if (!mService.isAppSearchEnabled()) {
+ return;
+ }
+ setCaller(CALLING_PACKAGE_1, USER_0);
+ mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"),
+ makeShortcut("s2"),
+ makeShortcut("s3"),
+ makeShortcut("s4"),
+ makeShortcut("s5")
+ ));
+ // Verifies disableShortcuts removes shortcuts from persistence layer
+ mManager.disableShortcuts(list("s3"));
+ final List<ShortcutInfo> shortcuts = getAllPersistedShortcuts();
+ assertNotNull(shortcuts);
+ assertEquals(4, shortcuts.size());
+ final Set<String> shortcutIds =
+ shortcuts.stream().map(ShortcutInfo::getId).collect(Collectors.toSet());
+ assertTrue(shortcutIds.contains("s1"));
+ assertTrue(shortcutIds.contains("s2"));
+ assertTrue(shortcutIds.contains("s4"));
+ assertTrue(shortcutIds.contains("s5"));
+ }
+
+ public void testUpdateShortcuts_UpdateShortcutsOnDisk() {
+ if (!mService.isAppSearchEnabled()) {
+ return;
+ }
+ setCaller(CALLING_PACKAGE_1, USER_0);
+ mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"),
+ makeShortcut("s2"),
+ makeShortcut("s3"),
+ makeShortcut("s4"),
+ makeShortcut("s5")
+ ));
+ // Verifies disableShortcuts removes shortcuts from persistence layer
+ mManager.updateShortcuts(list(makeShortcutWithShortLabel("s3", "custom")));
+ final List<ShortcutInfo> shortcuts = getAllPersistedShortcuts();
+ assertNotNull(shortcuts);
+ assertEquals(5, shortcuts.size());
+ final Map<String, ShortcutInfo> map = shortcuts.stream()
+ .collect(Collectors.toMap(ShortcutInfo::getId, Function.identity()));
+ assertTrue(map.containsKey("s3"));
+ assertEquals("custom", map.get("s3").getShortLabel());
+ }
+
+ private List<ShortcutInfo> getAllPersistedShortcuts() {
+ try {
+ SystemClock.sleep(500);
+ final AndroidFuture<List<ShortcutInfo>> future = new AndroidFuture<>();
+ getPersistedShortcut(future);
+ return future.get(10, TimeUnit.SECONDS);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index ea46eab..d593e80 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -32,7 +32,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
@@ -40,6 +39,7 @@
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.after;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -48,6 +48,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.KeyguardManager;
import android.app.Notification;
@@ -73,7 +74,6 @@
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Slog;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityManager;
@@ -102,6 +102,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
+@SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service.
public class BuzzBeepBlinkTest extends UiServiceTestCase {
@Mock AudioManager mAudioManager;
@@ -156,6 +157,7 @@
when(mAudioManager.getRingtonePlayer()).thenReturn(mRingtonePlayer);
when(mAudioManager.getStreamVolume(anyInt())).thenReturn(10);
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
+ when(mAudioManager.getFocusRampTimeMs(anyInt(), any(AudioAttributes.class))).thenReturn(50);
when(mUsageStats.isAlertRateLimited(any())).thenReturn(false);
when(mVibrator.hasFrequencyControl()).thenReturn(false);
when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(false);
@@ -444,6 +446,11 @@
timeout(MAX_VIBRATION_DELAY).times(1));
}
+ private void verifyDelayedNeverVibrate() {
+ verify(mVibrator, after(MAX_VIBRATION_DELAY).never()).vibrate(anyInt(), anyString(), any(),
+ anyString(), any(AudioAttributes.class));
+ }
+
private void verifyVibrate(ArgumentMatcher<VibrationEffect> effectMatcher,
VerificationMode verification) {
ArgumentCaptor<AudioAttributes> captor = ArgumentCaptor.forClass(AudioAttributes.class);
@@ -1588,8 +1595,51 @@
// beep wasn't reset
verifyNeverBeep();
verifyNeverVibrate();
- verify(mRingtonePlayer, never()).stopAsync();
- verify(mVibrator, never()).cancel();
+ verifyNeverStopAudio();
+ verifyNeverStopVibrate();
+ }
+
+ @Test
+ public void testRingtoneInsistentBeep_clearEffectsStopsSoundAndVibration() throws Exception {
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(Uri.fromParts("a", "b", "c"),
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ ringtoneChannel.enableVibration(true);
+ NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
+ mService.addNotification(ringtoneNotification);
+ assertFalse(mService.shouldMuteNotificationLocked(ringtoneNotification));
+ mService.buzzBeepBlinkLocked(ringtoneNotification);
+ verifyBeepLooped();
+ verifyDelayedVibrateLooped();
+
+ mService.clearSoundLocked();
+ mService.clearVibrateLocked();
+
+ verifyStopAudio();
+ verifyStopVibrate();
+ }
+
+ @Test
+ public void testRingtoneInsistentBeep_neverVibratesWhenEffectsClearedBeforeDelay()
+ throws Exception {
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(Uri.fromParts("a", "b", "c"),
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ ringtoneChannel.enableVibration(true);
+ NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
+ mService.addNotification(ringtoneNotification);
+ assertFalse(mService.shouldMuteNotificationLocked(ringtoneNotification));
+ mService.buzzBeepBlinkLocked(ringtoneNotification);
+ verifyBeepLooped();
+ verifyNeverVibrate();
+
+ mService.clearSoundLocked();
+ mService.clearVibrateLocked();
+
+ verifyStopAudio();
+ verifyDelayedNeverVibrate();
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 7237b24..31e7ad0 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -142,6 +142,7 @@
import android.graphics.Color;
import android.graphics.drawable.Icon;
import android.media.AudioManager;
+import android.media.session.MediaSession;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -8465,4 +8466,51 @@
fail("call to matchesCallFilter with listener permissions should work");
}
}
+
+ @Test
+ public void testMediaNotificationsBypassBlock() throws Exception {
+ when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
+ .thenReturn(FLAG_MUTABLE | FLAG_ONE_SHOT);
+ when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+
+ Notification.Builder nb = new Notification.Builder(
+ mContext, mTestNotificationChannel.getId())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .addAction(new Notification.Action.Builder(null, "test", null).build());
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+ NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ mBinderService.setNotificationsEnabledForPackage(
+ r.getSbn().getPackageName(), r.getUid(), false);
+
+ // normal blocked notifications - blocked
+ assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
+ r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
+
+ // just using the style - blocked
+ nb.setStyle(new Notification.MediaStyle());
+ sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+ r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
+ r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
+
+ // style + media session - bypasses block
+ nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class)));
+ sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+ r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
+ r.getSbn().getId(), r.getSbn().getTag(), r, false)).isTrue();
+ }
+
+ @Test
+ public void testGetAllUsersNotificationPermissions_migrationNotEnabled() {
+ // make sure we don't bother if the migration is not enabled
+ assertThat(mService.getAllUsersNotificationPermissions()).isNull();
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
index 423ba94..ea01963 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
@@ -20,9 +20,9 @@
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.NotificationManager.EXTRA_BLOCKED_STATE;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
-import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_NONE;
-import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.app.PendingIntent.FLAG_MUTABLE;
+import static android.app.PendingIntent.FLAG_ONE_SHOT;
import static android.content.pm.PackageManager.FEATURE_WATCH;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.UserHandle.USER_SYSTEM;
@@ -36,6 +36,7 @@
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -82,8 +83,10 @@
import android.content.pm.ParceledListSlice;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutServiceInternal;
+import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.media.AudioManager;
+import android.media.session.MediaSession;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
@@ -102,8 +105,10 @@
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.testing.TestablePermissions;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
+import android.util.Pair;
import androidx.test.InstrumentationRegistry;
@@ -125,7 +130,6 @@
import com.android.server.wm.WindowManagerInternal;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -711,4 +715,132 @@
assertThat(r.isImportanceFixed()).isTrue();
}
+
+ @Test
+ public void testMediaNotificationsBypassBlock() throws Exception {
+ when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
+ .thenReturn(FLAG_MUTABLE | FLAG_ONE_SHOT);
+ when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+
+ Notification.Builder nb = new Notification.Builder(
+ mContext, mTestNotificationChannel.getId())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .addAction(new Notification.Action.Builder(null, "test", null).build());
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+ NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
+
+ // normal blocked notifications - blocked
+ assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
+ r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
+
+ // just using the style - blocked
+ nb.setStyle(new Notification.MediaStyle());
+ sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+ r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
+ r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
+
+ // style + media session - bypasses block
+ nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class)));
+ sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+ r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
+ r.getSbn().getId(), r.getSbn().getTag(), r, false)).isTrue();
+ }
+
+ @Test
+ public void testMediaNotificationsBypassBlock_atPost() throws Exception {
+ when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
+ when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+
+ Notification.Builder nb = new Notification.Builder(
+ mContext, mTestNotificationChannel.getId())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .addAction(new Notification.Action.Builder(null, "test", null).build());
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+ NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ when(mPermissionHelper.hasPermission(anyInt())).thenReturn(false);
+
+ mService.addEnqueuedNotification(r);
+ NotificationManagerService.PostNotificationRunnable runnable =
+ mService.new PostNotificationRunnable(r.getKey());
+ runnable.run();
+ waitForIdle();
+
+ verify(mUsageStats).registerBlocked(any());
+ verify(mUsageStats, never()).registerPostedByApp(any());
+
+ // just using the style - blocked
+ mService.clearNotifications();
+ reset(mUsageStats);
+ nb.setStyle(new Notification.MediaStyle());
+ sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+ r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ mService.addEnqueuedNotification(r);
+ runnable = mService.new PostNotificationRunnable(r.getKey());
+ runnable.run();
+ waitForIdle();
+
+ verify(mUsageStats).registerBlocked(any());
+ verify(mUsageStats, never()).registerPostedByApp(any());
+
+ // style + media session - bypasses block
+ mService.clearNotifications();
+ reset(mUsageStats);
+ nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class)));
+ sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+ r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ mService.addEnqueuedNotification(r);
+ runnable = mService.new PostNotificationRunnable(r.getKey());
+ runnable.run();
+ waitForIdle();
+
+ verify(mUsageStats, never()).registerBlocked(any());
+ verify(mUsageStats).registerPostedByApp(any());
+ }
+
+ @Test
+ public void testGetAllUsersNotificationPermissions() {
+ // In this case, there are multiple users each with notification permissions (and also,
+ // for good measure, some without).
+ // make sure the collection returned contains info for all of them
+ final List<UserInfo> userInfos = new ArrayList<>();
+ userInfos.add(new UserInfo(0, "user0", 0));
+ userInfos.add(new UserInfo(1, "user1", 0));
+ userInfos.add(new UserInfo(2, "user2", 0));
+ when(mUm.getUsers()).thenReturn(userInfos);
+
+ // construct the permissions for each of them
+ ArrayMap<Pair<Integer, String>, Boolean> permissions0 = new ArrayMap<>(),
+ permissions1 = new ArrayMap<>();
+ permissions0.put(new Pair<>(10, "package1"), true);
+ permissions0.put(new Pair<>(20, "package2"), false);
+ permissions1.put(new Pair<>(11, "package1"), false);
+ permissions1.put(new Pair<>(21, "package2"), true);
+ when(mPermissionHelper.getNotificationPermissionValues(0)).thenReturn(permissions0);
+ when(mPermissionHelper.getNotificationPermissionValues(1)).thenReturn(permissions1);
+ when(mPermissionHelper.getNotificationPermissionValues(2)).thenReturn(new ArrayMap<>());
+
+ ArrayMap<Pair<Integer, String>, Boolean> combinedPermissions =
+ mService.getAllUsersNotificationPermissions();
+ assertTrue(combinedPermissions.get(new Pair<>(10, "package1")));
+ assertFalse(combinedPermissions.get(new Pair<>(20, "package2")));
+ assertFalse(combinedPermissions.get(new Pair<>(11, "package1")));
+ assertTrue(combinedPermissions.get(new Pair<>(21, "package2")));
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index b89a94a..dd6d469 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -34,6 +34,7 @@
import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
+import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES;
import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.CHANNEL_ID_FIELD_NUMBER;
import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.CHANNEL_NAME_FIELD_NUMBER;
import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.IMPORTANCE_FIELD_NUMBER;
@@ -97,6 +98,7 @@
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
import android.service.notification.ConversationChannelWrapper;
+import android.service.notification.nano.RankingHelperProto;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.TestableContentResolver;
import android.text.format.DateUtils;
@@ -104,20 +106,19 @@
import android.util.ArraySet;
import android.util.IntArray;
import android.util.Pair;
-import android.util.Slog;
import android.util.StatsEvent;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import android.util.Xml;
+import android.util.proto.ProtoOutputStream;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.os.AtomsProto.PackageNotificationPreferences;
import com.android.server.UiServiceTestCase;
import com.android.server.notification.PermissionHelper.PackagePermission;
-import com.google.common.collect.ImmutableMap;
-
import org.json.JSONArray;
import org.json.JSONObject;
import org.junit.Before;
@@ -130,6 +131,8 @@
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -2545,6 +2548,416 @@
}
@Test
+ public void testDumpJson_prePermissionMigration() throws Exception {
+ when(mPermissionHelper.isMigrationEnabled()).thenReturn(false);
+ // before the migration is active, we want to verify that:
+ // - all notification importance info should come from package preferences
+ // - if there are permissions granted or denied from packages PreferencesHelper doesn't
+ // know about, those are ignored if migration is not enabled
+
+ // package permissions map to be passed in
+ ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
+ appPermissions.put(new Pair(1, "first"), true); // not in local prefs
+ appPermissions.put(new Pair(3, "third"), false); // not in local prefs
+ appPermissions.put(new Pair(UID_P, PKG_P), true); // in local prefs
+ appPermissions.put(new Pair(UID_O, PKG_O), false); // in local prefs
+
+ NotificationChannel channel1 =
+ new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+ NotificationChannel channel3 = new NotificationChannel("id3", "name3", IMPORTANCE_HIGH);
+
+ mHelper.createNotificationChannel(PKG_P, UID_P, channel1, true, false);
+ mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_LOW);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, false, false);
+ mHelper.setImportance(PKG_N_MR1, UID_N_MR1, IMPORTANCE_NONE);
+ mHelper.createNotificationChannel(PKG_O, UID_O, getChannel(), true, false);
+ mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
+
+ // in the json array, all of the individual package preferences are simply elements in the
+ // values array. this set is to collect expected outputs for each of our packages.
+ // the key/value pairs are: (userId, package name) -> expected importance
+ ArrayMap<Pair<Integer, String>, String> expected = new ArrayMap<>();
+ expected.put(new Pair(UserHandle.getUserId(UID_P), PKG_P), "LOW");
+ expected.put(new Pair(UserHandle.getUserId(UID_O), PKG_O), "HIGH");
+ expected.put(new Pair(UserHandle.getUserId(UID_N_MR1), PKG_N_MR1), "NONE");
+
+ JSONArray actual = (JSONArray) mHelper.dumpJson(
+ new NotificationManagerService.DumpFilter(), appPermissions)
+ .get("PackagePreferencess");
+ assertThat(actual.length()).isEqualTo(expected.size());
+ for (int i = 0; i < actual.length(); i++) {
+ JSONObject pkgInfo = actual.getJSONObject(i);
+ Pair<Integer, String> pkgKey =
+ new Pair(pkgInfo.getInt("userId"), pkgInfo.getString("packageName"));
+ assertTrue(expected.containsKey(pkgKey));
+ assertThat(pkgInfo.getString("importance")).isEqualTo(expected.get(pkgKey));
+ }
+
+ // also make sure that (more likely to actually happen) if we don't provide an array of
+ // app preferences (and do null instead), the same thing happens, so do the same checks
+ JSONArray actualWithNullInput = (JSONArray) mHelper.dumpJson(
+ new NotificationManagerService.DumpFilter(), null)
+ .get("PackagePreferencess");
+ assertThat(actualWithNullInput.length()).isEqualTo(expected.size());
+ for (int i = 0; i < actualWithNullInput.length(); i++) {
+ JSONObject pkgInfo = actualWithNullInput.getJSONObject(i);
+ Pair<Integer, String> pkgKey =
+ new Pair(pkgInfo.getInt("userId"), pkgInfo.getString("packageName"));
+ assertTrue(expected.containsKey(pkgKey));
+ assertThat(pkgInfo.getString("importance")).isEqualTo(expected.get(pkgKey));
+ }
+ }
+
+ @Test
+ public void testDumpJson_postPermissionMigration() throws Exception {
+ when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+ // when getting a json dump, we want to verify that:
+ // - all notification importance info should come from the permission, even if the data
+ // isn't there yet but is present in package preferences
+ // - if there are permissions granted or denied from packages PreferencesHelper doesn't
+ // know about, those should still be included
+
+ // package permissions map to be passed in
+ ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
+ appPermissions.put(new Pair(1, "first"), true); // not in local prefs
+ appPermissions.put(new Pair(3, "third"), false); // not in local prefs
+ appPermissions.put(new Pair(UID_P, PKG_P), true); // in local prefs
+ appPermissions.put(new Pair(UID_O, PKG_O), false); // in local prefs
+
+ NotificationChannel channel1 =
+ new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+ NotificationChannel channel2 =
+ new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+ NotificationChannel channel3 = new NotificationChannel("id3", "name3", IMPORTANCE_HIGH);
+
+ mHelper.createNotificationChannel(PKG_P, UID_P, channel1, true, false);
+ mHelper.createNotificationChannel(PKG_P, UID_P, channel2, false, false);
+ mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_LOW);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, false, false);
+ mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE);
+ mHelper.createNotificationChannel(PKG_O, UID_O, getChannel(), true, false);
+ mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
+
+ // in the json array, all of the individual package preferences are simply elements in the
+ // values array. this set is to collect expected outputs for each of our packages.
+ // the key/value pairs are: (userId, package name) -> expected importance
+ ArrayMap<Pair<Integer, String>, String> expected = new ArrayMap<>();
+
+ // packages that only exist via the app permissions; should be present
+ expected.put(new Pair(UserHandle.getUserId(1), "first"), "DEFAULT");
+ expected.put(new Pair(UserHandle.getUserId(3), "third"), "NONE");
+
+ // packages that exist in both app permissions & local preferences
+ expected.put(new Pair(UserHandle.getUserId(UID_P), PKG_P), "DEFAULT");
+ expected.put(new Pair(UserHandle.getUserId(UID_O), PKG_O), "NONE");
+
+ // package that only exists in local preferences; expect no importance output
+ expected.put(new Pair(UserHandle.getUserId(UID_N_MR1), PKG_N_MR1), null);
+
+ JSONArray actual = (JSONArray) mHelper.dumpJson(
+ new NotificationManagerService.DumpFilter(), appPermissions)
+ .get("PackagePreferencess");
+ assertThat(actual.length()).isEqualTo(expected.size());
+ for (int i = 0; i < actual.length(); i++) {
+ JSONObject pkgInfo = actual.getJSONObject(i);
+ Pair<Integer, String> pkgKey =
+ new Pair(pkgInfo.getInt("userId"), pkgInfo.getString("packageName"));
+ assertTrue(expected.containsKey(pkgKey));
+ if (pkgInfo.has("importance")) {
+ assertThat(pkgInfo.getString("importance")).isEqualTo(expected.get(pkgKey));
+ } else {
+ assertThat(expected.get(pkgKey)).isNull();
+ }
+ }
+ }
+
+ @Test
+ public void testDumpJson_givenNullInput_postMigration() throws Exception {
+ // simple test just to make sure nothing dies if we pass in null input even post migration
+ // for some reason, even though in practice this should not be how one calls this method
+ when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+
+ // some packages exist, with some importance info that won't be looked at
+ mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
+ mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE);
+
+ JSONArray actual = (JSONArray) mHelper.dumpJson(
+ new NotificationManagerService.DumpFilter(), null)
+ .get("PackagePreferencess");
+
+ // there should still be info for the packages
+ assertThat(actual.length()).isEqualTo(2);
+
+ // but they should not have importance info because the migration is enabled and it got
+ // no info
+ for (int i = 0; i < actual.length(); i++) {
+ assertFalse(actual.getJSONObject(i).has("importance"));
+ }
+ }
+
+ @Test
+ public void testDumpBansJson_prePermissionMigration() throws Exception {
+ // confirm that the package bans that are in json are only from package preferences, and
+ // not from the passed-in permissions map
+ when(mPermissionHelper.isMigrationEnabled()).thenReturn(false);
+
+ ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
+ appPermissions.put(new Pair(1, "first"), true); // not in local prefs
+ appPermissions.put(new Pair(3, "third"), false); // not in local prefs
+ appPermissions.put(new Pair(UID_O, PKG_O), false); // in local prefs
+
+ // package preferences: only PKG_P is banned
+ mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
+ mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE);
+
+ // make sure that's the only thing in the package ban output
+ JSONArray actual = mHelper.dumpBansJson(
+ new NotificationManagerService.DumpFilter(), appPermissions);
+ assertThat(actual.length()).isEqualTo(1);
+
+ JSONObject ban = actual.getJSONObject(0);
+ assertThat(ban.getInt("userId")).isEqualTo(UserHandle.getUserId(UID_P));
+ assertThat(ban.getString("packageName")).isEqualTo(PKG_P);
+ }
+
+ @Test
+ public void testDumpBansJson_postPermissionMigration() throws Exception {
+ // confirm that the package bans that are in the output include all packages that
+ // have their permission set to false, and not based on PackagePreferences importance
+ when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+
+ ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
+ appPermissions.put(new Pair(1, "first"), true); // not in local prefs
+ appPermissions.put(new Pair(3, "third"), false); // not in local prefs
+ appPermissions.put(new Pair(UID_O, PKG_O), false); // in local prefs
+
+ // package preferences: PKG_O not banned based on local importance, and PKG_P is
+ mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
+ mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE);
+
+ // expected output
+ ArraySet<Pair<Integer, String>> expected = new ArraySet<>();
+ expected.add(new Pair(UserHandle.getUserId(3), "third"));
+ expected.add(new Pair(UserHandle.getUserId(UID_O), PKG_O));
+
+ // make sure that's the only thing in the package ban output
+ JSONArray actual = mHelper.dumpBansJson(
+ new NotificationManagerService.DumpFilter(), appPermissions);
+ assertThat(actual.length()).isEqualTo(expected.size());
+
+ for (int i = 0; i < actual.length(); i++) {
+ JSONObject ban = actual.getJSONObject(i);
+ assertTrue(expected.contains(
+ new Pair(ban.getInt("userId"), ban.getString("packageName"))));
+ }
+ }
+
+ @Test
+ public void testDumpBansJson_givenNullInput() throws Exception {
+ // no one should do this, but...
+ when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+
+ mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
+ mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE);
+
+ JSONArray actual = mHelper.dumpBansJson(
+ new NotificationManagerService.DumpFilter(), null);
+ assertThat(actual.length()).isEqualTo(0);
+ }
+
+ @Test
+ public void testDumpString_prePermissionMigration() {
+ // confirm that the string resulting from dumpImpl contains only info from package prefs
+ when(mPermissionHelper.isMigrationEnabled()).thenReturn(false);
+
+ ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
+ appPermissions.put(new Pair(1, "first"), true); // not in local prefs
+ appPermissions.put(new Pair(3, "third"), false); // not in local prefs
+ appPermissions.put(new Pair(UID_O, PKG_O), false); // in local prefs
+
+ // local package preferences: PKG_O is not banned even though the permissions would
+ // indicate so
+ mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
+ mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE);
+
+ // get dump output as a string so we can inspect the contents later
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ mHelper.dump(pw, "", new NotificationManagerService.DumpFilter(), appPermissions);
+ pw.flush();
+ String actual = sw.toString();
+
+ // expected (substring) output for each preference
+ ArrayList<String> expected = new ArrayList<>();
+ expected.add(PKG_O + " (" + UID_O + ") importance=HIGH");
+ expected.add(PKG_P + " (" + UID_P + ") importance=NONE");
+
+ // make sure the things in app permissions do NOT show up
+ ArrayList<String> notExpected = new ArrayList<>();
+ notExpected.add("first (1) importance=DEFAULT");
+ notExpected.add("third (3) importance=NONE");
+
+ for (String exp : expected) {
+ assertTrue(actual.contains(exp));
+ }
+
+ for (String notExp : notExpected) {
+ assertFalse(actual.contains(notExp));
+ }
+
+ // also make sure it works the same if we pass in a null input
+ StringWriter sw2 = new StringWriter();
+ PrintWriter pw2 = new PrintWriter(sw2);
+ mHelper.dump(pw2, "", new NotificationManagerService.DumpFilter(), null);
+ pw.flush();
+ String actualWithNullInput = sw2.toString();
+ assertThat(actualWithNullInput).isEqualTo(actual);
+ }
+
+ @Test
+ public void testDumpString_postPermissionMigration() {
+ // confirm that the string resulting from dumpImpl contains only importances from permission
+ when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+
+ ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
+ appPermissions.put(new Pair(1, "first"), true); // not in local prefs
+ appPermissions.put(new Pair(3, "third"), false); // not in local prefs
+ appPermissions.put(new Pair(UID_O, PKG_O), false); // in local prefs
+
+ // local package preferences
+ mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
+ mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE);
+
+ // get dump output as a string so we can inspect the contents later
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ mHelper.dump(pw, "", new NotificationManagerService.DumpFilter(), appPermissions);
+ pw.flush();
+ String actual = sw.toString();
+
+ // expected (substring) output for each preference via permissions
+ ArrayList<String> expected = new ArrayList<>();
+ expected.add("first (1) importance=DEFAULT");
+ expected.add("third (3) importance=NONE");
+ expected.add(PKG_O + " (" + UID_O + ") importance=NONE");
+ expected.add(PKG_P + " (" + UID_P + ")");
+
+ // make sure we don't have package preference info
+ ArrayList<String> notExpected = new ArrayList<>();
+ notExpected.add(PKG_O + " (" + UID_O + ") importance=HIGH");
+ notExpected.add(PKG_P + " (" + UID_P + ") importance="); // no importance for PKG_P
+
+ for (String exp : expected) {
+ assertTrue(actual.contains(exp));
+ }
+
+ for (String notExp : notExpected) {
+ assertFalse(actual.contains(notExp));
+ }
+ }
+
+ @Test
+ public void testDumpString_givenNullInput() {
+ // test that this doesn't choke on null input
+ when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+
+ // local package preferences
+ mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
+ mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE);
+
+ // get dump output
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ mHelper.dump(pw, "", new NotificationManagerService.DumpFilter(), null);
+ pw.flush();
+ String actual = sw.toString();
+
+ // nobody gets any importance
+ assertFalse(actual.contains("importance="));
+ }
+
+ @Test
+ public void testDumpProto_prePermissionMigration() throws Exception {
+ // test that dumping to proto gets the importances from the right place
+ when(mPermissionHelper.isMigrationEnabled()).thenReturn(false);
+
+ ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
+ appPermissions.put(new Pair(1, "first"), true); // not in local prefs
+ appPermissions.put(new Pair(3, "third"), false); // not in local prefs
+ appPermissions.put(new Pair(UID_O, PKG_O), false); // in local prefs
+
+ // local package preferences
+ mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
+ mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE);
+
+ // expected output: only the local preferences
+ // map format: (uid, package name) -> importance (int)
+ ArrayMap<Pair<Integer, String>, Integer> expected = new ArrayMap<>();
+ expected.put(new Pair(UID_O, PKG_O), IMPORTANCE_HIGH);
+ expected.put(new Pair(UID_P, PKG_P), IMPORTANCE_NONE);
+
+ // get the proto output and inspect its contents
+ ProtoOutputStream proto = new ProtoOutputStream();
+ mHelper.dump(proto, new NotificationManagerService.DumpFilter(), appPermissions);
+
+ RankingHelperProto actual = RankingHelperProto.parseFrom(proto.getBytes());
+ assertThat(actual.records.length).isEqualTo(expected.size());
+ for (int i = 0; i < actual.records.length; i++) {
+ RankingHelperProto.RecordProto record = actual.records[i];
+ Pair<Integer, String> pkgKey = new Pair(record.uid, record.package_);
+ assertTrue(expected.containsKey(pkgKey));
+ assertThat(record.importance).isEqualTo(expected.get(pkgKey));
+ }
+
+ // also check that it's the same as passing in null input
+ ProtoOutputStream proto2 = new ProtoOutputStream();
+ mHelper.dump(proto2, new NotificationManagerService.DumpFilter(), null);
+ assertThat(proto.getBytes()).isEqualTo(proto2.getBytes());
+ }
+
+ @Test
+ public void testDumpProto_postPermissionMigration() throws Exception {
+ // test that dumping to proto gets the importances from the right place
+ when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+
+ // permissions -- these should take precedence
+ ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
+ appPermissions.put(new Pair(1, "first"), true); // not in local prefs
+ appPermissions.put(new Pair(3, "third"), false); // not in local prefs
+ appPermissions.put(new Pair(UID_O, PKG_O), false); // in local prefs
+
+ // local package preferences
+ mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
+ mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_LOW);
+
+ // expected output: all the packages, but only the ones provided via appPermissions
+ // should have importance set (aka not PKG_P)
+ // map format: (uid, package name) -> importance (int)
+ ArrayMap<Pair<Integer, String>, Integer> expected = new ArrayMap<>();
+ expected.put(new Pair(1, "first"), IMPORTANCE_DEFAULT);
+ expected.put(new Pair(3, "third"), IMPORTANCE_NONE);
+ expected.put(new Pair(UID_O, PKG_O), IMPORTANCE_NONE);
+
+ // unfortunately, due to how nano protos work, there's no distinction between unset
+ // fields and default-value fields, so we have no choice here but to check for a value of 0.
+ // at least we can make sure the local importance for PKG_P in this test is not 0 (NONE).
+ expected.put(new Pair(UID_P, PKG_P), 0);
+
+ // get the proto output and inspect its contents
+ ProtoOutputStream proto = new ProtoOutputStream();
+ mHelper.dump(proto, new NotificationManagerService.DumpFilter(), appPermissions);
+
+ RankingHelperProto actual = RankingHelperProto.parseFrom(proto.getBytes());
+ assertThat(actual.records.length).isEqualTo(expected.size());
+ for (int i = 0; i < actual.records.length; i++) {
+ RankingHelperProto.RecordProto record = actual.records[i];
+ Pair<Integer, String> pkgKey = new Pair(record.uid, record.package_);
+ assertTrue(expected.containsKey(pkgKey));
+ assertThat(record.importance).isEqualTo(expected.get(pkgKey));
+ }
+ }
+
+ @Test
public void testBadgingOverrideTrue() throws Exception {
Secure.putIntForUser(getContext().getContentResolver(),
Secure.NOTIFICATION_BADGING, 1,
@@ -4576,6 +4989,86 @@
}
@Test
+ public void testPullPackagePreferencesStats_prePermissionMigration() {
+ when(mPermissionHelper.isMigrationEnabled()).thenReturn(false);
+
+ // build a collection of app permissions that should be passed in but ignored
+ ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
+ appPermissions.put(new Pair(1, "first"), true); // not in local prefs
+ appPermissions.put(new Pair(3, "third"), false); // not in local prefs
+ appPermissions.put(new Pair(UID_O, PKG_O), false); // in local prefs
+
+ // package preferences: PKG_O not banned based on local importance, and PKG_P is
+ mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
+ mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE);
+
+ // expected output. format: uid -> importance, as only uid (and not package name)
+ // is in PackageNotificationPreferences
+ ArrayMap<Integer, Integer> expected = new ArrayMap<>();
+ expected.put(UID_O, IMPORTANCE_HIGH);
+ expected.put(UID_P, IMPORTANCE_NONE);
+
+ // unexpected output. these UIDs should not show up in the output at all
+ ArraySet<Integer> unexpected = new ArraySet<>();
+ unexpected.add(1);
+ unexpected.add(3);
+
+ ArrayList<StatsEvent> events = new ArrayList<>();
+ mHelper.pullPackagePreferencesStats(events, appPermissions);
+
+ for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
+ if (builder.getAtomId() == PACKAGE_NOTIFICATION_PREFERENCES) {
+ int uid = builder.getInt(PackageNotificationPreferences.UID_FIELD_NUMBER);
+
+ // this shouldn't be any of the forbidden uids
+ assertFalse(unexpected.contains(uid));
+
+ // if it's one of the expected ids, then make sure the importance matches
+ assertTrue(expected.containsKey(uid));
+ assertThat(expected.get(uid)).isEqualTo(
+ builder.getInt(PackageNotificationPreferences.IMPORTANCE_FIELD_NUMBER));
+ }
+ }
+ }
+
+ @Test
+ public void testPullPackagePreferencesStats_postPermissionMigration() {
+ when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
+
+ // build a collection of app permissions that should be passed in but ignored
+ ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
+ appPermissions.put(new Pair(1, "first"), true); // not in local prefs
+ appPermissions.put(new Pair(3, "third"), false); // not in local prefs
+ appPermissions.put(new Pair(UID_O, PKG_O), false); // in local prefs
+
+ // package preferences: PKG_O not banned based on local importance, and PKG_P is
+ mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
+ mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE);
+
+ // expected output. format: uid -> importance, as only uid (and not package name)
+ // is in PackageNotificationPreferences
+ ArrayMap<Integer, Integer> expected = new ArrayMap<>();
+ expected.put(1, IMPORTANCE_DEFAULT);
+ expected.put(3, IMPORTANCE_NONE);
+ expected.put(UID_O, IMPORTANCE_NONE); // banned by permissions
+ expected.put(UID_P, IMPORTANCE_NONE); // defaults to none
+
+ ArrayList<StatsEvent> events = new ArrayList<>();
+ mHelper.pullPackagePreferencesStats(events, appPermissions);
+
+ for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
+ if (builder.getAtomId() == PACKAGE_NOTIFICATION_PREFERENCES) {
+ int uid = builder.getInt(PackageNotificationPreferences.UID_FIELD_NUMBER);
+
+ // if it's one of the expected ids, then make sure the importance matches
+ assertTrue(expected.containsKey(uid));
+ assertThat(expected.get(uid)).isEqualTo(
+ builder.getInt(PackageNotificationPreferences.IMPORTANCE_FIELD_NUMBER));
+ }
+ }
+ }
+
+ @Test
public void testUnlockNotificationChannelImportance() {
NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, false);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index d4991fc..2954d78 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -1084,6 +1084,7 @@
@Test
@EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY,
ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
public void testOverrideMinAspectRatioScreenOrientationNotSetThenChangedToPortrait() {
// In this test, the activity's orientation isn't fixed to portrait, therefore the override
@@ -1115,6 +1116,7 @@
@Test
@EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY,
ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
public void testOverrideMinAspectRatioScreenOrientationLandscapeThenChangedToPortrait() {
// In this test, the activity's orientation isn't fixed to portrait, therefore the override
@@ -1147,6 +1149,7 @@
@Test
@EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY,
ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
public void testOverrideMinAspectRatioScreenOrientationPortraitThenChangedToUnspecified() {
setUpDisplaySizeWithApp(1000, 1200);
@@ -1175,6 +1178,52 @@
}
@Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
+ @DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY})
+ public void testOverrideMinAspectRatioPortraitOnlyDisabledScreenOrientationNotSet() {
+ setUpDisplaySizeWithApp(1000, 1200);
+
+ // Create a size compat activity on the same task.
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setUid(android.os.Process.myUid())
+ .build();
+
+ // The per-package override forces the activity into a 3:2 aspect ratio
+ assertEquals(1200, activity.getBounds().height());
+ assertEquals(1200 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE,
+ activity.getBounds().width(), 0.5);
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
+ @DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY})
+ public void testOverrideMinAspectRatioPortraitOnlyDisabledScreenOrientationLandscape() {
+ // In this test, the activity's orientation isn't fixed to portrait, therefore the override
+ // isn't applied.
+
+ setUpDisplaySizeWithApp(1000, 1200);
+
+ // Create a size compat activity on the same task.
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setUid(android.os.Process.myUid())
+ .build();
+
+ // The per-package override forces the activity into a 3:2 aspect ratio
+ assertEquals(1000 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE,
+ activity.getBounds().height(), 0.5);
+ assertEquals(1000, activity.getBounds().width());
+ }
+
+ @Test
@EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
public void testOverrideMinAspectRatioWithoutGlobalOverride() {
// In this test, only OVERRIDE_MIN_ASPECT_RATIO_1_5 is set, which has no effect without
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index cac716e..0ddd52d 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -69,7 +69,14 @@
* them know that the app has crashed and that their call was continued using the pre-loaded dialer
* app.
* <p>
- * Further, the pre-loaded dialer will ALWAYS be used when the user places an emergency call.
+ * The pre-loaded dialer will ALWAYS be used when the user places an emergency call, even if your
+ * app fills the {@link android.app.role.RoleManager#ROLE_DIALER} role. To ensure an optimal
+ * experience when placing an emergency call, the default dialer should ALWAYS use
+ * {@link android.telecom.TelecomManager#placeCall(Uri, Bundle)} to place calls (including
+ * emergency calls). This ensures that the platform is able to verify that the request came from
+ * the default dialer. If a non-preloaded dialer app uses {@link Intent#ACTION_CALL} to place an
+ * emergency call, it will be raised to the preloaded dialer app using {@link Intent#ACTION_DIAL}
+ * for confirmation; this is a suboptimal user experience.
* <p>
* Below is an example manifest registration for an {@code InCallService}. The meta-data
* {@link TelecomManager#METADATA_IN_CALL_SERVICE_UI} indicates that this particular
diff --git a/telephony/java/android/telephony/AvailableNetworkInfo.java b/telephony/java/android/telephony/AvailableNetworkInfo.java
index ae597e0..2b355ae 100644
--- a/telephony/java/android/telephony/AvailableNetworkInfo.java
+++ b/telephony/java/android/telephony/AvailableNetworkInfo.java
@@ -16,11 +16,14 @@
package android.telephony;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.RadioAccessSpecifier;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -32,7 +35,6 @@
* Network Service when passed through {@link TelephonyManager#updateAvailableNetworks}
*/
public final class AvailableNetworkInfo implements Parcelable {
-
/*
* Defines number of priority level high.
*/
@@ -48,6 +50,14 @@
*/
public static final int PRIORITY_LOW = 3;
+ /** @hide */
+ @IntDef({
+ PRIORITY_HIGH,
+ PRIORITY_MED,
+ PRIORITY_LOW,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AvailableNetworkInfoPriority {}
/**
* subscription Id of the available network. This value must be one of the entry retrieved from
* {@link SubscriptionManager#getOpportunisticSubscriptions}
@@ -62,7 +72,7 @@
* for network selection. If there are more than one subId with highest priority then the
* network with highest RSRP is chosen.
*/
- private int mPriority;
+ private @AvailableNetworkInfoPriority int mPriority;
/**
* Describes the List of PLMN ids (MCC-MNC) associated with mSubId.
@@ -77,8 +87,7 @@
* Opportunistic network service will use these bands to scan.
*
* When no specific bands are specified (empty array or null) CBRS band
- * {@link AccessNetworkConstants.EutranBand.BAND_48
- * } will be used for network scan.
+ * {@link AccessNetworkConstants.EutranBand.BAND_48} will be used for network scan.
*
* See {@link AccessNetworkConstants} for details.
*
@@ -94,7 +103,7 @@
* If this entry is left empty, {@link RadioAcccessSpecifier}s with {@link AccessNetworkType}s
* of {@link AccessNetworkConstants.AccessNetworkType.EUTRAN} and {@link
* AccessNetworkConstants.AccessNetworkType.NGRAN} with bands 48 and 71 on each will be assumed
- * by Opportunistic network service.
+ * by Opportunistic network service for a network scan.
*/
private ArrayList<RadioAccessSpecifier> mRadioAccessSpecifiers;
@@ -117,6 +126,7 @@
* network with highest RSRP is chosen.
* @return priority level
*/
+ @AvailableNetworkInfoPriority
public int getPriority() {
return mPriority;
}
@@ -149,15 +159,9 @@
* Returns a list of {@link RadioAccessSpecifier} associated with the available network.
* Opportunistic network service will use this to determine which bands to scan for.
*
- * the returned value is one of {@link AccessNetworkConstants.AccessNetworkType}. When no
- * specific access network type is specified, {@link RadioAccessSpecifier}s with {@link
- * AccessNetworkType}s of {@link AccessNetworkConstants.AccessNetworkType.EUTRAN} and {@link
- * AccessNetworkConstants.AccessNetworkType.NGRAN} with bands 48 and 71 on each will be assumed
- * by Opportunistic network service.
* @return the access network type associated with the available network.
- * @hide
*/
- public List<RadioAccessSpecifier> getRadioAccessSpecifiers() {
+ public @NonNull List<RadioAccessSpecifier> getRadioAccessSpecifiers() {
return (List<RadioAccessSpecifier>) mRadioAccessSpecifiers.clone();
}
@@ -193,9 +197,9 @@
}
/** @hide */
- private AvailableNetworkInfo(int subId, int priority, @NonNull List<String> mccMncs,
- @NonNull List<Integer> bands, @NonNull List<RadioAccessSpecifier>
- radioAccessSpecifiers) {
+ private AvailableNetworkInfo(int subId, @AvailableNetworkInfoPriority int priority,
+ @NonNull List<String> mccMncs, @NonNull List<Integer> bands,
+ @NonNull List<RadioAccessSpecifier> radioAccessSpecifiers) {
mSubId = subId;
mPriority = priority;
mMccMncs = new ArrayList<String>(mccMncs);
@@ -261,27 +265,39 @@
*
* <pre><code>
*
- * AvailableNetworkInfo aNI = new AvailableNetworkInfo.Builder()
- * .setSubId(1)
+ * AvailableNetworkInfo aNI = new AvailableNetworkInfo.Builder(subId)
* .setPriority(AvailableNetworkInfo.PRIORITY_MED)
+ * .setRadioAccessSpecifiers(radioAccessSpecifiers)
+ * .setMccMncs(mccMncs)
* .build();
* </code></pre>
- *
- * @hide
*/
public static final class Builder {
private int mSubId = Integer.MIN_VALUE;
- private int mPriority = AvailableNetworkInfo.PRIORITY_LOW;
+ private @AvailableNetworkInfoPriority int mPriority = AvailableNetworkInfo.PRIORITY_LOW;
private ArrayList<String> mMccMncs = new ArrayList<>();
- private ArrayList<Integer> mBands = new ArrayList<>();
private ArrayList<RadioAccessSpecifier> mRadioAccessSpecifiers = new ArrayList<>();
- public @NonNull Builder setSubId(int subId) {
+ /**
+ *
+ */
+ /**
+ * Creates an AvailableNetworkInfo Builder with specified subscription id.
+ *
+ * @param subId of the availableNetwork.
+ */
+ public Builder(int subId) {
mSubId = subId;
- return this;
}
- public @NonNull Builder setPriority(int priority) {
+ /**
+ * Sets the priority for the subscription id.
+ *
+ * @param priority of the subscription id. See {@link AvailableNetworkInfo#getPriority} for
+ * more details
+ * @return the original Builder object.
+ */
+ public @NonNull Builder setPriority(@AvailableNetworkInfoPriority int priority) {
if (priority > AvailableNetworkInfo.PRIORITY_LOW
|| priority < AvailableNetworkInfo.PRIORITY_HIGH) {
throw new IllegalArgumentException("A valid priority must be set");
@@ -290,30 +306,48 @@
return this;
}
- public @NonNull Builder setMccMncs(@NonNull ArrayList<String> mccMncs) {
- Objects.requireNonNull(mccMncs, "A non-null ArrayList of mccmncs must be set. An empty "
- + "list is still accepted. Please read documentation in "
- + "AvailableNetworkService to see consequences of an empty Arraylist.");
- mMccMncs = mccMncs;
+ /**
+ * Sets the list of mccmncs associated with the subscription id.
+ *
+ * @param mccMncs nonull list of mccmncs. An empty List is still accepted. Please read
+ * documentation in {@link AvailableNetworkInfo} to see consequences of an empty List.
+ * @return the original Builder object.
+ */
+ public @NonNull Builder setMccMncs(@NonNull List<String> mccMncs) {
+ Objects.requireNonNull(mccMncs, "A non-null List of mccmncs must be set. An empty "
+ + "List is still accepted. Please read documentation in "
+ + "AvailableNetworkInfo to see consequences of an empty List.");
+ mMccMncs = new ArrayList<>(mccMncs);
return this;
}
+ /**
+ * Sets the list of mccmncs associated with the subscription id.
+ *
+ * @param radioAccessSpecifiers nonull list of radioAccessSpecifiers. An empty List is still
+ * accepted. Please read documentation in {@link AvailableNetworkInfo} to see
+ * consequences of an empty List.
+ * @return the original Builder object.
+ */
public @NonNull Builder setRadioAccessSpecifiers(
- @NonNull ArrayList<RadioAccessSpecifier> radioAccessSpecifiers) {
- Objects.requireNonNull(radioAccessSpecifiers, "A non-null ArrayList of "
- + "RadioAccessSpecifiers must be set. An empty list is still accepted. Please "
- + "read documentation in AvailableNetworkService to see consequences of an "
- + "empty Arraylist.");
- mRadioAccessSpecifiers = radioAccessSpecifiers;
+ @NonNull List<RadioAccessSpecifier> radioAccessSpecifiers) {
+ Objects.requireNonNull(radioAccessSpecifiers, "A non-null List of "
+ + "RadioAccessSpecifiers must be set. An empty List is still accepted. Please "
+ + "read documentation in AvailableNetworkInfo to see consequences of an "
+ + "empty List.");
+ mRadioAccessSpecifiers = new ArrayList<>(radioAccessSpecifiers);
return this;
}
+ /**
+ * @return an AvailableNetworkInfo object with all the fields previously set by the Builder.
+ */
public @NonNull AvailableNetworkInfo build() {
if (mSubId == Integer.MIN_VALUE) {
throw new IllegalArgumentException("A valid subId must be set");
}
- return new AvailableNetworkInfo(mSubId, mPriority, mMccMncs, mBands,
+ return new AvailableNetworkInfo(mSubId, mPriority, mMccMncs, new ArrayList<>(),
mRadioAccessSpecifiers);
}
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 3e9ae38..2dfa9a45 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3697,6 +3697,49 @@
"show_wifi_calling_icon_in_status_bar_bool";
/**
+ * Configuration to indicate that the carrier supports opportunistic data
+ * auto provisioning. Based on this flag, the device downloads and activates
+ * corresponding opportunistic profile.
+ */
+ public static final String KEY_CARRIER_SUPPORTS_OPP_DATA_AUTO_PROVISIONING_BOOL =
+ "carrier_supports_opp_data_auto_provisioning_bool";
+
+ /**
+ * SMDP+ server address for downloading opportunistic eSIM profile.
+ * FQDN (Fully Qualified Domain Name) of the SM-DP+ (e.g., smdp.gsma.com) restricted to the
+ * Alphanumeric mode character set defined in table 5 of ISO/IEC 18004 [15] excluding '$'.
+ */
+ public static final String KEY_SMDP_SERVER_ADDRESS_STRING =
+ "smdp_server_address_string";
+
+ /**
+ * This timer value is used in the eSIM Exponential Backoff download retry algorithm.
+ * Value should be in seconds.
+ * <OL>
+ * <LI>When the first download failure occurs, retry download after BACKOFF_TIMER_VALUE
+ * seconds.</LI>
+ *
+ * <LI>If download fails again then, retry after either BACKOFF_TIMER_VALUE,
+ * 2xBACKOFF_TIMER_VALUE, or 3xBACKOFF_TIMER_VALUE seconds.</LI>
+ *
+ * <LI>In general after the cth failed attempt, retry after k * BACKOFF_TIMER_VALUE
+ * seconds, where k is a random integer between 1 and 2^c − 1. Max c value is
+ * {@link #KEY_ESIM_MAX_DOWNLOAD_RETRY_ATTEMPTS_INT}</LI>
+ * </OL>
+ */
+ public static final String KEY_ESIM_DOWNLOAD_RETRY_BACKOFF_TIMER_SEC_INT =
+ "esim_download_retry_backoff_timer_sec_int";
+
+ /**
+ * If eSIM profile download fails then, the number of retry attempts by UE
+ * will be based on this configuration. If download still fails even after the
+ * MAX attempts configured by this item then the retry is postponed until next
+ * device bootup.
+ */
+ public static final String KEY_ESIM_MAX_DOWNLOAD_RETRY_ATTEMPTS_INT =
+ "esim_max_download_retry_attempts_int";
+
+ /**
* Controls RSRP threshold at which OpportunisticNetworkService will decide whether
* the opportunistic network is good enough for internet data.
*/
@@ -3906,6 +3949,30 @@
public static final String KEY_ENABLE_4G_OPPORTUNISTIC_NETWORK_SCAN_BOOL =
"enabled_4g_opportunistic_network_scan_bool";
+ /**
+ * Only relevant when the device supports opportunistic networks but does not support
+ * simultaneuous 5G+5G. Controls how long, in milliseconds, to wait before opportunistic network
+ * goes out of service before switching the 5G capability back to primary stack. The idea of
+ * waiting a few seconds is to minimize the calling of the expensive capability switching
+ * operation in the case where CBRS goes back into service shortly after going out of it.
+ *
+ * @hide
+ */
+ public static final String KEY_TIME_TO_SWITCH_BACK_TO_PRIMARY_IF_OPPORTUNISTIC_OOS_LONG =
+ "time_to_switch_back_to_primary_if_opportunistic_oos_long";
+
+ /**
+ * Only relevant when the device supports opportunistic networks but does not support
+ * simultaneuous 5G+5G. Controls how long, in milliseconds, after 5G capability has switched back
+ * to primary stack due to opportunistic network being OOS. The idea is to minimizing the
+ * 'ping-ponging' effect where device is constantly witching capability back and forth between
+ * primary and opportunistic stack.
+ *
+ * @hide
+ */
+ public static final String KEY_OPPORTUNISTIC_TIME_TO_SCAN_AFTER_CAPABILITY_SWITCH_TO_PRIMARY_LONG
+ = "opportunistic_time_to_scan_after_capability_switch_to_primary_long";
+
/**
* Indicates zero or more emergency number prefix(es), because some carrier requires
* if users dial an emergency number address with a specific prefix, the combination of the
@@ -5781,6 +5848,10 @@
sDefaults.putBoolean(KEY_UNMETERED_NR_SA_SUB6_BOOL, false);
sDefaults.putBoolean(KEY_ASCII_7_BIT_SUPPORT_FOR_LONG_MESSAGE_BOOL, false);
sDefaults.putBoolean(KEY_SHOW_WIFI_CALLING_ICON_IN_STATUS_BAR_BOOL, false);
+ sDefaults.putBoolean(KEY_CARRIER_SUPPORTS_OPP_DATA_AUTO_PROVISIONING_BOOL, false);
+ sDefaults.putString(KEY_SMDP_SERVER_ADDRESS_STRING, "");
+ sDefaults.putInt(KEY_ESIM_MAX_DOWNLOAD_RETRY_ATTEMPTS_INT, 5);
+ sDefaults.putInt(KEY_ESIM_DOWNLOAD_RETRY_BACKOFF_TIMER_SEC_INT, 60);
/* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_GOOD */
sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT, -108);
/* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_MODERATE */
@@ -5824,6 +5895,10 @@
/* Default value is 2 seconds. */
sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG, 2000);
sDefaults.putBoolean(KEY_ENABLE_4G_OPPORTUNISTIC_NETWORK_SCAN_BOOL, true);
+ sDefaults.putInt(KEY_TIME_TO_SWITCH_BACK_TO_PRIMARY_IF_OPPORTUNISTIC_OOS_LONG, 60000);
+ sDefaults.putInt(
+ KEY_OPPORTUNISTIC_TIME_TO_SCAN_AFTER_CAPABILITY_SWITCH_TO_PRIMARY_LONG,
+ 120000);
sDefaults.putAll(Gps.getDefaults());
sDefaults.putIntArray(KEY_CDMA_ENHANCED_ROAMING_INDICATOR_FOR_HOME_NETWORK_INT_ARRAY,
new int[] {
diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp
index 5d2eda3..a266b47 100644
--- a/tools/aapt2/util/Files.cpp
+++ b/tools/aapt2/util/Files.cpp
@@ -154,7 +154,7 @@
const char* end = path.end();
const char* last_dir_sep = path.begin();
for (const char* c = path.begin(); c != end; ++c) {
- if (*c == sDirSep) {
+ if (*c == sDirSep || *c == sInvariantDirSep) {
last_dir_sep = c + 1;
}
}
diff --git a/tools/aapt2/util/Files.h b/tools/aapt2/util/Files.h
index 877cd56..a2b1b58 100644
--- a/tools/aapt2/util/Files.h
+++ b/tools/aapt2/util/Files.h
@@ -41,6 +41,8 @@
constexpr const char sPathSep = ':';
#endif
+constexpr const char sInvariantDirSep = '/';
+
enum class FileType {
kUnknown = 0,
kNonExistant,