Merge "freezer: synchronous unfreeze" into rvc-dev
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
index f53f1f1..d1e28e9 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
@@ -145,6 +145,9 @@
/** @hide */
public static final int INVALID_RES_ID = -1;
+ /** @hide */
+ public static final String DESC_RES_TYPE_STRING = "string";
+
private final Context mContext;
private final IBlobStoreManager mService;
@@ -269,6 +272,9 @@
* <p> When an app acquires a lease on a blob, the System will try to keep this
* blob around but note that it can still be deleted if it was requested by the user.
*
+ * <p> In case the resource name for the {@code descriptionResId} is modified as part of
+ * an app update, apps should re-acquire the lease with the new resource id.
+ *
* @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to
* acquire a lease for.
* @param descriptionResId the resource id for a short description string that can be surfaced
@@ -380,6 +386,9 @@
* <p> When an app acquires a lease on a blob, the System will try to keep this
* blob around but note that it can still be deleted if it was requested by the user.
*
+ * <p> In case the resource name for the {@code descriptionResId} is modified as part of
+ * an app update, apps should re-acquire the lease with the new resource id.
+ *
* @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to
* acquire a lease for.
* @param descriptionResId the resource id for a short description string that can be surfaced
diff --git a/apex/blobstore/framework/java/android/app/blob/XmlTags.java b/apex/blobstore/framework/java/android/app/blob/XmlTags.java
index 9834d74..e64edc3 100644
--- a/apex/blobstore/framework/java/android/app/blob/XmlTags.java
+++ b/apex/blobstore/framework/java/android/app/blob/XmlTags.java
@@ -51,6 +51,6 @@
// For leasee
public static final String TAG_LEASEE = "l";
- public static final String ATTR_DESCRIPTION_RES_ID = "rid";
+ public static final String ATTR_DESCRIPTION_RES_NAME = "rn";
public static final String ATTR_DESCRIPTION = "d";
}
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
index 4a85a69..dab4797 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
@@ -15,8 +15,9 @@
*/
package com.android.server.blob;
+import static android.app.blob.BlobStoreManager.DESC_RES_TYPE_STRING;
import static android.app.blob.XmlTags.ATTR_DESCRIPTION;
-import static android.app.blob.XmlTags.ATTR_DESCRIPTION_RES_ID;
+import static android.app.blob.XmlTags.ATTR_DESCRIPTION_RES_NAME;
import static android.app.blob.XmlTags.ATTR_EXPIRY_TIME;
import static android.app.blob.XmlTags.ATTR_ID;
import static android.app.blob.XmlTags.ATTR_PACKAGE;
@@ -29,7 +30,9 @@
import static android.os.Process.INVALID_UID;
import static android.system.OsConstants.O_RDONLY;
+import static com.android.server.blob.BlobStoreConfig.LOGV;
import static com.android.server.blob.BlobStoreConfig.TAG;
+import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_DESC_RES_NAME;
import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_STRING_DESC;
import android.annotation.NonNull;
@@ -154,7 +157,7 @@
synchronized (mMetadataLock) {
// We need to override the leasee data, so first remove any existing
// leasee before adding the new one.
- final Leasee leasee = new Leasee(callingPackage, callingUid,
+ final Leasee leasee = new Leasee(mContext, callingPackage, callingUid,
descriptionResId, description, leaseExpiryTimeMillis);
mLeasees.remove(leasee);
mLeasees.add(leasee);
@@ -239,7 +242,7 @@
return hasOtherLeasees(null, uid);
}
- private boolean isALeasee(@Nullable String packageName, int uid) {
+ boolean isALeasee(@Nullable String packageName, int uid) {
synchronized (mMetadataLock) {
// Check if the package is a leasee of the data blob.
for (int i = 0, size = mLeasees.size(); i < size; ++i) {
@@ -459,59 +462,123 @@
}
static final class Leasee extends Accessor {
- public final int descriptionResId;
+ public final String descriptionResEntryName;
public final CharSequence description;
public final long expiryTimeMillis;
- Leasee(String packageName, int uid, int descriptionResId, CharSequence description,
- long expiryTimeMillis) {
+ Leasee(@NonNull Context context, @NonNull String packageName,
+ int uid, int descriptionResId,
+ @Nullable CharSequence description, long expiryTimeMillis) {
super(packageName, uid);
- this.descriptionResId = descriptionResId;
+ final Resources packageResources = getPackageResources(context, packageName,
+ UserHandle.getUserId(uid));
+ this.descriptionResEntryName = getResourceEntryName(packageResources, descriptionResId);
+ this.expiryTimeMillis = expiryTimeMillis;
+ this.description = description == null
+ ? getDescription(packageResources, descriptionResId)
+ : description;
+ }
+
+ Leasee(String packageName, int uid, @Nullable String descriptionResEntryName,
+ @Nullable CharSequence description, long expiryTimeMillis) {
+ super(packageName, uid);
+ this.descriptionResEntryName = descriptionResEntryName;
this.expiryTimeMillis = expiryTimeMillis;
this.description = description;
}
+ @Nullable
+ private static String getResourceEntryName(@Nullable Resources packageResources,
+ int resId) {
+ if (!ResourceId.isValid(resId) || packageResources == null) {
+ return null;
+ }
+ return packageResources.getResourceEntryName(resId);
+ }
+
+ @Nullable
+ private static String getDescription(@NonNull Context context,
+ @NonNull String descriptionResEntryName, @NonNull String packageName, int userId) {
+ if (descriptionResEntryName == null || descriptionResEntryName.isEmpty()) {
+ return null;
+ }
+ final Resources resources = getPackageResources(context, packageName, userId);
+ if (resources == null) {
+ return null;
+ }
+ try {
+ final int resId = resources.getIdentifier(descriptionResEntryName,
+ DESC_RES_TYPE_STRING, packageName);
+ return resId <= 0 ? null : resources.getString(resId);
+ } catch (Resources.NotFoundException e) {
+ if (LOGV) {
+ Slog.w(TAG, "Description resource not found", e);
+ }
+ return null;
+ }
+ }
+
+ @Nullable
+ private static String getDescription(@Nullable Resources packageResources,
+ int descriptionResId) {
+ if (!ResourceId.isValid(descriptionResId) || packageResources == null) {
+ return null;
+ }
+ return packageResources.getString(descriptionResId);
+ }
+
boolean isStillValid() {
return expiryTimeMillis == 0 || expiryTimeMillis <= System.currentTimeMillis();
}
- void dump(Context context, IndentingPrintWriter fout) {
+ void dump(@NonNull Context context, @NonNull IndentingPrintWriter fout) {
fout.println("desc: " + getDescriptionToDump(context));
fout.println("expiryMs: " + expiryTimeMillis);
}
- private String getDescriptionToDump(Context context) {
- String desc = null;
- if (ResourceId.isValid(descriptionResId)) {
- try {
- final Resources leaseeRes = context.getPackageManager()
- .getResourcesForApplicationAsUser(
- packageName, UserHandle.getUserId(uid));
- desc = leaseeRes.getString(descriptionResId);
- } catch (PackageManager.NameNotFoundException e) {
- Slog.d(TAG, "Unknown package in user " + UserHandle.getUserId(uid) + ": "
- + packageName, e);
- desc = "<none>";
- }
- } else {
+ @NonNull
+ private String getDescriptionToDump(@NonNull Context context) {
+ String desc = getDescription(context, descriptionResEntryName, packageName,
+ UserHandle.getUserId(uid));
+ if (desc == null) {
desc = description.toString();
}
- return desc;
+ return desc == null ? "<none>" : desc;
+ }
+
+ @Nullable
+ private static Resources getPackageResources(@NonNull Context context,
+ @NonNull String packageName, int userId) {
+ try {
+ return context.getPackageManager()
+ .getResourcesForApplicationAsUser(packageName, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.d(TAG, "Unknown package in user " + userId + ": "
+ + packageName, e);
+ return null;
+ }
}
void writeToXml(@NonNull XmlSerializer out) throws IOException {
XmlUtils.writeStringAttribute(out, ATTR_PACKAGE, packageName);
XmlUtils.writeIntAttribute(out, ATTR_UID, uid);
- XmlUtils.writeIntAttribute(out, ATTR_DESCRIPTION_RES_ID, descriptionResId);
+ XmlUtils.writeStringAttribute(out, ATTR_DESCRIPTION_RES_NAME, descriptionResEntryName);
XmlUtils.writeLongAttribute(out, ATTR_EXPIRY_TIME, expiryTimeMillis);
XmlUtils.writeStringAttribute(out, ATTR_DESCRIPTION, description);
}
@NonNull
- static Leasee createFromXml(@NonNull XmlPullParser in, int version) throws IOException {
+ static Leasee createFromXml(@NonNull XmlPullParser in, int version)
+ throws IOException {
final String packageName = XmlUtils.readStringAttribute(in, ATTR_PACKAGE);
final int uid = XmlUtils.readIntAttribute(in, ATTR_UID);
- final int descriptionResId = XmlUtils.readIntAttribute(in, ATTR_DESCRIPTION_RES_ID);
+ final String descriptionResEntryName;
+ if (version >= XML_VERSION_ADD_DESC_RES_NAME) {
+ descriptionResEntryName = XmlUtils.readStringAttribute(
+ in, ATTR_DESCRIPTION_RES_NAME);
+ } else {
+ descriptionResEntryName = null;
+ }
final long expiryTimeMillis = XmlUtils.readLongAttribute(in, ATTR_EXPIRY_TIME);
final CharSequence description;
if (version >= XML_VERSION_ADD_STRING_DESC) {
@@ -520,7 +587,8 @@
description = null;
}
- return new Leasee(packageName, uid, descriptionResId, description, expiryTimeMillis);
+ return new Leasee(packageName, uid, descriptionResEntryName,
+ description, expiryTimeMillis);
}
}
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
index bcc1610..5e8ea7a 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
@@ -15,12 +15,23 @@
*/
package com.android.server.blob;
+import static android.provider.DeviceConfig.NAMESPACE_BLOBSTORE;
+import static android.text.format.Formatter.FLAG_IEC_UNITS;
+import static android.text.format.Formatter.formatFileSize;
+import static android.util.TimeUtils.formatDuration;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Context;
import android.os.Environment;
+import android.provider.DeviceConfig;
+import android.provider.DeviceConfig.Properties;
+import android.util.DataUnit;
import android.util.Log;
import android.util.Slog;
+import com.android.internal.util.IndentingPrintWriter;
+
import java.io.File;
import java.util.concurrent.TimeUnit;
@@ -32,8 +43,9 @@
public static final int XML_VERSION_INIT = 1;
// Added a string variant of lease description.
public static final int XML_VERSION_ADD_STRING_DESC = 2;
+ public static final int XML_VERSION_ADD_DESC_RES_NAME = 3;
- public static final int XML_VERSION_CURRENT = XML_VERSION_ADD_STRING_DESC;
+ public static final int XML_VERSION_CURRENT = XML_VERSION_ADD_DESC_RES_NAME;
private static final String ROOT_DIR_NAME = "blobstore";
private static final String BLOBS_DIR_NAME = "blobs";
@@ -54,6 +66,76 @@
*/
public static final long SESSION_EXPIRY_TIMEOUT_MILLIS = TimeUnit.DAYS.toMillis(7);
+ public static class DeviceConfigProperties {
+ /**
+ * Denotes how low the limit for the amount of data, that an app will be allowed to acquire
+ * a lease on, can be.
+ */
+ public static final String KEY_TOTAL_BYTES_PER_APP_LIMIT_FLOOR =
+ "total_bytes_per_app_limit_floor";
+ public static final long DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FLOOR =
+ DataUnit.MEBIBYTES.toBytes(300); // 300 MiB
+ public static long TOTAL_BYTES_PER_APP_LIMIT_FLOOR =
+ DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FLOOR;
+
+ /**
+ * Denotes the maximum amount of data an app can acquire a lease on, in terms of fraction
+ * of total disk space.
+ */
+ public static final String KEY_TOTAL_BYTES_PER_APP_LIMIT_FRACTION =
+ "total_bytes_per_app_limit_fraction";
+ public static final float DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FRACTION = 0.01f;
+ public static float TOTAL_BYTES_PER_APP_LIMIT_FRACTION =
+ DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FRACTION;
+
+ static void refresh(Properties properties) {
+ if (!NAMESPACE_BLOBSTORE.equals(properties.getNamespace())) {
+ return;
+ }
+ properties.getKeyset().forEach(key -> {
+ switch (key) {
+ case KEY_TOTAL_BYTES_PER_APP_LIMIT_FLOOR:
+ TOTAL_BYTES_PER_APP_LIMIT_FLOOR = properties.getLong(key,
+ DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FLOOR);
+ break;
+ case KEY_TOTAL_BYTES_PER_APP_LIMIT_FRACTION:
+ TOTAL_BYTES_PER_APP_LIMIT_FRACTION = properties.getFloat(key,
+ DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FRACTION);
+ break;
+ default:
+ Slog.wtf(TAG, "Unknown key in device config properties: " + key);
+ }
+ });
+ }
+
+ static void dump(IndentingPrintWriter fout, Context context) {
+ final String dumpFormat = "%s: [cur: %s, def: %s]";
+ fout.println(String.format(dumpFormat, KEY_TOTAL_BYTES_PER_APP_LIMIT_FLOOR,
+ formatFileSize(context, TOTAL_BYTES_PER_APP_LIMIT_FLOOR, FLAG_IEC_UNITS),
+ formatFileSize(context, DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FLOOR,
+ FLAG_IEC_UNITS)));
+ fout.println(String.format(dumpFormat, KEY_TOTAL_BYTES_PER_APP_LIMIT_FRACTION,
+ TOTAL_BYTES_PER_APP_LIMIT_FRACTION,
+ DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FRACTION));
+ }
+ }
+
+ public static void initialize(Context context) {
+ DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_BLOBSTORE,
+ context.getMainExecutor(),
+ properties -> DeviceConfigProperties.refresh(properties));
+ DeviceConfigProperties.refresh(DeviceConfig.getProperties(NAMESPACE_BLOBSTORE));
+ }
+
+ /**
+ * Returns the maximum amount of data that an app can acquire a lease on.
+ */
+ public static long getAppDataBytesLimit() {
+ final long totalBytesLimit = (long) (Environment.getDataSystemDirectory().getTotalSpace()
+ * DeviceConfigProperties.TOTAL_BYTES_PER_APP_LIMIT_FRACTION);
+ return Math.max(DeviceConfigProperties.TOTAL_BYTES_PER_APP_LIMIT_FLOOR, totalBytesLimit);
+ }
+
@Nullable
public static File prepareBlobFile(long sessionId) {
final File blobsDir = prepareBlobsDir();
@@ -122,4 +204,21 @@
public static File getBlobStoreRootDir() {
return new File(Environment.getDataSystemDirectory(), ROOT_DIR_NAME);
}
+
+ public static void dump(IndentingPrintWriter fout, Context context) {
+ fout.println("XML current version: " + XML_VERSION_CURRENT);
+
+ fout.println("Idle job ID: " + IDLE_JOB_ID);
+ fout.println("Idle job period: " + formatDuration(IDLE_JOB_PERIOD_MILLIS));
+
+ fout.println("Session expiry timeout: " + formatDuration(SESSION_EXPIRY_TIMEOUT_MILLIS));
+
+ fout.println("Total bytes per app limit: " + formatFileSize(context,
+ getAppDataBytesLimit(), FLAG_IEC_UNITS));
+
+ fout.println("Device config properties:");
+ fout.increaseIndent();
+ DeviceConfigProperties.dump(fout, context);
+ fout.decreaseIndent();
+ }
}
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
index 49adaa8..7006781 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -54,6 +54,7 @@
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageStats;
import android.content.res.ResourceId;
+import android.content.res.Resources;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
@@ -187,7 +188,9 @@
@Override
public void onBootPhase(int phase) {
- if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
+ if (phase == PHASE_ACTIVITY_MANAGER_READY) {
+ BlobStoreConfig.initialize(mContext);
+ } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
synchronized (mBlobsLock) {
final SparseArray<SparseArray<String>> allPackages = getAllPackages();
readBlobSessionsLocked(allPackages);
@@ -380,6 +383,11 @@
throw new IllegalArgumentException(
"Lease expiry cannot be later than blobs expiry time");
}
+ if (getTotalUsageBytesLocked(callingUid, callingPackage)
+ + blobMetadata.getSize() > BlobStoreConfig.getAppDataBytesLimit()) {
+ throw new IllegalStateException("Total amount of data with an active lease"
+ + " is exceeding the max limit");
+ }
blobMetadata.addLeasee(callingPackage, callingUid,
descriptionResId, description, leaseExpiryTimeMillis);
if (LOGV) {
@@ -390,6 +398,18 @@
}
}
+ @VisibleForTesting
+ @GuardedBy("mBlobsLock")
+ long getTotalUsageBytesLocked(int callingUid, String callingPackage) {
+ final AtomicLong totalBytes = new AtomicLong(0);
+ forEachBlobInUser((blobMetadata) -> {
+ if (blobMetadata.isALeasee(callingPackage, callingUid)) {
+ totalBytes.getAndAdd(blobMetadata.getSize());
+ }
+ }, UserHandle.getUserId(callingUid));
+ return totalBytes.get();
+ }
+
private void releaseLeaseInternal(BlobHandle blobHandle, int callingUid,
String callingPackage) {
synchronized (mBlobsLock) {
@@ -1187,8 +1207,12 @@
final int callingUid = Binder.getCallingUid();
verifyCallingPackage(callingUid, packageName);
- acquireLeaseInternal(blobHandle, descriptionResId, description, leaseExpiryTimeMillis,
- callingUid, packageName);
+ try {
+ acquireLeaseInternal(blobHandle, descriptionResId, description,
+ leaseExpiryTimeMillis, callingUid, packageName);
+ } catch (Resources.NotFoundException e) {
+ throw new IllegalArgumentException(e);
+ }
}
@Override
@@ -1231,8 +1255,10 @@
}
synchronized (mBlobsLock) {
- fout.println("mCurrentMaxSessionId: " + mCurrentMaxSessionId);
- fout.println();
+ if (dumpArgs.shouldDumpAllSections()) {
+ fout.println("mCurrentMaxSessionId: " + mCurrentMaxSessionId);
+ fout.println();
+ }
if (dumpArgs.shouldDumpSessions()) {
dumpSessionsLocked(fout, dumpArgs);
@@ -1243,6 +1269,14 @@
fout.println();
}
}
+
+ if (dumpArgs.shouldDumpConfig()) {
+ fout.println("BlobStore config:");
+ fout.increaseIndent();
+ BlobStoreConfig.dump(fout, mContext);
+ fout.decreaseIndent();
+ fout.println();
+ }
}
@Override
@@ -1255,14 +1289,16 @@
}
static final class DumpArgs {
+ private static final int FLAG_DUMP_SESSIONS = 1 << 0;
+ private static final int FLAG_DUMP_BLOBS = 1 << 1;
+ private static final int FLAG_DUMP_CONFIG = 1 << 2;
+
+ private int mSelectedSectionFlags;
private boolean mDumpFull;
private final ArrayList<String> mDumpPackages = new ArrayList<>();
private final ArrayList<Integer> mDumpUids = new ArrayList<>();
private final ArrayList<Integer> mDumpUserIds = new ArrayList<>();
private final ArrayList<Long> mDumpBlobIds = new ArrayList<>();
- private boolean mDumpOnlySelectedSections;
- private boolean mDumpSessions;
- private boolean mDumpBlobs;
private boolean mDumpHelp;
public boolean shouldDumpSession(String packageName, int uid, long blobId) {
@@ -1281,18 +1317,41 @@
return true;
}
+ public boolean shouldDumpAllSections() {
+ return mSelectedSectionFlags == 0;
+ }
+
+ public void allowDumpSessions() {
+ mSelectedSectionFlags |= FLAG_DUMP_SESSIONS;
+ }
+
public boolean shouldDumpSessions() {
- if (!mDumpOnlySelectedSections) {
+ if (shouldDumpAllSections()) {
return true;
}
- return mDumpSessions;
+ return (mSelectedSectionFlags & FLAG_DUMP_SESSIONS) != 0;
+ }
+
+ public void allowDumpBlobs() {
+ mSelectedSectionFlags |= FLAG_DUMP_BLOBS;
}
public boolean shouldDumpBlobs() {
- if (!mDumpOnlySelectedSections) {
+ if (shouldDumpAllSections()) {
return true;
}
- return mDumpBlobs;
+ return (mSelectedSectionFlags & FLAG_DUMP_BLOBS) != 0;
+ }
+
+ public void allowDumpConfig() {
+ mSelectedSectionFlags |= FLAG_DUMP_CONFIG;
+ }
+
+ public boolean shouldDumpConfig() {
+ if (shouldDumpAllSections()) {
+ return true;
+ }
+ return (mSelectedSectionFlags & FLAG_DUMP_CONFIG) != 0;
}
public boolean shouldDumpBlob(long blobId) {
@@ -1329,11 +1388,11 @@
dumpArgs.mDumpFull = true;
}
} else if ("--sessions".equals(opt)) {
- dumpArgs.mDumpOnlySelectedSections = true;
- dumpArgs.mDumpSessions = true;
+ dumpArgs.allowDumpSessions();
} else if ("--blobs".equals(opt)) {
- dumpArgs.mDumpOnlySelectedSections = true;
- dumpArgs.mDumpBlobs = true;
+ dumpArgs.allowDumpBlobs();
+ } else if ("--config".equals(opt)) {
+ dumpArgs.allowDumpConfig();
} else if ("--package".equals(opt) || "-p".equals(opt)) {
dumpArgs.mDumpPackages.add(getStringArgRequired(args, ++i, "packageName"));
} else if ("--uid".equals(opt) || "-u".equals(opt)) {
@@ -1392,6 +1451,8 @@
printWithIndent(pw, "Dump only the sessions info");
pw.println("--blobs");
printWithIndent(pw, "Dump only the committed blobs info");
+ pw.println("--config");
+ printWithIndent(pw, "Dump only the config values");
pw.println("--package | -p [package-name]");
printWithIndent(pw, "Dump blobs info associated with the given package");
pw.println("--uid | -u [uid]");
diff --git a/api/current.txt b/api/current.txt
index d4cbe6d..a953ad7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -315,7 +315,7 @@
field public static final int animationOrder = 16843214; // 0x10101ce
field @Deprecated public static final int animationResolution = 16843546; // 0x101031a
field public static final int antialias = 16843034; // 0x101011a
- field public static final int anyDensity = 16843372; // 0x101026c
+ field @Deprecated public static final int anyDensity = 16843372; // 0x101026c
field public static final int apduServiceBanner = 16843757; // 0x10103ed
field public static final int apiKey = 16843281; // 0x1010211
field public static final int appCategory = 16844101; // 0x1010545
@@ -11475,7 +11475,7 @@
field public static final int FLAG_SUPPORTS_LARGE_SCREENS = 2048; // 0x800
field public static final int FLAG_SUPPORTS_NORMAL_SCREENS = 1024; // 0x400
field public static final int FLAG_SUPPORTS_RTL = 4194304; // 0x400000
- field public static final int FLAG_SUPPORTS_SCREEN_DENSITIES = 8192; // 0x2000
+ field @Deprecated public static final int FLAG_SUPPORTS_SCREEN_DENSITIES = 8192; // 0x2000
field public static final int FLAG_SUPPORTS_SMALL_SCREENS = 512; // 0x200
field public static final int FLAG_SUPPORTS_XLARGE_SCREENS = 524288; // 0x80000
field public static final int FLAG_SUSPENDED = 1073741824; // 0x40000000
@@ -40574,7 +40574,7 @@
field public static final String EXTRA_APP_PACKAGE = "android.provider.extra.APP_PACKAGE";
field public static final String EXTRA_AUTHORITIES = "authorities";
field public static final String EXTRA_BATTERY_SAVER_MODE_ENABLED = "android.settings.extra.battery_saver_mode_enabled";
- field public static final String EXTRA_BIOMETRIC_MINIMUM_STRENGTH_REQUIRED = "android.provider.extra.BIOMETRIC_MINIMUM_STRENGTH_REQUIRED";
+ field public static final String EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED = "android.provider.extra.BIOMETRIC_AUTHENTICATORS_ALLOWED";
field public static final String EXTRA_CHANNEL_ID = "android.provider.extra.CHANNEL_ID";
field public static final String EXTRA_CONVERSATION_ID = "android.provider.extra.CONVERSATION_ID";
field public static final String EXTRA_DO_NOT_DISTURB_MODE_ENABLED = "android.settings.extra.do_not_disturb_mode_enabled";
@@ -43552,9 +43552,9 @@
public abstract class ControlAction {
method public abstract int getActionType();
method @Nullable public String getChallengeValue();
+ method @NonNull public static android.service.controls.actions.ControlAction getErrorAction();
method @NonNull public String getTemplateId();
method public static final boolean isValidResponse(int);
- field @NonNull public static final android.service.controls.actions.ControlAction ERROR_ACTION;
field public static final int RESPONSE_CHALLENGE_ACK = 3; // 0x3
field public static final int RESPONSE_CHALLENGE_PASSPHRASE = 5; // 0x5
field public static final int RESPONSE_CHALLENGE_PIN = 4; // 0x4
@@ -43566,7 +43566,6 @@
field public static final int TYPE_ERROR = -1; // 0xffffffff
field public static final int TYPE_FLOAT = 2; // 0x2
field public static final int TYPE_MODE = 4; // 0x4
- field public static final int TYPE_MULTI_FLOAT = 3; // 0x3
}
public final class FloatAction extends android.service.controls.actions.ControlAction {
@@ -43583,13 +43582,6 @@
method public int getNewMode();
}
- public final class MultiFloatAction extends android.service.controls.actions.ControlAction {
- ctor public MultiFloatAction(@NonNull String, @NonNull float[], @Nullable String);
- ctor public MultiFloatAction(@NonNull String, @NonNull float[]);
- method public int getActionType();
- method @NonNull public float[] getNewValues();
- }
-
}
package android.service.controls.templates {
@@ -43604,16 +43596,15 @@
}
public abstract class ControlTemplate {
+ method @NonNull public static android.service.controls.templates.ControlTemplate getErrorTemplate();
+ method @NonNull public static android.service.controls.templates.ControlTemplate getNoTemplateObject();
method @NonNull public String getTemplateId();
method public abstract int getTemplateType();
- field @NonNull public static final android.service.controls.templates.ControlTemplate ERROR_TEMPLATE;
- field @NonNull public static final android.service.controls.templates.ControlTemplate NO_TEMPLATE;
field public static final int TYPE_ERROR = -1; // 0xffffffff
- field public static final int TYPE_NONE = 0; // 0x0
+ field public static final int TYPE_NO_TEMPLATE = 0; // 0x0
field public static final int TYPE_RANGE = 2; // 0x2
field public static final int TYPE_STATELESS = 8; // 0x8
field public static final int TYPE_TEMPERATURE = 7; // 0x7
- field public static final int TYPE_THUMBNAIL = 3; // 0x3
field public static final int TYPE_TOGGLE = 1; // 0x1
field public static final int TYPE_TOGGLE_RANGE = 6; // 0x6
}
@@ -43653,13 +43644,6 @@
field public static final int MODE_UNKNOWN = 0; // 0x0
}
- public final class ThumbnailTemplate extends android.service.controls.templates.ControlTemplate {
- ctor public ThumbnailTemplate(@NonNull String, @NonNull android.graphics.drawable.Icon, @NonNull CharSequence);
- method @NonNull public CharSequence getContentDescription();
- method public int getTemplateType();
- method @NonNull public android.graphics.drawable.Icon getThumbnail();
- }
-
public final class ToggleRangeTemplate extends android.service.controls.templates.ControlTemplate {
ctor public ToggleRangeTemplate(@NonNull String, @NonNull android.service.controls.templates.ControlButton, @NonNull android.service.controls.templates.RangeTemplate);
ctor public ToggleRangeTemplate(@NonNull String, boolean, @NonNull CharSequence, @NonNull android.service.controls.templates.RangeTemplate);
diff --git a/api/system-current.txt b/api/system-current.txt
index f062112..2ce6fd0 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -9131,6 +9131,7 @@
field public static final String NAMESPACE_ATTENTION_MANAGER_SERVICE = "attention_manager_service";
field public static final String NAMESPACE_AUTOFILL = "autofill";
field public static final String NAMESPACE_BIOMETRICS = "biometrics";
+ field public static final String NAMESPACE_BLOBSTORE = "blobstore";
field public static final String NAMESPACE_CONNECTIVITY = "connectivity";
field public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture";
field @Deprecated public static final String NAMESPACE_DEX_BOOT = "dex_boot";
diff --git a/core/java/android/app/NotificationHistory.java b/core/java/android/app/NotificationHistory.java
index 8c2cc94..59dc999 100644
--- a/core/java/android/app/NotificationHistory.java
+++ b/core/java/android/app/NotificationHistory.java
@@ -22,6 +22,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import android.util.Slog;
import java.util.ArrayList;
import java.util.Arrays;
@@ -107,9 +108,11 @@
", mChannelName='" + mChannelName + '\'' +
", mChannelId='" + mChannelId + '\'' +
", mUserId=" + mUserId +
+ ", mUid=" + mUid +
", mTitle='" + mTitle + '\'' +
", mText='" + mText + '\'' +
", mIcon=" + mIcon +
+ ", mPostedTimeMs=" + mPostedTimeMs +
", mConversationId=" + mConversationId +
'}';
}
@@ -285,9 +288,7 @@
if (!hasNextNotification()) {
return null;
}
-
HistoricalNotification n = readNotificationFromParcel(mParcel);
-
mIndex++;
if (!hasNextNotification()) {
mParcel.recycle();
diff --git a/core/java/android/content/om/OverlayManager.java b/core/java/android/content/om/OverlayManager.java
index dbe3954..2bdca7d 100644
--- a/core/java/android/content/om/OverlayManager.java
+++ b/core/java/android/content/om/OverlayManager.java
@@ -27,14 +27,50 @@
import android.compat.annotation.EnabledAfter;
import android.content.Context;
import android.os.Build;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import com.android.server.SystemConfig;
+
import java.util.List;
/**
* Updates OverlayManager state; gets information about installed overlay packages.
+ *
+ * <p>Users of this API must be actors of any overlays they desire to change the state of.</p>
+ *
+ * <p>An actor is a package responsible for managing the state of overlays targeting overlayables
+ * that specify the actor. For example, an actor may enable or disable an overlay or otherwise
+ * change its state.</p>
+ *
+ * <p>Actors are specified as part of the overlayable definition.
+ *
+ * <pre>{@code
+ * <overlayable name="OverlayableResourcesName" actor="overlay://namespace/actorName">
+ * }</pre></p>
+ *
+ * <p>Actors are defined through {@link SystemConfig}. Only system packages can be used.
+ * The namespace "android" is reserved for use by AOSP and any "android" definitions must
+ * have an implementation on device that fulfill their intended functionality.</p>
+ *
+ * <pre>{@code
+ * <named-actor
+ * namespace="namespace"
+ * name="actorName"
+ * package="com.example.pkg"
+ * />
+ * }</pre></p>
+ *
+ * <p>An actor can manipulate a particular overlay if any of the following is true:
+ * <ul>
+ * <li>its UID is {@link Process#ROOT_UID}, {@link Process#SYSTEM_UID}</li>
+ * <li>it is the target of the overlay package</li>
+ * <li>it has the CHANGE_OVERLAY_PACKAGES permission and the target does not specify an actor</li>
+ * <li>it is the actor specified by the overlayable</li>
+ * </ul></p>
+ *
* @hide
*/
@SystemApi
@@ -84,6 +120,8 @@
* If a set of overlay packages share the same category, single call to this method is
* equivalent to multiple calls to {@link #setEnabled(String, boolean, UserHandle)}.
*
+ * The caller must pass the actor requirements specified in the class comment.
+ *
* @param packageName the name of the overlay package to enable.
* @param user The user for which to change the overlay.
*
@@ -116,6 +154,8 @@
* While {@link #setEnabledExclusiveInCategory(String, UserHandle)} doesn't support disabling
* every overlay in a category, this method allows you to disable everything.
*
+ * The caller must pass the actor requirements specified in the class comment.
+ *
* @param packageName the name of the overlay package to enable.
* @param enable {@code false} if the overlay should be turned off.
* @param user The user for which to change the overlay.
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 4c6fef2..a15afe0 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -247,7 +247,11 @@
* accommodate different screen densities. Corresponds to
* {@link android.R.styleable#AndroidManifestSupportsScreens_anyDensity
* android:anyDensity}.
+ *
+ * @deprecated Set by default when targeting API 4 or higher and apps
+ * should not set this to false.
*/
+ @Deprecated
public static final int FLAG_SUPPORTS_SCREEN_DENSITIES = 1<<13;
/**
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 505d4ca..aa511cc 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -111,6 +111,14 @@
public static final String NAMESPACE_AUTOFILL = "autofill";
/**
+ * Namespace for blobstore feature that allows apps to share data blobs.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_BLOBSTORE = "blobstore";
+
+ /**
* Namespace for all networking connectivity related features.
*
* @hide
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 83a304f..641de4a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -703,13 +703,18 @@
* Weak or above, as defined by the CDD. Only biometrics that meet or exceed Strong, as defined
* in the CDD are allowed to participate in Keystore operations.
* <p>
- * Input: extras {@link #EXTRA_BIOMETRIC_MINIMUM_STRENGTH_REQUIRED} as an integer, with
+ * Input: extras {@link #EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED} as an integer, with
* constants defined in {@link android.hardware.biometrics.BiometricManager.Authenticators},
* e.g. {@link android.hardware.biometrics.BiometricManager.Authenticators#BIOMETRIC_STRONG}.
* If not specified, the default behavior is
* {@link android.hardware.biometrics.BiometricManager.Authenticators#BIOMETRIC_WEAK}.
* <p>
- * Output: Nothing.
+ * Output: Returns {@link android.app.Activity#RESULT_CANCELED} if the user already has an
+ * authenticator that meets the requirements, or if the device cannot fulfill the request
+ * (e.g. does not have biometric hardware). Returns {@link android.app.Activity#RESULT_OK}
+ * otherwise. Note that callers should still check
+ * {@link android.hardware.biometrics.BiometricManager#canAuthenticate(int)}
+ * afterwards to ensure that the user actually completed enrollment.
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_BIOMETRIC_ENROLL =
@@ -719,12 +724,12 @@
* Activity Extra: The minimum strength to request enrollment for.
* <p>
* This can be passed as an extra field to the {@link #ACTION_BIOMETRIC_ENROLL} intent to
- * indicate that only enrollment for sensors that meet this strength should be shown. The
- * value should be one of the biometric strength constants defined in
+ * indicate that only enrollment for sensors that meet these requirements should be shown. The
+ * value should be a combination of the constants defined in
* {@link android.hardware.biometrics.BiometricManager.Authenticators}.
*/
- public static final String EXTRA_BIOMETRIC_MINIMUM_STRENGTH_REQUIRED =
- "android.provider.extra.BIOMETRIC_MINIMUM_STRENGTH_REQUIRED";
+ public static final String EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED =
+ "android.provider.extra.BIOMETRIC_AUTHENTICATORS_ALLOWED";
/**
* Activity Action: Show settings to allow configuration of cast endpoints.
diff --git a/core/java/android/service/controls/Control.java b/core/java/android/service/controls/Control.java
index dabd977..d01bc25 100644
--- a/core/java/android/service/controls/Control.java
+++ b/core/java/android/service/controls/Control.java
@@ -395,7 +395,7 @@
* {@link ControlsProviderService#createPublisherForAllAvailable}:
* <ul>
* <li> Status: {@link Status#STATUS_UNKNOWN}
- * <li> Control template: {@link ControlTemplate#NO_TEMPLATE}
+ * <li> Control template: {@link ControlTemplate#getNoTemplateObject}
* <li> Status text: {@code ""}
* </ul>
*/
@@ -593,7 +593,7 @@
* <li> Title: {@code ""}
* <li> Subtitle: {@code ""}
* <li> Status: {@link Status#STATUS_UNKNOWN}
- * <li> Control template: {@link ControlTemplate#NO_TEMPLATE}
+ * <li> Control template: {@link ControlTemplate#getNoTemplateObject}
* <li> Status text: {@code ""}
* </ul>
*/
diff --git a/core/java/android/service/controls/ControlsProviderService.java b/core/java/android/service/controls/ControlsProviderService.java
index 9debb37..9accf5b 100644
--- a/core/java/android/service/controls/ControlsProviderService.java
+++ b/core/java/android/service/controls/ControlsProviderService.java
@@ -239,7 +239,8 @@
private static boolean isStatelessControl(Control control) {
return (control.getStatus() == Control.STATUS_UNKNOWN
- && control.getControlTemplate().getTemplateType() == ControlTemplate.TYPE_NONE
+ && control.getControlTemplate().getTemplateType()
+ == ControlTemplate.TYPE_NO_TEMPLATE
&& TextUtils.isEmpty(control.getStatusText()));
}
diff --git a/core/java/android/service/controls/DeviceTypes.java b/core/java/android/service/controls/DeviceTypes.java
index ac3b36c..f973610 100644
--- a/core/java/android/service/controls/DeviceTypes.java
+++ b/core/java/android/service/controls/DeviceTypes.java
@@ -23,6 +23,18 @@
/**
* Device types for {@link Control}.
+ *
+ * Each {@link Control} declares a type for the device they represent. This type will be used to
+ * determine icons and colors.
+ * <p>
+ * The type of the device may change on status updates of the {@link Control}. For example, a
+ * device of {@link #TYPE_OUTLET} could be determined by the {@link ControlsProviderService} to be
+ * a {@link #TYPE_COFFEE_MAKER} and change the type for that {@link Control}, therefore possibly
+ * changing icon and color.
+ * <p>
+ * In case the device type is not know by the application but the basic function is, or there is no
+ * provided type, one of the generic types (those starting with {@code TYPE_GENERIC}) can be used.
+ * These will provide an identifiable icon based on the basic function of the device.
*/
public class DeviceTypes {
diff --git a/core/java/android/service/controls/actions/ControlAction.java b/core/java/android/service/controls/actions/ControlAction.java
index 45e63d7..37a75f0 100644
--- a/core/java/android/service/controls/actions/ControlAction.java
+++ b/core/java/android/service/controls/actions/ControlAction.java
@@ -22,7 +22,7 @@
import android.annotation.Nullable;
import android.os.Bundle;
import android.service.controls.Control;
-import android.service.controls.IControlsActionCallback;
+import android.service.controls.ControlsProviderService;
import android.service.controls.templates.ControlTemplate;
import android.util.Log;
@@ -34,8 +34,15 @@
/**
* An abstract action indicating a user interaction with a {@link Control}.
*
- * The action may have a value to authenticate the input, when the provider has requested it to
- * complete the action.
+ * In some cases, an action needs to be validated by the user, using a password, PIN or simple
+ * acknowledgment. For those cases, an optional (nullable) parameter can be passed to send the user
+ * input. This <b>challenge value</b> will be requested from the user and sent as part
+ * of a {@link ControlAction} only if the service has responded to an action with one of:
+ * <ul>
+ * <li> {@link #RESPONSE_CHALLENGE_ACK}
+ * <li> {@link #RESPONSE_CHALLENGE_PIN}
+ * <li> {@link #RESPONSE_CHALLENGE_PASSPHRASE}
+ * </ul>
*/
public abstract class ControlAction {
@@ -53,7 +60,6 @@
TYPE_ERROR,
TYPE_BOOLEAN,
TYPE_FLOAT,
- TYPE_MULTI_FLOAT,
TYPE_MODE,
TYPE_COMMAND
})
@@ -61,6 +67,7 @@
/**
* Object returned when there is an unparcelling error.
+ * @hide
*/
public static final @NonNull ControlAction ERROR_ACTION = new ControlAction() {
@Override
@@ -70,7 +77,7 @@
};
/**
- * The identifier of {@link #ERROR_ACTION}
+ * The identifier of the action returned by {@link #getErrorAction}.
*/
public static final @ActionType int TYPE_ERROR = -1;
@@ -85,11 +92,6 @@
public static final @ActionType int TYPE_FLOAT = 2;
/**
- * The identifier of {@link MultiFloatAction}.
- */
- public static final @ActionType int TYPE_MULTI_FLOAT = 3;
-
- /**
* The identifier of {@link ModeAction}.
*/
public static final @ActionType int TYPE_MODE = 4;
@@ -121,28 +123,32 @@
public static final @ResponseResult int RESPONSE_UNKNOWN = 0;
/**
- * Response code for {@link IControlsActionCallback#accept} indicating that
- * the action has been performed. The action may still fail later and the state may not change.
+ * Response code for the {@code consumer} in
+ * {@link ControlsProviderService#performControlAction} indicating that the action has been
+ * performed. The action may still fail later and the state may not change.
*/
public static final @ResponseResult int RESPONSE_OK = 1;
/**
- * Response code for {@link IControlsActionCallback#accept} indicating that
- * the action has failed.
+ * Response code for the {@code consumer} in
+ * {@link ControlsProviderService#performControlAction} indicating that the action has failed.
*/
public static final @ResponseResult int RESPONSE_FAIL = 2;
/**
- * Response code for {@link IControlsActionCallback#accept} indicating that
- * in order for the action to be performed, acknowledgment from the user is required.
+ * Response code for the {@code consumer} in
+ * {@link ControlsProviderService#performControlAction} indicating that in order for the action
+ * to be performed, acknowledgment from the user is required.
*/
public static final @ResponseResult int RESPONSE_CHALLENGE_ACK = 3;
/**
- * Response code for {@link IControlsActionCallback#accept} indicating that
- * in order for the action to be performed, a PIN is required.
+ * Response code for the {@code consumer} in
+ * {@link ControlsProviderService#performControlAction} indicating that in order for the action
+ * to be performed, a PIN is required.
*/
public static final @ResponseResult int RESPONSE_CHALLENGE_PIN = 4;
/**
- * Response code for {@link IControlsActionCallback#accept} indicating that
- * in order for the action to be performed, an alphanumeric passphrase is required.
+ * Response code for the {@code consumer} in
+ * {@link ControlsProviderService#performControlAction} indicating that in order for the action
+ * to be performed, an alphanumeric passphrase is required.
*/
public static final @ResponseResult int RESPONSE_CHALLENGE_PASSPHRASE = 5;
@@ -228,8 +234,6 @@
return new BooleanAction(bundle);
case TYPE_FLOAT:
return new FloatAction(bundle);
- case TYPE_MULTI_FLOAT:
- return new MultiFloatAction(bundle);
case TYPE_MODE:
return new ModeAction(bundle);
case TYPE_COMMAND:
@@ -243,4 +247,12 @@
return ERROR_ACTION;
}
}
+
+ /**
+ * Returns a singleton {@link ControlAction} used for indicating an error in unparceling.
+ */
+ @NonNull
+ public static ControlAction getErrorAction() {
+ return ERROR_ACTION;
+ }
}
diff --git a/core/java/android/service/controls/actions/MultiFloatAction.java b/core/java/android/service/controls/actions/MultiFloatAction.java
deleted file mode 100644
index e574079..0000000
--- a/core/java/android/service/controls/actions/MultiFloatAction.java
+++ /dev/null
@@ -1,82 +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 android.service.controls.actions;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Bundle;
-import android.util.Log;
-
-import com.android.internal.util.Preconditions;
-
-public final class MultiFloatAction extends ControlAction {
-
- private static final String TAG = "MultiFloatAction";
- private static final @ActionType int TYPE = TYPE_MULTI_FLOAT;
- private static final String KEY_VALUES = "key_values";
-
- private final @NonNull float[] mNewValues;
-
- @Override
- public int getActionType() {
- return TYPE;
- }
-
- public MultiFloatAction(@NonNull String templateId,
- @NonNull float[] newValues,
- @Nullable String challengeValue) {
- super(templateId, challengeValue);
- Preconditions.checkNotNull(newValues);
- if (newValues.length == 0) {
- throw new IllegalArgumentException("newValues array length 0");
- }
- if (newValues.length == 1) {
- Log.w(TAG, "newValues array length 1");
- }
- mNewValues = newValues.clone();
- }
-
- public MultiFloatAction(@NonNull String templateId, @NonNull float[] newValues) {
- this(templateId, newValues, null);
- }
-
- /**
- * @param b
- * @hide
- */
- MultiFloatAction(Bundle b) {
- super(b);
- mNewValues = b.getFloatArray(KEY_VALUES);
- }
-
- @NonNull
- public float[] getNewValues() {
- return mNewValues.clone();
- }
-
- /**
- * @return
- * @hide
- */
- @Override
- @NonNull
- Bundle getDataBundle() {
- Bundle b = super.getDataBundle();
- b.putFloatArray(KEY_VALUES, mNewValues);
- return b;
- }
-}
diff --git a/core/java/android/service/controls/templates/ControlTemplate.java b/core/java/android/service/controls/templates/ControlTemplate.java
index 30efd80..1e16273 100644
--- a/core/java/android/service/controls/templates/ControlTemplate.java
+++ b/core/java/android/service/controls/templates/ControlTemplate.java
@@ -49,18 +49,20 @@
/**
* Singleton representing a {@link Control} with no input.
+ * @hide
*/
public static final @NonNull ControlTemplate NO_TEMPLATE = new ControlTemplate("") {
@Override
public int getTemplateType() {
- return TYPE_NONE;
+ return TYPE_NO_TEMPLATE;
}
};
/**
* Object returned when there is an unparcelling error.
+ * @hide
*/
- public static final @NonNull ControlTemplate ERROR_TEMPLATE = new ControlTemplate("") {
+ private static final @NonNull ControlTemplate ERROR_TEMPLATE = new ControlTemplate("") {
@Override
public int getTemplateType() {
return TYPE_ERROR;
@@ -73,10 +75,9 @@
@Retention(RetentionPolicy.SOURCE)
@IntDef({
TYPE_ERROR,
- TYPE_NONE,
+ TYPE_NO_TEMPLATE,
TYPE_TOGGLE,
TYPE_RANGE,
- TYPE_THUMBNAIL,
TYPE_TOGGLE_RANGE,
TYPE_TEMPERATURE,
TYPE_STATELESS
@@ -84,14 +85,14 @@
public @interface TemplateType {}
/**
- * Type identifier of {@link #ERROR_TEMPLATE}.
+ * Type identifier of the template returned by {@link #getErrorTemplate()}.
*/
public static final @TemplateType int TYPE_ERROR = -1;
/**
- * Type identifier of {@link ControlTemplate#NO_TEMPLATE}.
+ * Type identifier of {@link ControlTemplate#getNoTemplateObject}.
*/
- public static final @TemplateType int TYPE_NONE = 0;
+ public static final @TemplateType int TYPE_NO_TEMPLATE = 0;
/**
* Type identifier of {@link ToggleTemplate}.
@@ -104,11 +105,6 @@
public static final @TemplateType int TYPE_RANGE = 2;
/**
- * Type identifier of {@link ThumbnailTemplate}.
- */
- public static final @TemplateType int TYPE_THUMBNAIL = 3;
-
- /**
* Type identifier of {@link ToggleRangeTemplate}.
*/
public static final @TemplateType int TYPE_TOGGLE_RANGE = 6;
@@ -191,15 +187,13 @@
return new ToggleTemplate(bundle);
case TYPE_RANGE:
return new RangeTemplate(bundle);
- case TYPE_THUMBNAIL:
- return new ThumbnailTemplate(bundle);
case TYPE_TOGGLE_RANGE:
return new ToggleRangeTemplate(bundle);
case TYPE_TEMPERATURE:
return new TemperatureControlTemplate(bundle);
case TYPE_STATELESS:
return new StatelessTemplate(bundle);
- case TYPE_NONE:
+ case TYPE_NO_TEMPLATE:
return NO_TEMPLATE;
case TYPE_ERROR:
default:
@@ -210,4 +204,27 @@
return ERROR_TEMPLATE;
}
}
+
+ /**
+ * @return a singleton {@link ControlTemplate} used for indicating an error in unparceling.
+ */
+ @NonNull
+ public static ControlTemplate getErrorTemplate() {
+ return ERROR_TEMPLATE;
+ }
+
+ /**
+ * Get a singleton {@link ControlTemplate} that has no features.
+ *
+ * This template has no distinctive field, not even an identifier. Used for a {@link Control}
+ * that accepts no type of input, or when there is no known state.
+ *
+ * @return a singleton {@link ControlTemplate} to indicate no specific template is used by
+ * this {@link Control}
+ */
+ @NonNull
+ public static ControlTemplate getNoTemplateObject() {
+ return NO_TEMPLATE;
+ }
+
}
diff --git a/core/java/android/service/controls/templates/TemperatureControlTemplate.java b/core/java/android/service/controls/templates/TemperatureControlTemplate.java
index 0818c7e..96be97a 100644
--- a/core/java/android/service/controls/templates/TemperatureControlTemplate.java
+++ b/core/java/android/service/controls/templates/TemperatureControlTemplate.java
@@ -60,16 +60,34 @@
private static final int NUM_MODES = 6;
+ /**
+ * Use when the current or active mode of the device is not known
+ */
public static final @Mode int MODE_UNKNOWN = 0;
+ /**
+ * Indicates that the current or active mode of the device is off.
+ */
public static final @Mode int MODE_OFF = 1;
+ /**
+ * Indicates that the current or active mode of the device is set to heat.
+ */
public static final @Mode int MODE_HEAT = 2;
+ /**
+ * Indicates that the current or active mode of the device is set to cool.
+ */
public static final @Mode int MODE_COOL = 3;
+ /**
+ * Indicates that the current or active mode of the device is set to heat-cool.
+ */
public static final @Mode int MODE_HEAT_COOL = 4;
+ /**
+ * Indicates that the current or active mode of the device is set to eco.
+ */
public static final @Mode int MODE_ECO = 5;
/**
@@ -85,10 +103,29 @@
})
public @interface ModeFlag {}
+ /**
+ * Flag to indicate that the device supports off mode.
+ */
public static final int FLAG_MODE_OFF = 1 << MODE_OFF;
+
+ /**
+ * Flag to indicate that the device supports heat mode.
+ */
public static final int FLAG_MODE_HEAT = 1 << MODE_HEAT;
+
+ /**
+ * Flag to indicate that the device supports cool mode.
+ */
public static final int FLAG_MODE_COOL = 1 << MODE_COOL;
+
+ /**
+ * Flag to indicate that the device supports heat-cool mode.
+ */
public static final int FLAG_MODE_HEAT_COOL = 1 << MODE_HEAT_COOL;
+
+ /**
+ * Flag to indicate that the device supports eco mode.
+ */
public static final int FLAG_MODE_ECO = 1 << MODE_ECO;
private static final int ALL_FLAGS =
FLAG_MODE_OFF |
diff --git a/core/java/android/service/controls/templates/ThumbnailTemplate.java b/core/java/android/service/controls/templates/ThumbnailTemplate.java
deleted file mode 100644
index 72179f4..0000000
--- a/core/java/android/service/controls/templates/ThumbnailTemplate.java
+++ /dev/null
@@ -1,98 +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 android.service.controls.templates;
-
-import android.annotation.NonNull;
-import android.graphics.drawable.Icon;
-import android.os.Bundle;
-import android.service.controls.Control;
-
-import com.android.internal.util.Preconditions;
-
-/**
- * A template for a {@link Control} that displays an image.
- */
-public final class ThumbnailTemplate extends ControlTemplate {
-
- private static final @TemplateType int TYPE = TYPE_THUMBNAIL;
- private static final String KEY_ICON = "key_icon";
- private static final String KEY_CONTENT_DESCRIPTION = "key_content_description";
-
- private final @NonNull Icon mThumbnail;
- private final @NonNull CharSequence mContentDescription;
-
- /**
- * @param templateId the identifier for this template object
- * @param thumbnail an image to display on the {@link Control}
- * @param contentDescription a description of the image for accessibility.
- */
- public ThumbnailTemplate(@NonNull String templateId, @NonNull Icon thumbnail,
- @NonNull CharSequence contentDescription) {
- super(templateId);
- Preconditions.checkNotNull(thumbnail);
- Preconditions.checkNotNull(contentDescription);
- mThumbnail = thumbnail;
- mContentDescription = contentDescription;
- }
-
- /**
- * @param b
- * @hide
- */
- ThumbnailTemplate(Bundle b) {
- super(b);
- mThumbnail = b.getParcelable(KEY_ICON);
- mContentDescription = b.getCharSequence(KEY_CONTENT_DESCRIPTION, "");
- }
-
- /**
- * The {@link Icon} (image) displayed by this template.
- */
- @NonNull
- public Icon getThumbnail() {
- return mThumbnail;
- }
-
- /**
- * The description of the image returned by {@link ThumbnailTemplate#getThumbnail()}
- */
- @NonNull
- public CharSequence getContentDescription() {
- return mContentDescription;
- }
-
- /**
- * @return {@link ControlTemplate#TYPE_THUMBNAIL}
- */
- @Override
- public int getTemplateType() {
- return TYPE;
- }
-
- /**
- * @return
- * @hide
- */
- @Override
- @NonNull
- Bundle getDataBundle() {
- Bundle b = super.getDataBundle();
- b.putObject(KEY_ICON, mThumbnail);
- b.putObject(KEY_CONTENT_DESCRIPTION, mContentDescription);
- return b;
- }
-}
diff --git a/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java b/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java
index 130ee64..91ba0df 100644
--- a/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java
+++ b/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java
@@ -20,7 +20,7 @@
import com.android.internal.logging.UiEventLogger;
import java.util.LinkedList;
-import java.util.Queue;
+import java.util.List;
/**
* Fake logger that queues up logged events for inspection.
@@ -52,11 +52,24 @@
}
}
- private Queue<FakeUiEvent> mLogs = new LinkedList<>();
+ private List<FakeUiEvent> mLogs = new LinkedList<>();
- public Queue<FakeUiEvent> getLogs() {
+ /** Returns list of all logging events recorded. */
+ public List<FakeUiEvent> getLogs() {
return mLogs;
}
+ /** Returns number of logging events recorded. */
+ public int numLogs() {
+ return mLogs.size();
+ }
+ /** Returns a particular logging event. */
+ public FakeUiEvent get(int index) {
+ return mLogs.get(index);
+ }
+ /** Returns event id (as integer) of a particular logging event. */
+ public int eventId(int index) {
+ return mLogs.get(index).eventId;
+ }
@Override
public void log(UiEventEnum event) {
@@ -67,7 +80,7 @@
public void log(UiEventEnum event, int uid, String packageName) {
final int eventId = event.getId();
if (eventId > 0) {
- mLogs.offer(new FakeUiEvent(eventId, uid, packageName));
+ mLogs.add(new FakeUiEvent(eventId, uid, packageName));
}
}
@@ -76,7 +89,7 @@
InstanceId instance) {
final int eventId = event.getId();
if (eventId > 0) {
- mLogs.offer(new FakeUiEvent(eventId, uid, packageName, instance));
+ mLogs.add(new FakeUiEvent(eventId, uid, packageName, instance));
}
}
}
diff --git a/core/proto/android/providers/settings/config.proto b/core/proto/android/providers/settings/config.proto
index cc24196..b0a70ef 100644
--- a/core/proto/android/providers/settings/config.proto
+++ b/core/proto/android/providers/settings/config.proto
@@ -47,6 +47,7 @@
repeated SettingProto systemui_settings = 20;
repeated SettingProto telephony_settings = 21;
repeated SettingProto textclassifier_settings = 22;
+ repeated SettingProto blobstore_settings = 23;
message NamespaceProto {
optional string namespace = 1;
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 08dca62..c245131 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2266,9 +2266,9 @@
a newer platform that supports more screens. -->
<attr name="resizeable" format="boolean" />
<!-- Indicates whether the application can accommodate any screen
- density. Older applications are assumed to not be able to,
- new ones able to. You can explicitly supply your abilities
- here. -->
+ density. This is assumed true if targetSdkVersion is 4 or higher.
+ @deprecated Should always be true by default and not overridden.
+ -->
<attr name="anyDensity" format="boolean" />
</declare-styleable>
diff --git a/core/tests/coretests/src/android/app/NotificationHistoryTest.java b/core/tests/coretests/src/android/app/NotificationHistoryTest.java
index 0443d3a..c951091 100644
--- a/core/tests/coretests/src/android/app/NotificationHistoryTest.java
+++ b/core/tests/coretests/src/android/app/NotificationHistoryTest.java
@@ -351,5 +351,7 @@
HistoricalNotification postParcelNotification = parceledHistory.getNextNotification();
assertThat(postParcelNotification).isEqualTo(expectedEntries.get(i));
}
+ assertThat(parceledHistory.hasNextNotification()).isFalse();
+ assertThat(parceledHistory.getNextNotification()).isNull();
}
}
diff --git a/core/tests/coretests/src/android/service/controls/actions/ControlActionTest.java b/core/tests/coretests/src/android/service/controls/actions/ControlActionTest.java
index 10a7b76..d8088b7 100644
--- a/core/tests/coretests/src/android/service/controls/actions/ControlActionTest.java
+++ b/core/tests/coretests/src/android/service/controls/actions/ControlActionTest.java
@@ -56,16 +56,6 @@
}
@Test
- public void testUnparcelingCorrectClass_multiFloat() {
- ControlAction toParcel = new MultiFloatAction(TEST_ID, new float[] {0f, 1f});
-
- ControlAction fromParcel = parcelAndUnparcel(toParcel);
-
- assertEquals(ControlAction.TYPE_MULTI_FLOAT, fromParcel.getActionType());
- assertTrue(fromParcel instanceof MultiFloatAction);
- }
-
- @Test
public void testUnparcelingCorrectClass_mode() {
ControlAction toParcel = new ModeAction(TEST_ID, 1);
diff --git a/core/tests/coretests/src/android/service/controls/templates/ControlTemplateTest.java b/core/tests/coretests/src/android/service/controls/templates/ControlTemplateTest.java
index 292ac09..87dc1b7 100644
--- a/core/tests/coretests/src/android/service/controls/templates/ControlTemplateTest.java
+++ b/core/tests/coretests/src/android/service/controls/templates/ControlTemplateTest.java
@@ -103,16 +103,6 @@
}
@Test
- public void testUnparcelingCorrectClass_thumbnail() {
- ControlTemplate toParcel = new ThumbnailTemplate(TEST_ID, mIcon, TEST_ACTION_DESCRIPTION);
-
- ControlTemplate fromParcel = parcelAndUnparcel(toParcel);
-
- assertEquals(ControlTemplate.TYPE_THUMBNAIL, fromParcel.getTemplateType());
- assertTrue(fromParcel instanceof ThumbnailTemplate);
- }
-
- @Test
public void testUnparcelingCorrectClass_toggleRange() {
ControlTemplate toParcel = new ToggleRangeTemplate(TEST_ID, mControlButton,
new RangeTemplate(TEST_ID, 0, 2, 1, 1, "%f"));
diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml
index 5d2e303..38e18a9 100644
--- a/data/etc/com.android.systemui.xml
+++ b/data/etc/com.android.systemui.xml
@@ -39,6 +39,7 @@
<permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<permission name="android.permission.OBSERVE_NETWORK_POLICY"/>
<permission name="android.permission.OVERRIDE_WIFI_CONFIG"/>
+ <permission name="android.permission.PACKAGE_USAGE_STATS" />
<permission name="android.permission.READ_DREAM_STATE"/>
<permission name="android.permission.READ_FRAME_BUFFER"/>
<permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/>
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
index a0d5a1b..bae42b5 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -21,8 +21,8 @@
import android.content.Context;
+import com.android.keyguard.KeyguardViewController;
import com.android.systemui.car.CarDeviceProvisionedControllerImpl;
-import com.android.systemui.car.CarNotificationInterruptionStateProvider;
import com.android.systemui.dagger.SystemUIRootComponent;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerImpl;
@@ -39,7 +39,6 @@
import com.android.systemui.statusbar.car.CarStatusBar;
import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
@@ -63,10 +62,6 @@
@Module(includes = {DividerModule.class})
abstract class CarSystemUIModule {
- @Binds
- abstract NotificationInterruptionStateProvider bindNotificationInterruptionStateProvider(
- CarNotificationInterruptionStateProvider notificationInterruptionStateProvider);
-
@Singleton
@Provides
@Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME)
@@ -136,6 +131,10 @@
CarStatusBarKeyguardViewManager keyguardViewManager);
@Binds
+ abstract KeyguardViewController bindKeyguardViewController(
+ CarStatusBarKeyguardViewManager keyguardViewManager);
+
+ @Binds
abstract DeviceProvisionedController bindDeviceProvisionedController(
CarDeviceProvisionedControllerImpl deviceProvisionedController);
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
deleted file mode 100644
index 447e579..0000000
--- a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2018 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.car;
-
-import android.content.Context;
-
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.notification.NotificationFilter;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.policy.BatteryController;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-/** Auto-specific implementation of {@link NotificationInterruptionStateProvider}. */
-@Singleton
-public class CarNotificationInterruptionStateProvider extends
- NotificationInterruptionStateProvider {
-
- @Inject
- public CarNotificationInterruptionStateProvider(Context context,
- NotificationFilter filter,
- StatusBarStateController stateController,
- BatteryController batteryController) {
- super(context, filter, stateController, batteryController);
- }
-
- @Override
- public boolean shouldHeadsUp(NotificationEntry entry) {
- // Because space is usually constrained in the auto use-case, there should not be a
- // pinned notification when the shade has been expanded. Ensure this by not pinning any
- // notification if the shade is already opened.
- if (!getPresenter().isPresenterFullyCollapsed()) {
- return false;
- }
-
- return super.shouldHeadsUp(entry);
- }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 83248f4..7ad3d45 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -97,13 +97,14 @@
import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationAlertingManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.init.NotificationsController;
+import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.phone.AutoHideController;
@@ -267,10 +268,9 @@
RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
- NotificationInterruptionStateProvider notificationInterruptionStateProvider,
+ NotificationInterruptStateProvider notificationInterruptStateProvider,
NotificationViewHierarchyManager notificationViewHierarchyManager,
KeyguardViewMediator keyguardViewMediator,
- NotificationAlertingManager notificationAlertingManager,
DisplayMetrics displayMetrics,
MetricsLogger metricsLogger,
@UiBackground Executor uiBgExecutor,
@@ -350,10 +350,9 @@
remoteInputQuickSettingsDisabler,
notificationGutsManager,
notificationLogger,
- notificationInterruptionStateProvider,
+ notificationInterruptStateProvider,
notificationViewHierarchyManager,
keyguardViewMediator,
- notificationAlertingManager,
displayMetrics,
metricsLogger,
uiBgExecutor,
@@ -491,6 +490,22 @@
.isCurrentUserSetupInProgress();
}
});
+
+ mNotificationInterruptStateProvider.addSuppressor(new NotificationInterruptSuppressor() {
+ @Override
+ public String getName() {
+ return TAG;
+ }
+
+ @Override
+ public boolean suppressInterruptions(NotificationEntry entry) {
+ // Because space is usually constrained in the auto use-case, there should not be a
+ // pinned notification when the shade has been expanded.
+ // Ensure this by not allowing any interruptions (ie: pinning any notifications) if
+ // the shade is already opened.
+ return !getPresenter().isPresenterFullyCollapsed();
+ }
+ });
}
@Override
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
index aea4bd4..9798ee7 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
@@ -58,13 +58,12 @@
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.dagger.StatusBarDependenciesModule;
-import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationAlertingManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.init.NotificationsController;
+import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationRowModule;
@@ -143,10 +142,9 @@
RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
- NotificationInterruptionStateProvider notificationInterruptionStateProvider,
+ NotificationInterruptStateProvider notificationInterruptionStateProvider,
NotificationViewHierarchyManager notificationViewHierarchyManager,
KeyguardViewMediator keyguardViewMediator,
- NotificationAlertingManager notificationAlertingManager,
DisplayMetrics displayMetrics,
MetricsLogger metricsLogger,
@UiBackground Executor uiBgExecutor,
@@ -228,7 +226,6 @@
notificationInterruptionStateProvider,
notificationViewHierarchyManager,
keyguardViewMediator,
- notificationAlertingManager,
displayMetrics,
metricsLogger,
uiBgExecutor,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 8789a5c..af74121 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -52,6 +52,8 @@
ConfigSettingsProto.APP_COMPAT_SETTINGS);
namespaceToFieldMap.put(DeviceConfig.NAMESPACE_AUTOFILL,
ConfigSettingsProto.AUTOFILL_SETTINGS);
+ namespaceToFieldMap.put(DeviceConfig.NAMESPACE_BLOBSTORE,
+ ConfigSettingsProto.BLOBSTORE_SETTINGS);
namespaceToFieldMap.put(DeviceConfig.NAMESPACE_CONNECTIVITY,
ConfigSettingsProto.CONNECTIVITY_SETTINGS);
namespaceToFieldMap.put(DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 617ed4e..30b461d 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -180,6 +180,8 @@
<!-- Adding Controls to SystemUI -->
<uses-permission android:name="android.permission.BIND_CONTROLS" />
+ <!-- Check foreground controls applications -->
+ <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
<!-- Quick Settings tile: Night Mode / Dark Theme -->
<uses-permission android:name="android.permission.MODIFY_DAY_NIGHT_MODE" />
@@ -686,6 +688,25 @@
android:visibleToInstantApps="true">
</activity>
+ <receiver android:name=".controls.management.ControlsRequestReceiver">
+ <intent-filter>
+ <action android:name="android.service.controls.action.ADD_CONTROL" />
+ </intent-filter>
+ </receiver>
+
+ <!-- started from ControlsFavoritingActivity -->
+ <activity
+ android:name=".controls.management.ControlsRequestDialog"
+ android:exported="true"
+ android:theme="@style/Theme.ControlsRequestDialog"
+ android:finishOnCloseSystemDialogs="true"
+ android:showForAllUsers="true"
+ android:clearTaskOnLaunch="true"
+ android:launchMode="singleTask"
+ android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
+ android:excludeFromRecents="true"
+ android:visibleToInstantApps="true"/>
+
<!-- Doze with notifications, run in main sysui process for every user -->
<service
android:name=".doze.DozeService"
diff --git a/packages/SystemUI/res/layout-land-television/volume_dialog.xml b/packages/SystemUI/res/layout-land-television/volume_dialog.xml
new file mode 100644
index 0000000..e0d158d
--- /dev/null
+++ b/packages/SystemUI/res/layout-land-television/volume_dialog.xml
@@ -0,0 +1,101 @@
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:sysui="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/volume_dialog_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@android:color/transparent"
+ android:theme="@style/qs_theme">
+
+ <FrameLayout
+ android:id="@+id/volume_dialog"
+ android:minWidth="@dimen/volume_dialog_panel_width"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right"
+ android:background="@android:color/transparent"
+ android:paddingRight="@dimen/volume_dialog_panel_transparent_padding_right"
+ android:paddingTop="@dimen/volume_dialog_panel_transparent_padding"
+ android:paddingBottom="@dimen/volume_dialog_panel_transparent_padding"
+ android:paddingLeft="@dimen/volume_dialog_panel_transparent_padding"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:id="@+id/main"
+ android:layout_width="wrap_content"
+ android:minWidth="@dimen/volume_dialog_panel_width"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="68dp"
+ android:layout_gravity="right"
+ android:orientation="vertical"
+ android:translationZ="@dimen/volume_dialog_elevation"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:background="@drawable/rounded_bg_full">
+
+ <LinearLayout
+ android:id="@+id/volume_dialog_rows"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minWidth="@dimen/volume_dialog_panel_width"
+ android:gravity="center"
+ android:orientation="horizontal"
+ android:paddingRight="@dimen/volume_dialog_stream_padding"
+ android:paddingLeft="@dimen/volume_dialog_stream_padding">
+ <!-- volume rows added and removed here! :-) -->
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <FrameLayout
+ android:id="@+id/odi_captions"
+ android:layout_width="@dimen/volume_dialog_caption_size"
+ android:layout_height="@dimen/volume_dialog_caption_size"
+ android:layout_marginRight="68dp"
+ android:layout_gravity="right"
+ android:clipToPadding="false"
+ android:translationZ="@dimen/volume_dialog_elevation"
+ android:background="@drawable/rounded_bg_full">
+
+ <com.android.systemui.volume.CaptionsToggleImageButton
+ android:id="@+id/odi_captions_icon"
+ android:src="@drawable/ic_volume_odi_captions_disabled"
+ style="@style/VolumeButtons"
+ android:background="@drawable/rounded_ripple"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:tint="@color/caption_tint_color_selector"
+ android:layout_gravity="center"
+ android:soundEffectsEnabled="false"
+ sysui:optedOut="false"/>
+
+ </FrameLayout>
+
+ <ViewStub
+ android:id="@+id/odi_captions_tooltip_stub"
+ android:inflatedId="@+id/odi_captions_tooltip_view"
+ android:layout="@layout/volume_tool_tip_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="@dimen/volume_tool_tip_right_margin"
+ android:layout_marginTop="@dimen/volume_tool_tip_top_margin"
+ android:layout_gravity="right"/>
+
+ </FrameLayout>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/controls_dialog.xml b/packages/SystemUI/res/layout/controls_dialog.xml
new file mode 100644
index 0000000..3effaf5
--- /dev/null
+++ b/packages/SystemUI/res/layout/controls_dialog.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="@dimen/controls_dialog_padding"
+ android:layout_margin="@dimen/controls_dialog_padding"
+ >
+
+ <include
+ android:id="@+id/control"
+ layout="@layout/controls_base_item"
+ android:layout_width="@dimen/controls_dialog_control_width"
+ android:layout_height="@dimen/control_height"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginTop="@dimen/controls_dialog_padding"
+ android:layout_marginBottom="@dimen/controls_dialog_padding"
+ />
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-television/integers.xml b/packages/SystemUI/res/values-television/integers.xml
new file mode 100644
index 0000000..91e83cc
--- /dev/null
+++ b/packages/SystemUI/res/values-television/integers.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<resources>
+ <!-- The position of the volume dialog on the screen.
+ See com.android.systemui.volume.VolumeDialogImpl.
+ Value 81 corresponds to BOTTOM|CENTER_HORIZONTAL. -->
+ <integer name="volume_dialog_gravity">81</integer>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 2dc0f5f..7aaf6f9 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1249,6 +1249,10 @@
<dimen name="controls_app_divider_side_margin">32dp</dimen>
<dimen name="controls_card_margin">2dp</dimen>
+ <item name="control_card_elevation" type="dimen" format="float">15</item>
+
+ <dimen name="controls_dialog_padding">8dp</dimen>
+ <dimen name="controls_dialog_control_width">200dp</dimen>
<!-- Screen Record -->
<dimen name="screenrecord_dialog_padding">18dp</dimen>
diff --git a/packages/SystemUI/res/values/integers.xml b/packages/SystemUI/res/values/integers.xml
index 4171cd9..f35f351 100644
--- a/packages/SystemUI/res/values/integers.xml
+++ b/packages/SystemUI/res/values/integers.xml
@@ -40,4 +40,8 @@
<integer name="magnification_default_scale">2</integer>
+ <!-- The position of the volume dialog on the screen.
+ See com.android.systemui.volume.VolumeDialogImpl.
+ Value 21 corresponds to RIGHT|CENTER_VERTICAL. -->
+ <integer name="volume_dialog_gravity">21</integer>
</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index bcf3a26..5b28479 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2639,4 +2639,11 @@
<string name="controls_favorite_load_error">The list of all controls could not be loaded.</string>
<!-- Controls management controls screen header for Other zone [CHAR LIMIT=60] -->
<string name="controls_favorite_other_zone_header">Other</string>
+
+ <!-- Controls dialog title [CHAR LIMIT=30] -->
+ <string name="controls_dialog_title">Add to Quick Controls</string>
+ <!-- Controls dialog add to favorites [CHAR LIMIT=30] -->
+ <string name="controls_dialog_ok">Add to favorites</string>
+ <!-- Controls dialog message [CHAR LIMIT=NONE] -->
+ <string name="controls_dialog_message"><xliff:g id="app" example="System UI">%s</xliff:g> suggested this control to add to your favorites.</string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 2eccf58..125dd8f 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -697,4 +697,7 @@
<!-- used to override dark/light theming -->
<item name="*android:colorPopupBackground">@color/control_list_popup_background</item>
</style>
+
+ <style name="Theme.ControlsRequestDialog" parent="@style/Theme.SystemUI.MediaProjectionAlertDialog"/>
+
</resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
new file mode 100644
index 0000000..fc29f5c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewRootImpl;
+
+import com.android.systemui.keyguard.DismissCallbackRegistry;
+import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.statusbar.phone.BiometricUnlockController;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.phone.NotificationPanelViewController;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+/**
+ * Interface to control Keyguard View. It should be implemented by KeyguardViewManagers, which
+ * should, in turn, be injected into {@link KeyguardViewMediator}.
+ */
+public interface KeyguardViewController {
+ /**
+ * Shows Keyguard.
+ * @param options
+ */
+ void show(Bundle options);
+
+ /**
+ * Hides Keyguard with the fade-out animation as configured by the parameters provided.
+ *
+ * @param startTime
+ * @param fadeoutDuration
+ */
+ void hide(long startTime, long fadeoutDuration);
+
+ /**
+ * Resets the state of Keyguard View.
+ * @param hideBouncerWhenShowing
+ */
+ void reset(boolean hideBouncerWhenShowing);
+
+ /**
+ * Called when the device started going to sleep.
+ */
+ void onStartedGoingToSleep();
+
+ /**
+ * Called when the device has finished going to sleep.
+ */
+ void onFinishedGoingToSleep();
+
+ /**
+ * Called when the device started waking up.
+ */
+ void onStartedWakingUp();
+
+ /**
+ * Called when the device started turning on.
+ */
+ void onScreenTurningOn();
+
+ /**
+ * Called when the device has finished turning on.
+ */
+ void onScreenTurnedOn();
+
+ /**
+ * Sets whether the Keyguard needs input.
+ * @param needsInput
+ */
+ void setNeedsInput(boolean needsInput);
+
+ /**
+ * Called when cancel button in bouncer is pressed.
+ */
+ void onCancelClicked();
+
+ /**
+ * Sets whether the keyguard is occluded by another window.
+ *
+ * @param occluded
+ * @param animate
+ */
+ void setOccluded(boolean occluded, boolean animate);
+
+ /**
+ * @return Whether the keyguard is showing
+ */
+ boolean isShowing();
+
+ /**
+ * Dismisses the keyguard by going to the next screen or making it gone.
+ */
+ void dismissAndCollapse();
+
+ /**
+ * Notifies that Keyguard is just about to go away.
+ */
+ void keyguardGoingAway();
+
+ /**
+ * @return Whether window animation for unlock should be disabled.
+ */
+ boolean shouldDisableWindowAnimationsForUnlock();
+
+ /**
+ * @return Whether the keyguard is going to notification shade.
+ */
+ boolean isGoingToNotificationShade();
+
+ /**
+ * @return Whether subtle animation should be used for unlocking the device.
+ */
+ boolean isUnlockWithWallpaper();
+
+ /**
+ * @return Whether subtle animation should be used for unlocking the device.
+ */
+ boolean shouldSubtleWindowAnimationsForUnlock();
+
+ /**
+ * Starts the animation before we dismiss Keyguard, i.e. an disappearing animation on the
+ * security view of the bouncer.
+ *
+ * @param finishRunnable the runnable to be run after the animation finished, or {@code null} if
+ * no action should be run
+ */
+ void startPreHideAnimation(Runnable finishRunnable);
+
+ /**
+ * @return the ViewRootImpl of the View where the Keyguard is mounted.
+ */
+ ViewRootImpl getViewRootImpl();
+
+ // TODO: Deprecate registerStatusBar in KeyguardViewController interface. It is currently
+ // only used for testing purposes in StatusBarKeyguardViewManager, and it prevents us from
+ // achieving complete abstraction away from where the Keyguard View is mounted.
+
+ /**
+ * Registers the StatusBar to which this Keyguard View is mounted.
+ *
+ * @param statusBar
+ * @param container
+ * @param notificationPanelViewController
+ * @param biometricUnlockController
+ * @param dismissCallbackRegistry
+ * @param lockIconContainer
+ * @param notificationContainer
+ * @param bypassController
+ * @param falsingManager
+ */
+ void registerStatusBar(StatusBar statusBar,
+ ViewGroup container,
+ NotificationPanelViewController notificationPanelViewController,
+ BiometricUnlockController biometricUnlockController,
+ DismissCallbackRegistry dismissCallbackRegistry,
+ ViewGroup lockIconContainer, View notificationContainer,
+ KeyguardBypassController bypassController, FalsingManager falsingManager);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index a868cf5..b6152da 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -71,12 +71,11 @@
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.notification.NotificationAlertingManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
import com.android.systemui.statusbar.notification.NotificationFilter;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.interruption.NotificationAlertingManager;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ChannelEditorDialogController;
import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
@@ -289,7 +288,6 @@
@Inject Lazy<NotificationLogger> mNotificationLogger;
@Inject Lazy<NotificationViewHierarchyManager> mNotificationViewHierarchyManager;
@Inject Lazy<NotificationFilter> mNotificationFilter;
- @Inject Lazy<NotificationInterruptionStateProvider> mNotificationInterruptionStateProvider;
@Inject Lazy<KeyguardDismissUtil> mKeyguardDismissUtil;
@Inject Lazy<SmartReplyController> mSmartReplyController;
@Inject Lazy<RemoteInputQuickSettingsDisabler> mRemoteInputQuickSettingsDisabler;
@@ -489,8 +487,6 @@
mProviders.put(NotificationViewHierarchyManager.class,
mNotificationViewHierarchyManager::get);
mProviders.put(NotificationFilter.class, mNotificationFilter::get);
- mProviders.put(NotificationInterruptionStateProvider.class,
- mNotificationInterruptionStateProvider::get);
mProviders.put(KeyguardDismissUtil.class, mKeyguardDismissUtil::get);
mProviders.put(SmartReplyController.class, mSmartReplyController::get);
mProviders.put(RemoteInputQuickSettingsDisabler.class,
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 48457f6..f873f42 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -83,11 +83,11 @@
import com.android.systemui.statusbar.NotificationRemoveInterceptor;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.ShadeController;
@@ -169,7 +169,7 @@
// Callback that updates BubbleOverflowActivity on data change.
@Nullable private Runnable mOverflowCallback = null;
- private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
+ private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private IStatusBarService mBarService;
// Used for determining view rect for touch interaction
@@ -279,7 +279,7 @@
ShadeController shadeController,
BubbleData data,
ConfigurationController configurationController,
- NotificationInterruptionStateProvider interruptionStateProvider,
+ NotificationInterruptStateProvider interruptionStateProvider,
ZenModeController zenModeController,
NotificationLockscreenUserManager notifUserManager,
NotificationGroupManager groupManager,
@@ -304,7 +304,7 @@
BubbleData data,
@Nullable BubbleStackView.SurfaceSynchronizer synchronizer,
ConfigurationController configurationController,
- NotificationInterruptionStateProvider interruptionStateProvider,
+ NotificationInterruptStateProvider interruptionStateProvider,
ZenModeController zenModeController,
NotificationLockscreenUserManager notifUserManager,
NotificationGroupManager groupManager,
@@ -316,7 +316,7 @@
dumpManager.registerDumpable(TAG, this);
mContext = context;
mShadeController = shadeController;
- mNotificationInterruptionStateProvider = interruptionStateProvider;
+ mNotificationInterruptStateProvider = interruptionStateProvider;
mNotifUserManager = notifUserManager;
mZenModeController = zenModeController;
mFloatingContentCoordinator = floatingContentCoordinator;
@@ -632,7 +632,7 @@
for (NotificationEntry e :
mNotificationEntryManager.getActiveNotificationsForCurrentUser()) {
if (savedBubbleKeys.contains(e.getKey())
- && mNotificationInterruptionStateProvider.shouldBubbleUp(e)
+ && mNotificationInterruptStateProvider.shouldBubbleUp(e)
&& canLaunchInActivityView(mContext, e)) {
updateBubble(e, /* suppressFlyout= */ true);
}
@@ -894,7 +894,7 @@
boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments(
mContext, entry, previouslyUserCreated, userBlocked);
- if (mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
+ if (mNotificationInterruptStateProvider.shouldBubbleUp(entry)
&& (canLaunchInActivityView(mContext, entry) || wasAdjusted)) {
if (wasAdjusted && !previouslyUserCreated) {
// Gotta treat the auto-bubbled / whitelisted packaged bubbles as usercreated
@@ -910,7 +910,7 @@
boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments(
mContext, entry, previouslyUserCreated, userBlocked);
- boolean shouldBubble = mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
+ boolean shouldBubble = mNotificationInterruptStateProvider.shouldBubbleUp(entry)
&& (canLaunchInActivityView(mContext, entry) || wasAdjusted);
if (!shouldBubble && mBubbleData.hasBubbleWithKey(entry.getKey())) {
// It was previously a bubble but no longer a bubble -- lets remove it
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
index ac97d8a..27c9e98 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
@@ -25,8 +25,8 @@
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.ShadeController;
@@ -54,7 +54,7 @@
ShadeController shadeController,
BubbleData data,
ConfigurationController configurationController,
- NotificationInterruptionStateProvider interruptionStateProvider,
+ NotificationInterruptStateProvider interruptionStateProvider,
ZenModeController zenModeController,
NotificationLockscreenUserManager notifUserManager,
NotificationGroupManager groupManager,
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
index f2881d4..7eafe2e 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
@@ -124,6 +124,18 @@
fun getFavoritesForComponent(componentName: ComponentName): List<StructureInfo>
/**
+ * Adds a single favorite to a given component and structure
+ * @param componentName the name of the service that provides the [Control]
+ * @param structureName the name of the structure that holds the [Control]
+ * @param controlInfo persistent information about the [Control] to be added.
+ */
+ fun addFavorite(
+ componentName: ComponentName,
+ structureName: CharSequence,
+ controlInfo: ControlInfo
+ )
+
+ /**
* Replaces the favorites for the given structure.
*
* Calling this method will eliminate the previous selection of favorites and replace it with a
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index dedd341..f818d19 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -300,6 +300,19 @@
bindingController.unsubscribe()
}
+ override fun addFavorite(
+ componentName: ComponentName,
+ structureName: CharSequence,
+ controlInfo: ControlInfo
+ ) {
+ if (!confirmAvailability()) return
+ executor.execute {
+ if (Favorites.addFavorite(componentName, structureName, controlInfo)) {
+ persistenceWrapper.storeFavorites(Favorites.getAllStructures())
+ }
+ }
+ }
+
override fun replaceFavoritesForStructure(structureInfo: StructureInfo) {
if (!confirmAvailability()) return
executor.execute {
@@ -437,6 +450,24 @@
favMap = newFavMap
}
+ fun addFavorite(
+ componentName: ComponentName,
+ structureName: CharSequence,
+ controlInfo: ControlInfo
+ ): Boolean {
+ // Check if control is in favorites
+ if (getControlsForComponent(componentName)
+ .any { it.controlId == controlInfo.controlId }) {
+ return false
+ }
+ val structureInfo = favMap.get(componentName)
+ ?.firstOrNull { it.structure == structureName }
+ ?: StructureInfo(componentName, structureName, emptyList())
+ val newStructureInfo = structureInfo.copy(controls = structureInfo.controls + controlInfo)
+ replaceControls(newStructureInfo)
+ return true
+ }
+
fun replaceControls(updatedStructure: StructureInfo) {
val newFavMap = favMap.toMutableMap()
val structures = mutableListOf<StructureInfo>()
@@ -456,8 +487,8 @@
structures.add(updatedStructure)
}
- newFavMap.put(componentName, structures.toList())
- favMap = newFavMap.toMap()
+ newFavMap.put(componentName, structures)
+ favMap = newFavMap
}
fun clear() {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
index 859311e..946a236 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
@@ -26,6 +26,7 @@
import com.android.systemui.controls.management.ControlsListingController
import com.android.systemui.controls.management.ControlsListingControllerImpl
import com.android.systemui.controls.management.ControlsProviderSelectorActivity
+import com.android.systemui.controls.management.ControlsRequestDialog
import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.controls.ui.ControlsUiControllerImpl
import dagger.Binds
@@ -69,4 +70,11 @@
abstract fun provideControlsFavoritingActivity(
activity: ControlsFavoritingActivity
): Activity
+
+ @Binds
+ @IntoMap
+ @ClassKey(ControlsRequestDialog::class)
+ abstract fun provideControlsRequestDialog(
+ activity: ControlsRequestDialog
+ ): Activity
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
index e87cf74..c21f724 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
@@ -42,7 +42,8 @@
* @param onlyFavorites set to true to only display favorites instead of all controls
*/
class ControlAdapter(
- private val layoutInflater: LayoutInflater
+ private val layoutInflater: LayoutInflater,
+ private val elevation: Float
) : RecyclerView.Adapter<Holder>() {
companion object {
@@ -66,7 +67,7 @@
layoutParams.apply {
width = ViewGroup.LayoutParams.MATCH_PARENT
}
- elevation = 15f
+ elevation = this@ControlAdapter.elevation
}
) { id, favorite ->
model?.changeFavoriteStatus(id, favorite)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
index 08a1a500..471f9d3 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
@@ -142,8 +142,9 @@
val margin = resources.getDimensionPixelSize(R.dimen.controls_card_margin)
val itemDecorator = MarginItemDecorator(margin, margin)
val layoutInflater = LayoutInflater.from(applicationContext)
+ val elevation = resources.getFloat(R.dimen.control_card_elevation)
- adapterAll = ControlAdapter(layoutInflater)
+ adapterAll = ControlAdapter(layoutInflater, elevation)
recyclerViewAll = requireViewById<RecyclerView>(R.id.listAll).apply {
adapter = adapterAll
layoutManager = GridLayoutManager(applicationContext, 2).apply {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt
new file mode 100644
index 0000000..463632b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls.management
+
+import android.app.AlertDialog
+import android.app.Dialog
+import android.content.ComponentName
+import android.content.DialogInterface
+import android.content.Intent
+import android.graphics.drawable.Icon
+import android.os.Bundle
+import android.os.UserHandle
+import android.service.controls.Control
+import android.service.controls.ControlsProviderService
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.ImageView
+import android.widget.TextView
+import com.android.systemui.R
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.controls.ControlsServiceInfo
+import com.android.systemui.controls.controller.ControlInfo
+import com.android.systemui.controls.controller.ControlsController
+import com.android.systemui.controls.ui.RenderInfo
+import com.android.systemui.settings.CurrentUserTracker
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.util.LifecycleActivity
+import javax.inject.Inject
+
+class ControlsRequestDialog @Inject constructor(
+ private val controller: ControlsController,
+ private val broadcastDispatcher: BroadcastDispatcher,
+ private val controlsListingController: ControlsListingController
+) : LifecycleActivity(), DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
+
+ companion object {
+ private const val TAG = "ControlsRequestDialog"
+ }
+
+ private lateinit var component: ComponentName
+ private lateinit var control: Control
+ private var dialog: Dialog? = null
+ private val callback = object : ControlsListingController.ControlsListingCallback {
+ override fun onServicesUpdated(candidates: List<ControlsServiceInfo>) {}
+ }
+
+ private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) {
+ private val startingUser = controller.currentUserId
+
+ override fun onUserSwitched(newUserId: Int) {
+ if (newUserId != startingUser) {
+ stopTracking()
+ finish()
+ }
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ if (!controller.available) {
+ Log.w(TAG, "Quick Controls not available for this user ")
+ finish()
+ }
+ currentUserTracker.startTracking()
+ controlsListingController.addCallback(callback)
+
+ val requestUser = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.USER_NULL)
+ val currentUser = controller.currentUserId
+
+ if (requestUser != currentUser) {
+ Log.w(TAG, "Current user ($currentUser) different from request user ($requestUser)")
+ finish()
+ }
+
+ component = intent.getParcelableExtra(Intent.EXTRA_COMPONENT_NAME) ?: run {
+ Log.e(TAG, "Request did not contain componentName")
+ finish()
+ return
+ }
+
+ control = intent.getParcelableExtra(ControlsProviderService.EXTRA_CONTROL) ?: run {
+ Log.e(TAG, "Request did not contain control")
+ finish()
+ return
+ }
+ }
+
+ override fun onResume() {
+ super.onResume()
+ val label = verifyComponentAndGetLabel()
+ if (label == null) {
+ Log.e(TAG, "The component specified (${component.flattenToString()} " +
+ "is not a valid ControlsProviderService")
+ finish()
+ return
+ }
+
+ if (isCurrentFavorite()) {
+ Log.w(TAG, "The control ${control.title} is already a favorite")
+ finish()
+ }
+
+ dialog = createDialog(label)
+
+ dialog?.show()
+ }
+
+ override fun onDestroy() {
+ dialog?.dismiss()
+ currentUserTracker.stopTracking()
+ controlsListingController.removeCallback(callback)
+ super.onDestroy()
+ }
+
+ private fun verifyComponentAndGetLabel(): CharSequence? {
+ return controlsListingController.getAppLabel(component)
+ }
+
+ private fun isCurrentFavorite(): Boolean {
+ val favorites = controller.getFavoritesForComponent(component)
+ return favorites.any { it.controls.any { it.controlId == control.controlId } }
+ }
+
+ fun createDialog(label: CharSequence): Dialog {
+
+ val renderInfo = RenderInfo.lookup(control.deviceType, true)
+ val frame = LayoutInflater.from(this).inflate(R.layout.controls_dialog, null).apply {
+ requireViewById<ImageView>(R.id.icon).apply {
+ setImageIcon(Icon.createWithResource(context, renderInfo.iconResourceId))
+ setImageTintList(
+ context.resources.getColorStateList(renderInfo.foreground, context.theme))
+ }
+ requireViewById<TextView>(R.id.title).text = control.title
+ requireViewById<TextView>(R.id.subtitle).text = control.subtitle
+ requireViewById<View>(R.id.control).elevation =
+ resources.getFloat(R.dimen.control_card_elevation)
+ }
+
+ val dialog = AlertDialog.Builder(this)
+ .setTitle(getString(R.string.controls_dialog_title))
+ .setMessage(getString(R.string.controls_dialog_message, label))
+ .setPositiveButton(R.string.controls_dialog_ok, this)
+ .setNegativeButton(android.R.string.cancel, this)
+ .setOnCancelListener(this)
+ .setView(frame)
+ .create()
+
+ SystemUIDialog.registerDismissListener(dialog)
+ dialog.setCanceledOnTouchOutside(true)
+ return dialog
+ }
+
+ override fun onCancel(dialog: DialogInterface?) {
+ finish()
+ }
+
+ override fun onClick(dialog: DialogInterface?, which: Int) {
+ if (which == Dialog.BUTTON_POSITIVE) {
+ controller.addFavorite(componentName, control.structure ?: "",
+ ControlInfo(control.controlId, control.title, control.deviceType))
+ }
+ finish()
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestReceiver.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestReceiver.kt
new file mode 100644
index 0000000..5c30b5a5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestReceiver.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls.management
+
+import android.app.ActivityManager
+import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
+import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE
+import android.content.BroadcastReceiver
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.os.UserHandle
+import android.service.controls.Control
+import android.service.controls.ControlsProviderService
+import android.util.Log
+
+/**
+ * Proxy to launch in user 0
+ */
+class ControlsRequestReceiver : BroadcastReceiver() {
+
+ companion object {
+ private const val TAG = "ControlsRequestReceiver"
+
+ fun isPackageInForeground(context: Context, packageName: String): Boolean {
+ val uid = try {
+ context.packageManager.getPackageUid(packageName, 0)
+ } catch (_: PackageManager.NameNotFoundException) {
+ Log.w(TAG, "Package $packageName not found")
+ return false
+ }
+
+ val am = context.getSystemService(ActivityManager::class.java)
+ if ((am?.getUidImportance(uid) ?: IMPORTANCE_GONE) != IMPORTANCE_FOREGROUND) {
+ Log.w(TAG, "Uid $uid not in foreground")
+ return false
+ }
+ return true
+ }
+ }
+
+ override fun onReceive(context: Context, intent: Intent) {
+
+ val packageName = intent.getParcelableExtra<ComponentName>(Intent.EXTRA_COMPONENT_NAME)
+ ?.packageName
+
+ if (packageName == null || !isPackageInForeground(context, packageName)) {
+ return
+ }
+
+ val activityIntent = Intent(context, ControlsRequestDialog::class.java).apply {
+ Intent.EXTRA_COMPONENT_NAME.let {
+ putExtra(it, intent.getParcelableExtra<ComponentName>(it))
+ }
+ ControlsProviderService.EXTRA_CONTROL.let {
+ putExtra(it, intent.getParcelableExtra<Control>(it))
+ }
+ }
+ activityIntent.putExtra(Intent.EXTRA_USER_ID, context.userId)
+
+ context.startActivityAsUser(activityIntent, UserHandle.SYSTEM)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index 6eed6b8..d56428d 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -25,7 +25,6 @@
import android.service.controls.actions.ControlAction
import android.service.controls.templates.ControlTemplate
import android.service.controls.templates.TemperatureControlTemplate
-import android.service.controls.templates.ThumbnailTemplate
import android.service.controls.templates.ToggleRangeTemplate
import android.service.controls.templates.ToggleTemplate
import android.util.Log
@@ -125,7 +124,6 @@
template is ToggleTemplate -> ToggleBehavior::class
template is ToggleRangeTemplate -> ToggleRangeBehavior::class
template is TemperatureControlTemplate -> TemperatureControlBehavior::class
- template is ThumbnailTemplate -> StaticBehavior::class
else -> DefaultBehavior::class
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/StaticBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/StaticBehavior.kt
deleted file mode 100644
index c006d6f..0000000
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/StaticBehavior.kt
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.controls.ui
-
-import android.graphics.drawable.ClipDrawable
-import android.graphics.drawable.LayerDrawable
-import android.service.controls.Control
-import android.service.controls.templates.ThumbnailTemplate
-
-import com.android.systemui.R
-import com.android.systemui.controls.ui.ControlActionCoordinator.MAX_LEVEL
-
-/**
- * Used for controls that cannot be interacted with. Information is presented to the user
- * but no actions can be taken. If using a ThumbnailTemplate, the background image will
- * be changed.
- */
-class StaticBehavior() : Behavior {
- lateinit var control: Control
- lateinit var cvh: ControlViewHolder
-
- override fun initialize(cvh: ControlViewHolder) {
- this.cvh = cvh
- }
-
- override fun bind(cws: ControlWithState) {
- this.control = cws.control!!
-
- cvh.status.setText(control.getStatusText())
-
- val ld = cvh.layout.getBackground() as LayerDrawable
- val clipLayer = ld.findDrawableByLayerId(R.id.clip_layer) as ClipDrawable
-
- clipLayer.setLevel(MAX_LEVEL)
- cvh.setEnabled(true)
- cvh.applyRenderInfo(RenderInfo.lookup(control.getDeviceType(), true))
-
- val template = control.getControlTemplate()
- if (template is ThumbnailTemplate) {
- cvh.bgExecutor.execute {
- // clear the default tinting in favor of only using alpha
- val drawable = template.getThumbnail().loadDrawable(cvh.context)
- drawable.setTintList(null)
- drawable.setAlpha((0.45 * 255).toInt())
- cvh.uiExecutor.execute {
- val radius = cvh.context.getResources()
- .getDimensionPixelSize(R.dimen.control_corner_radius).toFloat()
- clipLayer.setDrawable(CornerDrawable(drawable, radius))
- }
- }
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index 6c502d2..3a4b273 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -90,7 +90,7 @@
/** */
@Provides
- public AmbientDisplayConfiguration provideAmbientDispalyConfiguration(Context context) {
+ public AmbientDisplayConfiguration provideAmbientDisplayConfiguration(Context context) {
return new AmbientDisplayConfiguration(context);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index 3e257b6..b4e5125 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -23,6 +23,7 @@
import androidx.annotation.Nullable;
+import com.android.keyguard.KeyguardViewController;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerImpl;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -41,6 +42,7 @@
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.ShadeControllerImpl;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
@@ -117,4 +119,8 @@
@Binds
abstract DeviceProvisionedController bindDeviceProvisionedController(
DeviceProvisionedControllerImpl deviceProvisionedController);
+
+ @Binds
+ abstract KeyguardViewController bindKeyguardViewController(
+ StatusBarKeyguardViewManager statusBarKeyguardViewManager);
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index c129035..3d708a9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -80,6 +80,7 @@
import com.android.keyguard.KeyguardSecurityView;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.keyguard.KeyguardViewController;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
@@ -95,7 +96,6 @@
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.InjectionInflationController;
@@ -236,7 +236,7 @@
*/
private PowerManager.WakeLock mShowKeyguardWakeLock;
- private final Lazy<StatusBarKeyguardViewManager> mStatusBarKeyguardViewManagerLazy;
+ private final Lazy<KeyguardViewController> mKeyguardViewControllerLazy;
// these are protected by synchronized (this)
@@ -601,7 +601,7 @@
@Override
public void setNeedsInput(boolean needsInput) {
- mStatusBarKeyguardViewManagerLazy.get().setNeedsInput(needsInput);
+ mKeyguardViewControllerLazy.get().setNeedsInput(needsInput);
}
@Override
@@ -615,7 +615,7 @@
mKeyguardDonePending = true;
mHideAnimationRun = true;
mHideAnimationRunning = true;
- mStatusBarKeyguardViewManagerLazy.get()
+ mKeyguardViewControllerLazy.get()
.startPreHideAnimation(mHideAnimationFinishedRunnable);
mHandler.sendEmptyMessageDelayed(KEYGUARD_DONE_PENDING_TIMEOUT,
KEYGUARD_DONE_PENDING_TIMEOUT_MS);
@@ -647,7 +647,7 @@
@Override
public void onCancelClicked() {
- mStatusBarKeyguardViewManagerLazy.get().onCancelClicked();
+ mKeyguardViewControllerLazy.get().onCancelClicked();
}
@Override
@@ -715,7 +715,7 @@
LockPatternUtils lockPatternUtils,
BroadcastDispatcher broadcastDispatcher,
NotificationShadeWindowController notificationShadeWindowController,
- Lazy<StatusBarKeyguardViewManager> statusBarKeyguardViewManagerLazy,
+ Lazy<KeyguardViewController> statusBarKeyguardViewManagerLazy,
DismissCallbackRegistry dismissCallbackRegistry,
KeyguardUpdateMonitor keyguardUpdateMonitor, DumpManager dumpManager,
@UiBackground Executor uiBgExecutor, PowerManager powerManager,
@@ -726,7 +726,7 @@
mLockPatternUtils = lockPatternUtils;
mBroadcastDispatcher = broadcastDispatcher;
mNotificationShadeWindowController = notificationShadeWindowController;
- mStatusBarKeyguardViewManagerLazy = statusBarKeyguardViewManagerLazy;
+ mKeyguardViewControllerLazy = statusBarKeyguardViewManagerLazy;
mDismissCallbackRegistry = dismissCallbackRegistry;
mUiBgExecutor = uiBgExecutor;
mUpdateMonitor = keyguardUpdateMonitor;
@@ -1288,7 +1288,7 @@
if (mOccluded != isOccluded) {
mOccluded = isOccluded;
mUpdateMonitor.setKeyguardOccluded(isOccluded);
- mStatusBarKeyguardViewManagerLazy.get().setOccluded(isOccluded, animate
+ mKeyguardViewControllerLazy.get().setOccluded(isOccluded, animate
&& mDeviceInteractive);
adjustStatusBarLocked();
}
@@ -1359,7 +1359,7 @@
}
// if the keyguard is already showing, don't bother
- if (mStatusBarKeyguardViewManagerLazy.get().isShowing()) {
+ if (mKeyguardViewControllerLazy.get().isShowing()) {
if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
resetStateLocked();
return;
@@ -1423,7 +1423,7 @@
mDismissCallbackRegistry.addCallback(callback);
}
mCustomMessage = message;
- mStatusBarKeyguardViewManagerLazy.get().dismissAndCollapse();
+ mKeyguardViewControllerLazy.get().dismissAndCollapse();
} else if (callback != null) {
new DismissCallbackWrapper(callback).notifyDismissError();
}
@@ -1690,7 +1690,7 @@
} else if (!mHideAnimationRun) {
mHideAnimationRun = true;
mHideAnimationRunning = true;
- mStatusBarKeyguardViewManagerLazy.get()
+ mKeyguardViewControllerLazy.get()
.startPreHideAnimation(mHideAnimationFinishedRunnable);
}
}
@@ -1847,7 +1847,7 @@
mHiding = false;
mWakeAndUnlocking = false;
setShowingLocked(true);
- mStatusBarKeyguardViewManagerLazy.get().show(options);
+ mKeyguardViewControllerLazy.get().show(options);
resetKeyguardDonePendingLocked();
mHideAnimationRun = false;
adjustStatusBarLocked();
@@ -1872,22 +1872,22 @@
public void run() {
Trace.beginSection("KeyguardViewMediator.mKeyGuardGoingAwayRunnable");
if (DEBUG) Log.d(TAG, "keyguardGoingAway");
- mStatusBarKeyguardViewManagerLazy.get().keyguardGoingAway();
+ mKeyguardViewControllerLazy.get().keyguardGoingAway();
int flags = 0;
- if (mStatusBarKeyguardViewManagerLazy.get().shouldDisableWindowAnimationsForUnlock()
+ if (mKeyguardViewControllerLazy.get().shouldDisableWindowAnimationsForUnlock()
|| (mWakeAndUnlocking && !mPulsing)) {
flags |= WindowManagerPolicyConstants
.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
}
- if (mStatusBarKeyguardViewManagerLazy.get().isGoingToNotificationShade()
+ if (mKeyguardViewControllerLazy.get().isGoingToNotificationShade()
|| (mWakeAndUnlocking && mPulsing)) {
flags |= WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
}
- if (mStatusBarKeyguardViewManagerLazy.get().isUnlockWithWallpaper()) {
+ if (mKeyguardViewControllerLazy.get().isUnlockWithWallpaper()) {
flags |= WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
}
- if (mStatusBarKeyguardViewManagerLazy.get().shouldSubtleWindowAnimationsForUnlock()) {
+ if (mKeyguardViewControllerLazy.get().shouldSubtleWindowAnimationsForUnlock()) {
flags |= WindowManagerPolicyConstants
.KEYGUARD_GOING_AWAY_FLAG_SUBTLE_WINDOW_ANIMATIONS;
}
@@ -1973,7 +1973,7 @@
// Hack level over 9000: To speed up wake-and-unlock sequence, force it to report
// the next draw from here so we don't have to wait for window manager to signal
// this to our ViewRootImpl.
- mStatusBarKeyguardViewManagerLazy.get().getViewRootImpl().setReportNextDraw();
+ mKeyguardViewControllerLazy.get().getViewRootImpl().setReportNextDraw();
notifyDrawn(mDrawnCallback);
mDrawnCallback = null;
}
@@ -1987,7 +1987,7 @@
setShowingLocked(false);
mWakeAndUnlocking = false;
mDismissCallbackRegistry.notifyDismissSucceeded();
- mStatusBarKeyguardViewManagerLazy.get().hide(startTime, fadeoutDuration);
+ mKeyguardViewControllerLazy.get().hide(startTime, fadeoutDuration);
resetKeyguardDonePendingLocked();
mHideAnimationRun = false;
adjustStatusBarLocked();
@@ -2036,7 +2036,7 @@
private void handleReset() {
synchronized (KeyguardViewMediator.this) {
if (DEBUG) Log.d(TAG, "handleReset");
- mStatusBarKeyguardViewManagerLazy.get().reset(true /* hideBouncerWhenShowing */);
+ mKeyguardViewControllerLazy.get().reset(true /* hideBouncerWhenShowing */);
}
}
@@ -2049,7 +2049,7 @@
synchronized (KeyguardViewMediator.this) {
if (DEBUG) Log.d(TAG, "handleVerifyUnlock");
setShowingLocked(true);
- mStatusBarKeyguardViewManagerLazy.get().dismissAndCollapse();
+ mKeyguardViewControllerLazy.get().dismissAndCollapse();
}
Trace.endSection();
}
@@ -2057,7 +2057,7 @@
private void handleNotifyStartedGoingToSleep() {
synchronized (KeyguardViewMediator.this) {
if (DEBUG) Log.d(TAG, "handleNotifyStartedGoingToSleep");
- mStatusBarKeyguardViewManagerLazy.get().onStartedGoingToSleep();
+ mKeyguardViewControllerLazy.get().onStartedGoingToSleep();
}
}
@@ -2068,7 +2068,7 @@
private void handleNotifyFinishedGoingToSleep() {
synchronized (KeyguardViewMediator.this) {
if (DEBUG) Log.d(TAG, "handleNotifyFinishedGoingToSleep");
- mStatusBarKeyguardViewManagerLazy.get().onFinishedGoingToSleep();
+ mKeyguardViewControllerLazy.get().onFinishedGoingToSleep();
}
}
@@ -2076,7 +2076,7 @@
Trace.beginSection("KeyguardViewMediator#handleMotifyStartedWakingUp");
synchronized (KeyguardViewMediator.this) {
if (DEBUG) Log.d(TAG, "handleNotifyWakingUp");
- mStatusBarKeyguardViewManagerLazy.get().onStartedWakingUp();
+ mKeyguardViewControllerLazy.get().onStartedWakingUp();
}
Trace.endSection();
}
@@ -2085,7 +2085,7 @@
Trace.beginSection("KeyguardViewMediator#handleNotifyScreenTurningOn");
synchronized (KeyguardViewMediator.this) {
if (DEBUG) Log.d(TAG, "handleNotifyScreenTurningOn");
- mStatusBarKeyguardViewManagerLazy.get().onScreenTurningOn();
+ mKeyguardViewControllerLazy.get().onScreenTurningOn();
if (callback != null) {
if (mWakeAndUnlocking) {
mDrawnCallback = callback;
@@ -2104,7 +2104,7 @@
}
synchronized (this) {
if (DEBUG) Log.d(TAG, "handleNotifyScreenTurnedOn");
- mStatusBarKeyguardViewManagerLazy.get().onScreenTurnedOn();
+ mKeyguardViewControllerLazy.get().onScreenTurnedOn();
}
Trace.endSection();
}
@@ -2148,14 +2148,26 @@
Trace.endSection();
}
- public StatusBarKeyguardViewManager registerStatusBar(StatusBar statusBar,
+ /**
+ * Registers the StatusBar to which the Keyguard View is mounted.
+ *
+ * @param statusBar
+ * @param container
+ * @param panelView
+ * @param biometricUnlockController
+ * @param lockIconContainer
+ * @param notificationContainer
+ * @param bypassController
+ * @return the View Controller for the Keyguard View this class is mediating.
+ */
+ public KeyguardViewController registerStatusBar(StatusBar statusBar,
ViewGroup container, NotificationPanelViewController panelView,
BiometricUnlockController biometricUnlockController, ViewGroup lockIconContainer,
View notificationContainer, KeyguardBypassController bypassController) {
- mStatusBarKeyguardViewManagerLazy.get().registerStatusBar(statusBar, container, panelView,
+ mKeyguardViewControllerLazy.get().registerStatusBar(statusBar, container, panelView,
biometricUnlockController, mDismissCallbackRegistry, lockIconContainer,
notificationContainer, bypassController, mFalsingManager);
- return mStatusBarKeyguardViewManagerLazy.get();
+ return mKeyguardViewControllerLazy.get();
}
public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 367f464..9be4786 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -22,6 +22,7 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardViewController;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.dump.DumpManager;
@@ -30,7 +31,6 @@
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.util.DeviceConfigProxy;
import java.util.concurrent.Executor;
@@ -57,7 +57,7 @@
LockPatternUtils lockPatternUtils,
BroadcastDispatcher broadcastDispatcher,
NotificationShadeWindowController notificationShadeWindowController,
- Lazy<StatusBarKeyguardViewManager> statusBarKeyguardViewManagerLazy,
+ Lazy<KeyguardViewController> statusBarKeyguardViewManagerLazy,
DismissCallbackRegistry dismissCallbackRegistry,
KeyguardUpdateMonitor updateMonitor,
DumpManager dumpManager,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowBlurController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowBlurController.kt
index aadc451..6e905a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowBlurController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowBlurController.kt
@@ -21,6 +21,9 @@
import android.animation.ValueAnimator
import android.view.Choreographer
import android.view.View
+import androidx.dynamicanimation.animation.FloatPropertyCompat
+import androidx.dynamicanimation.animation.SpringAnimation
+import androidx.dynamicanimation.animation.SpringForce
import com.android.internal.util.IndentingPrintWriter
import com.android.systemui.Dumpable
import com.android.systemui.Interpolators
@@ -59,6 +62,16 @@
private var notificationAnimator: Animator? = null
private var updateScheduled: Boolean = false
private var shadeExpansion = 1.0f
+ private val shadeSpring = SpringAnimation(this, object :
+ FloatPropertyCompat<NotificationShadeWindowBlurController>("shadeBlurRadius") {
+ override fun setValue(rect: NotificationShadeWindowBlurController?, value: Float) {
+ shadeBlurRadius = value.toInt()
+ }
+
+ override fun getValue(rect: NotificationShadeWindowBlurController?): Float {
+ return shadeBlurRadius.toFloat()
+ }
+ })
private var shadeBlurRadius = 0
set(value) {
if (field == value) return
@@ -135,6 +148,9 @@
if (WAKE_UP_ANIMATION_ENABLED) {
keyguardStateController.addCallback(keyguardStateCallback)
}
+ shadeSpring.spring = SpringForce(0.0f)
+ shadeSpring.spring.dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
+ shadeSpring.spring.stiffness = SpringForce.STIFFNESS_LOW
}
/**
@@ -153,8 +169,7 @@
if (shadeBlurRadius == newBlur) {
return
}
- shadeBlurRadius = newBlur
- scheduleUpdate()
+ shadeSpring.animateToFinalPosition(newBlur.toFloat())
}
private fun scheduleUpdate() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index 8a23e37..2747696 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.notification
import android.animation.ObjectAnimator
-import android.content.Context
import android.util.FloatProperty
import com.android.systemui.Interpolators
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -26,10 +25,10 @@
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
import com.android.systemui.statusbar.notification.stack.StackStateAnimator
import com.android.systemui.statusbar.phone.DozeParameters
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.NotificationIconAreaController
import com.android.systemui.statusbar.phone.PanelExpansionListener
+import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
import javax.inject.Inject
@@ -37,15 +36,14 @@
@Singleton
class NotificationWakeUpCoordinator @Inject constructor(
- private val mHeadsUpManagerPhone: HeadsUpManagerPhone,
- private val statusBarStateController: StatusBarStateController,
- private val bypassController: KeyguardBypassController,
- private val dozeParameters: DozeParameters)
- : OnHeadsUpChangedListener, StatusBarStateController.StateListener,
- PanelExpansionListener {
+ private val mHeadsUpManager: HeadsUpManager,
+ private val statusBarStateController: StatusBarStateController,
+ private val bypassController: KeyguardBypassController,
+ private val dozeParameters: DozeParameters
+) : OnHeadsUpChangedListener, StatusBarStateController.StateListener, PanelExpansionListener {
- private val mNotificationVisibility
- = object : FloatProperty<NotificationWakeUpCoordinator>("notificationVisibility") {
+ private val mNotificationVisibility = object : FloatProperty<NotificationWakeUpCoordinator>(
+ "notificationVisibility") {
override fun setValue(coordinator: NotificationWakeUpCoordinator, value: Float) {
coordinator.setVisibilityAmount(value)
@@ -78,10 +76,10 @@
field = value
willWakeUp = false
if (value) {
- if (mNotificationsVisible && !mNotificationsVisibleForExpansion
- && !bypassController.bypassEnabled) {
+ if (mNotificationsVisible && !mNotificationsVisibleForExpansion &&
+ !bypassController.bypassEnabled) {
// We're waking up while pulsing, let's make sure the animation looks nice
- mStackScroller.wakeUpFromPulse();
+ mStackScroller.wakeUpFromPulse()
}
if (bypassController.bypassEnabled && !mNotificationsVisible) {
// Let's make sure our huns become visible once we are waking up in case
@@ -100,7 +98,7 @@
}
private var collapsedEnoughToHide: Boolean = false
- lateinit var iconAreaController : NotificationIconAreaController
+ lateinit var iconAreaController: NotificationIconAreaController
var pulsing: Boolean = false
set(value) {
@@ -132,8 +130,8 @@
var canShow = pulsing
if (bypassController.bypassEnabled) {
// We also allow pulsing on the lock screen!
- canShow = canShow || (wakingUp || willWakeUp || fullyAwake)
- && statusBarStateController.state == StatusBarState.KEYGUARD
+ canShow = canShow || (wakingUp || willWakeUp || fullyAwake) &&
+ statusBarStateController.state == StatusBarState.KEYGUARD
// We want to hide the notifications when collapsed too much
if (collapsedEnoughToHide) {
canShow = false
@@ -143,7 +141,7 @@
}
init {
- mHeadsUpManagerPhone.addListener(this)
+ mHeadsUpManager.addListener(this)
statusBarStateController.addCallback(this)
addListener(object : WakeUpListener {
override fun onFullyHiddenChanged(isFullyHidden: Boolean) {
@@ -155,7 +153,7 @@
increaseSpeed = false)
}
}
- });
+ })
}
fun setStackScroller(stackScroller: NotificationStackScrollLayout) {
@@ -178,46 +176,55 @@
* @param animate should this change be animated
* @param increaseSpeed should the speed be increased of the animation
*/
- fun setNotificationsVisibleForExpansion(visible: Boolean, animate: Boolean,
- increaseSpeed: Boolean) {
+ fun setNotificationsVisibleForExpansion(
+ visible: Boolean,
+ animate: Boolean,
+ increaseSpeed: Boolean
+ ) {
mNotificationsVisibleForExpansion = visible
updateNotificationVisibility(animate, increaseSpeed)
if (!visible && mNotificationsVisible) {
// If we stopped expanding and we're still visible because we had a pulse that hasn't
// times out, let's release them all to make sure were not stuck in a state where
// notifications are visible
- mHeadsUpManagerPhone.releaseAllImmediately()
+ mHeadsUpManager.releaseAllImmediately()
}
}
fun addListener(listener: WakeUpListener) {
- wakeUpListeners.add(listener);
+ wakeUpListeners.add(listener)
}
fun removeListener(listener: WakeUpListener) {
- wakeUpListeners.remove(listener);
+ wakeUpListeners.remove(listener)
}
- private fun updateNotificationVisibility(animate: Boolean, increaseSpeed: Boolean) {
+ private fun updateNotificationVisibility(
+ animate: Boolean,
+ increaseSpeed: Boolean
+ ) {
// TODO: handle Lockscreen wakeup for bypass when we're not pulsing anymore
- var visible = mNotificationsVisibleForExpansion || mHeadsUpManagerPhone.hasNotifications()
+ var visible = mNotificationsVisibleForExpansion || mHeadsUpManager.hasNotifications()
visible = visible && canShowPulsingHuns
if (!visible && mNotificationsVisible && (wakingUp || willWakeUp) && mDozeAmount != 0.0f) {
// let's not make notifications invisible while waking up, otherwise the animation
// is strange
- return;
+ return
}
setNotificationsVisible(visible, animate, increaseSpeed)
}
- private fun setNotificationsVisible(visible: Boolean, animate: Boolean,
- increaseSpeed: Boolean) {
+ private fun setNotificationsVisible(
+ visible: Boolean,
+ animate: Boolean,
+ increaseSpeed: Boolean
+ ) {
if (mNotificationsVisible == visible) {
return
}
mNotificationsVisible = visible
- mVisibilityAnimator?.cancel();
+ mVisibilityAnimator?.cancel()
if (animate) {
notifyAnimationStart(visible)
startVisibilityAnimation(increaseSpeed)
@@ -230,8 +237,8 @@
if (updateDozeAmountIfBypass()) {
return
}
- if (linear != 1.0f && linear != 0.0f
- && (mLinearDozeAmount == 0.0f || mLinearDozeAmount == 1.0f)) {
+ if (linear != 1.0f && linear != 0.0f &&
+ (mLinearDozeAmount == 0.0f || mLinearDozeAmount == 1.0f)) {
// Let's notify the scroller that an animation started
notifyAnimationStart(mLinearDozeAmount == 1.0f)
}
@@ -245,17 +252,17 @@
mStackScroller.setDozeAmount(mDozeAmount)
updateHideAmount()
if (changed && linear == 0.0f) {
- setNotificationsVisible(visible = false, animate = false, increaseSpeed = false);
+ setNotificationsVisible(visible = false, animate = false, increaseSpeed = false)
setNotificationsVisibleForExpansion(visible = false, animate = false,
increaseSpeed = false)
}
}
override fun onStateChanged(newState: Int) {
- updateDozeAmountIfBypass();
+ updateDozeAmountIfBypass()
if (bypassController.bypassEnabled &&
- newState == StatusBarState.KEYGUARD && state == StatusBarState.SHADE_LOCKED
- && (!statusBarStateController.isDozing || shouldAnimateVisibility())) {
+ newState == StatusBarState.KEYGUARD && state == StatusBarState.SHADE_LOCKED &&
+ (!statusBarStateController.isDozing || shouldAnimateVisibility())) {
// We're leaving shade locked. Let's animate the notifications away
setNotificationsVisible(visible = true, increaseSpeed = false, animate = false)
setNotificationsVisible(visible = false, increaseSpeed = false, animate = true)
@@ -266,23 +273,23 @@
override fun onPanelExpansionChanged(expansion: Float, tracking: Boolean) {
val collapsedEnough = expansion <= 0.9f
if (collapsedEnough != this.collapsedEnoughToHide) {
- val couldShowPulsingHuns = canShowPulsingHuns;
+ val couldShowPulsingHuns = canShowPulsingHuns
this.collapsedEnoughToHide = collapsedEnough
if (couldShowPulsingHuns && !canShowPulsingHuns) {
updateNotificationVisibility(animate = true, increaseSpeed = true)
- mHeadsUpManagerPhone.releaseAllImmediately()
+ mHeadsUpManager.releaseAllImmediately()
}
}
}
private fun updateDozeAmountIfBypass(): Boolean {
if (bypassController.bypassEnabled) {
- var amount = 1.0f;
- if (statusBarStateController.state == StatusBarState.SHADE
- || statusBarStateController.state == StatusBarState.SHADE_LOCKED) {
- amount = 0.0f;
+ var amount = 1.0f
+ if (statusBarStateController.state == StatusBarState.SHADE ||
+ statusBarStateController.state == StatusBarState.SHADE_LOCKED) {
+ amount = 0.0f
}
- setDozeAmount(amount, amount)
+ setDozeAmount(amount, amount)
return true
}
return false
@@ -300,7 +307,7 @@
visibilityAnimator.setInterpolator(Interpolators.LINEAR)
var duration = StackStateAnimator.ANIMATION_DURATION_WAKEUP.toLong()
if (increaseSpeed) {
- duration = (duration.toFloat() / 1.5F).toLong();
+ duration = (duration.toFloat() / 1.5F).toLong()
}
visibilityAnimator.setDuration(duration)
visibilityAnimator.start()
@@ -311,7 +318,7 @@
mLinearVisibilityAmount = visibilityAmount
mVisibilityAmount = mVisibilityInterpolator.getInterpolation(
visibilityAmount)
- handleAnimationFinished();
+ handleAnimationFinished()
updateHideAmount()
}
@@ -322,7 +329,7 @@
}
}
- fun getWakeUpHeight() : Float {
+ fun getWakeUpHeight(): Float {
return mStackScroller.wakeUpHeight
}
@@ -330,7 +337,7 @@
val linearAmount = Math.min(1.0f - mLinearVisibilityAmount, mLinearDozeAmount)
val amount = Math.min(1.0f - mVisibilityAmount, mDozeAmount)
mStackScroller.setHideAmount(linearAmount, amount)
- notificationsFullyHidden = linearAmount == 1.0f;
+ notificationsFullyHidden = linearAmount == 1.0f
}
private fun notifyAnimationStart(awake: Boolean) {
@@ -361,7 +368,7 @@
// if we animate, we see the shelf briefly visible. Instead we fully animate
// the notification and its background out
animate = false
- } else if (!wakingUp && !willWakeUp){
+ } else if (!wakingUp && !willWakeUp) {
// TODO: look that this is done properly and not by anyone else
entry.setHeadsUpAnimatingAway(true)
mEntrySetToClearWhenFinished.add(entry)
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 e8a62e4..4beeede 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
@@ -37,8 +37,8 @@
import com.android.systemui.statusbar.NotificationUiAdjustment;
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.NotificationClicker;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
import com.android.systemui.statusbar.notification.row.NotifBindPipeline;
@@ -66,7 +66,7 @@
private static final String TAG = "NotificationViewManager";
- private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
+ private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private final Context mContext;
private final NotifBindPipeline mNotifBindPipeline;
@@ -97,7 +97,7 @@
StatusBarStateController statusBarStateController,
NotificationGroupManager notificationGroupManager,
NotificationGutsManager notificationGutsManager,
- NotificationInterruptionStateProvider notificationInterruptionStateProvider,
+ NotificationInterruptStateProvider notificationInterruptionStateProvider,
Provider<RowInflaterTask> rowInflaterTaskProvider,
ExpandableNotificationRowComponent.Builder expandableNotificationRowComponentBuilder) {
mContext = context;
@@ -106,7 +106,7 @@
mMessagingUtil = notificationMessagingUtil;
mNotificationRemoteInputManager = notificationRemoteInputManager;
mNotificationLockscreenUserManager = notificationLockscreenUserManager;
- mNotificationInterruptionStateProvider = notificationInterruptionStateProvider;
+ mNotificationInterruptStateProvider = notificationInterruptionStateProvider;
mRowInflaterTaskProvider = rowInflaterTaskProvider;
mExpandableNotificationRowComponentBuilder = expandableNotificationRowComponentBuilder;
}
@@ -243,7 +243,7 @@
params.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
params.setUseLowPriority(entry.isAmbient());
- if (mNotificationInterruptionStateProvider.shouldHeadsUp(entry)) {
+ if (mNotificationInterruptStateProvider.shouldHeadsUp(entry)) {
params.requireContentViews(FLAG_CONTENT_VIEW_HEADS_UP);
}
//TODO: Replace this API with RowContentBindParams directly
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index d0b553d..cd6affd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -21,6 +21,8 @@
import android.view.accessibility.AccessibilityManager;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.UiEventLoggerImpl;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
@@ -29,10 +31,8 @@
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
-import com.android.systemui.statusbar.notification.NotificationAlertingManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
@@ -42,6 +42,9 @@
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.init.NotificationsControllerImpl;
import com.android.systemui.statusbar.notification.init.NotificationsControllerStub;
+import com.android.systemui.statusbar.notification.interruption.NotificationAlertingManager;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -54,6 +57,7 @@
import javax.inject.Singleton;
+import dagger.Binds;
import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
@@ -123,7 +127,7 @@
NotificationRemoteInputManager remoteInputManager,
VisualStabilityManager visualStabilityManager,
StatusBarStateController statusBarStateController,
- NotificationInterruptionStateProvider notificationInterruptionStateProvider,
+ NotificationInterruptStateProvider notificationInterruptStateProvider,
NotificationListener notificationListener,
HeadsUpManager headsUpManager) {
return new NotificationAlertingManager(
@@ -131,7 +135,7 @@
remoteInputManager,
visualStabilityManager,
statusBarStateController,
- notificationInterruptionStateProvider,
+ notificationInterruptStateProvider,
notificationListener,
headsUpManager);
}
@@ -153,6 +157,13 @@
expansionStateLogger);
}
+ /** Provides an instance of {@link com.android.internal.logging.UiEventLogger} */
+ @Singleton
+ @Provides
+ static UiEventLogger provideUiEventLogger() {
+ return new UiEventLoggerImpl();
+ }
+
/** Provides an instance of {@link NotificationBlockingHelperManager} */
@Singleton
@Provides
@@ -190,4 +201,9 @@
NotificationEntryManager entryManager) {
return featureFlags.isNewNotifPipelineRenderingEnabled() ? pipeline.get() : entryManager;
}
+
+ /** */
+ @Binds
+ NotificationInterruptStateProvider bindNotificationInterruptStateProvider(
+ NotificationInterruptStateProviderImpl notificationInterruptStateProviderImpl);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt
index 269a7a5..88888d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar.notification
+package com.android.systemui.statusbar.notification.interruption
import android.content.Context
import android.media.MediaMetadata
@@ -24,6 +24,7 @@
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationMediaManager
import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.notification.NotificationEntryManager
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
import com.android.systemui.statusbar.phone.KeyguardBypassController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationAlertingManager.java
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationAlertingManager.java
index df21f0b..b572502 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationAlertingManager.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification;
+package com.android.systemui.statusbar.notification.interruption;
import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
@@ -27,6 +27,9 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -39,7 +42,7 @@
private final NotificationRemoteInputManager mRemoteInputManager;
private final VisualStabilityManager mVisualStabilityManager;
private final StatusBarStateController mStatusBarStateController;
- private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
+ private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private final NotificationListener mNotificationListener;
private HeadsUpManager mHeadsUpManager;
@@ -52,13 +55,13 @@
NotificationRemoteInputManager remoteInputManager,
VisualStabilityManager visualStabilityManager,
StatusBarStateController statusBarStateController,
- NotificationInterruptionStateProvider notificationInterruptionStateProvider,
+ NotificationInterruptStateProvider notificationInterruptionStateProvider,
NotificationListener notificationListener,
HeadsUpManager headsUpManager) {
mRemoteInputManager = remoteInputManager;
mVisualStabilityManager = visualStabilityManager;
mStatusBarStateController = statusBarStateController;
- mNotificationInterruptionStateProvider = notificationInterruptionStateProvider;
+ mNotificationInterruptStateProvider = notificationInterruptionStateProvider;
mNotificationListener = notificationListener;
mHeadsUpManager = headsUpManager;
@@ -94,7 +97,7 @@
if (entry.getRow().getPrivateLayout().getHeadsUpChild() != null) {
// Possible for shouldHeadsUp to change between the inflation starting and ending.
// If it does and we no longer need to heads up, we should free the view.
- if (mNotificationInterruptionStateProvider.shouldHeadsUp(entry)) {
+ if (mNotificationInterruptStateProvider.shouldHeadsUp(entry)) {
mHeadsUpManager.showNotification(entry);
if (!mStatusBarStateController.isDozing()) {
// Mark as seen immediately
@@ -109,7 +112,7 @@
private void updateAlertState(NotificationEntry entry) {
boolean alertAgain = alertAgain(entry, entry.getSbn().getNotification());
// includes check for whether this notification should be filtered:
- boolean shouldAlert = mNotificationInterruptionStateProvider.shouldHeadsUp(entry);
+ boolean shouldAlert = mNotificationInterruptStateProvider.shouldHeadsUp(entry);
final boolean wasAlerting = mHeadsUpManager.isAlerting(entry.getKey());
if (wasAlerting) {
if (shouldAlert) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
new file mode 100644
index 0000000..3292a8f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.interruption;
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+/**
+ * Provides bubble-up and heads-up state for notification entries.
+ *
+ * When a notification is heads-up when dozing, this is also called "pulsing."
+ */
+public interface NotificationInterruptStateProvider {
+ /**
+ * If the device is awake (not dozing):
+ * Whether the notification should peek in from the top and alert the user.
+ *
+ * If the device is dozing:
+ * Whether the notification should show the ambient view of the notification ("pulse").
+ *
+ * @param entry the entry to check
+ * @return true if the entry should heads up, false otherwise
+ */
+ boolean shouldHeadsUp(NotificationEntry entry);
+
+ /**
+ * Whether the notification should appear as a bubble with a fly-out on top of the screen.
+ *
+ * @param entry the entry to check
+ * @return true if the entry should bubble up, false otherwise
+ */
+ boolean shouldBubbleUp(NotificationEntry entry);
+
+ /**
+ * Whether to launch the entry's full screen intent when the entry is added.
+ *
+ * @param entry the entry that was added
+ * @return {@code true} if we should launch the full screen intent
+ */
+ boolean shouldLaunchFullScreenIntentWhenAdded(NotificationEntry entry);
+
+ /**
+ * Add a component that can suppress visual interruptions.
+ */
+ void addSuppressor(NotificationInterruptSuppressor suppressor);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
similarity index 63%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index bbf2dde..46d5044 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -14,33 +14,35 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification;
+package com.android.systemui.statusbar.notification.interruption;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
import android.app.NotificationManager;
-import android.content.Context;
+import android.content.ContentResolver;
import android.database.ContentObserver;
import android.hardware.display.AmbientDisplayConfiguration;
+import android.os.Handler;
import android.os.PowerManager;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.Settings;
-import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
import android.service.notification.StatusBarNotification;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Dependency;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.NotificationFilter;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
+import java.util.ArrayList;
+import java.util.List;
+
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -48,120 +50,84 @@
* Provides heads-up and pulsing state for notification entries.
*/
@Singleton
-public class NotificationInterruptionStateProvider {
-
+public class NotificationInterruptStateProviderImpl implements NotificationInterruptStateProvider {
private static final String TAG = "InterruptionStateProvider";
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = true; //false;
private static final boolean DEBUG_HEADS_UP = true;
private static final boolean ENABLE_HEADS_UP = true;
private static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
+ private final List<NotificationInterruptSuppressor> mSuppressors = new ArrayList<>();
private final StatusBarStateController mStatusBarStateController;
private final NotificationFilter mNotificationFilter;
- private final AmbientDisplayConfiguration mAmbientDisplayConfiguration;
-
- private final Context mContext;
+ private final ContentResolver mContentResolver;
private final PowerManager mPowerManager;
private final IDreamManager mDreamManager;
+ private final AmbientDisplayConfiguration mAmbientDisplayConfiguration;
private final BatteryController mBatteryController;
-
- private NotificationPresenter mPresenter;
+ private final ContentObserver mHeadsUpObserver;
private HeadsUpManager mHeadsUpManager;
- private HeadsUpSuppressor mHeadsUpSuppressor;
- private ContentObserver mHeadsUpObserver;
@VisibleForTesting
protected boolean mUseHeadsUp = false;
- private boolean mDisableNotificationAlerts;
@Inject
- public NotificationInterruptionStateProvider(Context context, NotificationFilter filter,
- StatusBarStateController stateController, BatteryController batteryController) {
- this(context,
- (PowerManager) context.getSystemService(Context.POWER_SERVICE),
- IDreamManager.Stub.asInterface(
- ServiceManager.checkService(DreamService.DREAM_SERVICE)),
- new AmbientDisplayConfiguration(context),
- filter,
- batteryController,
- stateController);
- }
-
- @VisibleForTesting
- protected NotificationInterruptionStateProvider(
- Context context,
+ public NotificationInterruptStateProviderImpl(
+ ContentResolver contentResolver,
PowerManager powerManager,
IDreamManager dreamManager,
AmbientDisplayConfiguration ambientDisplayConfiguration,
NotificationFilter notificationFilter,
BatteryController batteryController,
- StatusBarStateController statusBarStateController) {
- mContext = context;
+ StatusBarStateController statusBarStateController,
+ HeadsUpManager headsUpManager,
+ @Main Handler mainHandler) {
+ mContentResolver = contentResolver;
mPowerManager = powerManager;
mDreamManager = dreamManager;
mBatteryController = batteryController;
mAmbientDisplayConfiguration = ambientDisplayConfiguration;
mNotificationFilter = notificationFilter;
mStatusBarStateController = statusBarStateController;
- }
-
- /** Sets up late-binding dependencies for this component. */
- public void setUpWithPresenter(
- NotificationPresenter notificationPresenter,
- HeadsUpManager headsUpManager,
- HeadsUpSuppressor headsUpSuppressor) {
- setUpWithPresenter(notificationPresenter, headsUpManager, headsUpSuppressor,
- new ContentObserver(Dependency.get(Dependency.MAIN_HANDLER)) {
- @Override
- public void onChange(boolean selfChange) {
- boolean wasUsing = mUseHeadsUp;
- mUseHeadsUp = ENABLE_HEADS_UP && !mDisableNotificationAlerts
- && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt(
- mContext.getContentResolver(),
- Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
- Settings.Global.HEADS_UP_OFF);
- Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled"));
- if (wasUsing != mUseHeadsUp) {
- if (!mUseHeadsUp) {
- Log.d(TAG,
- "dismissing any existing heads up notification on disable"
- + " event");
- mHeadsUpManager.releaseAllImmediately();
- }
- }
- }
- });
- }
-
- /** Sets up late-binding dependencies for this component. */
- public void setUpWithPresenter(
- NotificationPresenter notificationPresenter,
- HeadsUpManager headsUpManager,
- HeadsUpSuppressor headsUpSuppressor,
- ContentObserver observer) {
- mPresenter = notificationPresenter;
mHeadsUpManager = headsUpManager;
- mHeadsUpSuppressor = headsUpSuppressor;
- mHeadsUpObserver = observer;
+ mHeadsUpObserver = new ContentObserver(mainHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ boolean wasUsing = mUseHeadsUp;
+ mUseHeadsUp = ENABLE_HEADS_UP
+ && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt(
+ mContentResolver,
+ Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
+ Settings.Global.HEADS_UP_OFF);
+ Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled"));
+ if (wasUsing != mUseHeadsUp) {
+ if (!mUseHeadsUp) {
+ Log.d(TAG, "dismissing any existing heads up notification on "
+ + "disable event");
+ mHeadsUpManager.releaseAllImmediately();
+ }
+ }
+ }
+ };
if (ENABLE_HEADS_UP) {
- mContext.getContentResolver().registerContentObserver(
+ mContentResolver.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED),
true,
mHeadsUpObserver);
- mContext.getContentResolver().registerContentObserver(
+ mContentResolver.registerContentObserver(
Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true,
mHeadsUpObserver);
}
mHeadsUpObserver.onChange(true); // set up
}
- /**
- * Whether the notification should appear as a bubble with a fly-out on top of the screen.
- *
- * @param entry the entry to check
- * @return true if the entry should bubble up, false otherwise
- */
+ @Override
+ public void addSuppressor(NotificationInterruptSuppressor suppressor) {
+ mSuppressors.add(suppressor);
+ }
+
+ @Override
public boolean shouldBubbleUp(NotificationEntry entry) {
final StatusBarNotification sbn = entry.getSbn();
@@ -201,12 +167,8 @@
return true;
}
- /**
- * Whether the notification should peek in from the top and alert the user.
- *
- * @param entry the entry to check
- * @return true if the entry should heads up, false otherwise
- */
+
+ @Override
public boolean shouldHeadsUp(NotificationEntry entry) {
if (mStatusBarStateController.isDozing()) {
return shouldHeadsUpWhenDozing(entry);
@@ -215,6 +177,17 @@
}
}
+ /**
+ * When an entry was added, should we launch its fullscreen intent? Examples are Alarms or
+ * incoming calls.
+ */
+ @Override
+ public boolean shouldLaunchFullScreenIntentWhenAdded(NotificationEntry entry) {
+ return entry.getSbn().getNotification().fullScreenIntent != null
+ && (!shouldHeadsUp(entry)
+ || mStatusBarStateController.getState() == StatusBarState.KEYGUARD);
+ }
+
private boolean shouldHeadsUpWhenAwake(NotificationEntry entry) {
StatusBarNotification sbn = entry.getSbn();
@@ -271,13 +244,15 @@
return false;
}
- if (!mHeadsUpSuppressor.canHeadsUp(entry, sbn)) {
- if (DEBUG_HEADS_UP) {
- Log.d(TAG, "No heads up: aborted by suppressor: " + sbn.getKey());
+ for (int i = 0; i < mSuppressors.size(); i++) {
+ if (mSuppressors.get(i).suppressAwakeHeadsUp(entry)) {
+ if (DEBUG_HEADS_UP) {
+ Log.d(TAG, "No heads up: aborted by suppressor: "
+ + mSuppressors.get(i).getName() + " sbnKey=" + sbn.getKey());
+ }
+ return false;
}
- return false;
}
-
return true;
}
@@ -325,7 +300,7 @@
}
return false;
}
- return true;
+ return true;
}
/**
@@ -334,8 +309,7 @@
* @param entry the entry to check
* @return true if these checks pass, false if the notification should not alert
*/
- @VisibleForTesting
- public boolean canAlertCommon(NotificationEntry entry) {
+ private boolean canAlertCommon(NotificationEntry entry) {
StatusBarNotification sbn = entry.getSbn();
if (mNotificationFilter.shouldFilterOut(entry)) {
@@ -352,6 +326,16 @@
}
return false;
}
+
+ for (int i = 0; i < mSuppressors.size(); i++) {
+ if (mSuppressors.get(i).suppressInterruptions(entry)) {
+ if (DEBUG_HEADS_UP) {
+ Log.d(TAG, "No alerting: aborted by suppressor: "
+ + mSuppressors.get(i).getName() + " sbnKey=" + sbn.getKey());
+ }
+ return false;
+ }
+ }
return true;
}
@@ -361,15 +345,17 @@
* @param entry the entry to check
* @return true if these checks pass, false if the notification should not alert
*/
- @VisibleForTesting
- public boolean canAlertAwakeCommon(NotificationEntry entry) {
+ private boolean canAlertAwakeCommon(NotificationEntry entry) {
StatusBarNotification sbn = entry.getSbn();
- if (mPresenter.isDeviceInVrMode()) {
- if (DEBUG_HEADS_UP) {
- Log.d(TAG, "No alerting: no huns or vr mode");
+ for (int i = 0; i < mSuppressors.size(); i++) {
+ if (mSuppressors.get(i).suppressAwakeInterruptions(entry)) {
+ if (DEBUG_HEADS_UP) {
+ Log.d(TAG, "No alerting: aborted by suppressor: "
+ + mSuppressors.get(i).getName() + " sbnKey=" + sbn.getKey());
+ }
+ return false;
}
- return false;
}
if (isSnoozedPackage(sbn)) {
@@ -392,54 +378,4 @@
private boolean isSnoozedPackage(StatusBarNotification sbn) {
return mHeadsUpManager.isSnoozed(sbn.getPackageName());
}
-
- /** Sets whether to disable all alerts. */
- public void setDisableNotificationAlerts(boolean disableNotificationAlerts) {
- mDisableNotificationAlerts = disableNotificationAlerts;
- mHeadsUpObserver.onChange(true);
- }
-
- /** Whether all alerts are disabled. */
- @VisibleForTesting
- public boolean areNotificationAlertsDisabled() {
- return mDisableNotificationAlerts;
- }
-
- /** Whether HUNs should be used. */
- @VisibleForTesting
- public boolean getUseHeadsUp() {
- return mUseHeadsUp;
- }
-
- protected NotificationPresenter getPresenter() {
- return mPresenter;
- }
-
- /**
- * When an entry was added, should we launch its fullscreen intent? Examples are Alarms or
- * incoming calls.
- *
- * @param entry the entry that was added
- * @return {@code true} if we should launch the full screen intent
- */
- public boolean shouldLaunchFullScreenIntentWhenAdded(NotificationEntry entry) {
- return entry.getSbn().getNotification().fullScreenIntent != null
- && (!shouldHeadsUp(entry)
- || mStatusBarStateController.getState() == StatusBarState.KEYGUARD);
- }
-
- /** A component which can suppress heads-up notifications due to the overall state of the UI. */
- public interface HeadsUpSuppressor {
- /**
- * Returns false if the provided notification is ineligible for heads-up according to this
- * component.
- *
- * @param entry entry of the notification that might be heads upped
- * @param sbn notification that might be heads upped
- * @return false if the notification can not be heads upped
- */
- boolean canHeadsUp(NotificationEntry entry, StatusBarNotification sbn);
-
- }
-
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptSuppressor.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptSuppressor.java
new file mode 100644
index 0000000..c19f8bd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptSuppressor.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.interruption;
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+/** A component which can suppress visual interruptions of notifications such as heads-up and
+ * bubble-up.
+ */
+public interface NotificationInterruptSuppressor {
+ /**
+ * A unique name to identify this suppressor.
+ */
+ default String getName() {
+ return this.getClass().getName();
+ }
+
+ /**
+ * Returns true if the provided notification is, when the device is awake, ineligible for
+ * heads-up according to this component.
+ *
+ * @param entry entry of the notification that might heads-up
+ * @return true if the heads up interruption should be suppressed when the device is awake
+ */
+ default boolean suppressAwakeHeadsUp(NotificationEntry entry) {
+ return false;
+ }
+
+ /**
+ * Returns true if the provided notification is, when the device is awake, ineligible for
+ * heads-up or bubble-up according to this component.
+ *
+ * @param entry entry of the notification that might heads-up or bubble-up
+ * @return true if interruptions should be suppressed when the device is awake
+ */
+ default boolean suppressAwakeInterruptions(NotificationEntry entry) {
+ return false;
+ }
+
+ /**
+ * Returns true if the provided notification is, regardless of awake/dozing state,
+ * ineligible for heads-up or bubble-up according to this component.
+ *
+ * @param entry entry of the notification that might heads-up or bubble-up
+ * @return true if interruptions should be suppressed
+ */
+ default boolean suppressInterruptions(NotificationEntry entry) {
+ return false;
+ }
+}
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 1bd9bbe..cfcbd88 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
@@ -81,6 +81,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
@@ -502,6 +504,7 @@
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
@VisibleForTesting
protected final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
+ protected final UiEventLogger mUiEventLogger;
private final NotificationRemoteInputManager mRemoteInputManager =
Dependency.get(NotificationRemoteInputManager.class);
private final SysuiColorExtractor mColorExtractor = Dependency.get(SysuiColorExtractor.class);
@@ -547,7 +550,8 @@
FeatureFlags featureFlags,
NotifPipeline notifPipeline,
NotificationEntryManager entryManager,
- NotifCollection notifCollection
+ NotifCollection notifCollection,
+ UiEventLogger uiEventLogger
) {
super(context, attrs, 0, 0);
Resources res = getResources();
@@ -649,6 +653,7 @@
mDynamicPrivacyController = dynamicPrivacyController;
mStatusbarStateController = statusBarStateController;
initializeForegroundServiceSection(fgsFeatureController);
+ mUiEventLogger = uiEventLogger;
}
private void initializeForegroundServiceSection(
@@ -5524,7 +5529,8 @@
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- private void clearNotifications(
+ @VisibleForTesting
+ void clearNotifications(
@SelectedRows int selection,
boolean closeShade) {
// animate-swipe all dismissable notifications, then animate the shade closed
@@ -5567,6 +5573,9 @@
}
}
+ // Log dismiss event even if there's nothing to dismiss
+ mUiEventLogger.log(NotificationPanelEvent.fromSelection(selection));
+
if (viewsToRemove.isEmpty()) {
if (closeShade) {
Dependency.get(ShadeController.class).animateCollapsePanels(
@@ -6737,4 +6746,35 @@
public static final int ROWS_HIGH_PRIORITY = 1;
/** Only rows where entry.isHighPriority() is false. */
public static final int ROWS_GENTLE = 2;
+
+ /**
+ * Enum for UiEvent logged from this class
+ */
+ enum NotificationPanelEvent implements UiEventLogger.UiEventEnum {
+ INVALID(0),
+ @UiEvent(doc = "User dismissed all notifications from notification panel.")
+ DISMISS_ALL_NOTIFICATIONS_PANEL(312),
+ @UiEvent(doc = "User dismissed all silent notifications from notification panel.")
+ DISMISS_SILENT_NOTIFICATIONS_PANEL(314);
+ private final int mId;
+ NotificationPanelEvent(int id) {
+ mId = id;
+ }
+ @Override public int getId() {
+ return mId;
+ }
+
+ public static UiEventLogger.UiEventEnum fromSelection(@SelectedRows int selection) {
+ if (selection == ROWS_ALL) {
+ return DISMISS_ALL_NOTIFICATIONS_PANEL;
+ }
+ if (selection == ROWS_GENTLE) {
+ return DISMISS_SILENT_NOTIFICATIONS_PANEL;
+ }
+ if (NotificationStackScrollLayout.DEBUG) {
+ throw new IllegalArgumentException("Unexpected selection" + selection);
+ }
+ return INVALID;
+ }
+ }
}
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 3e6e027..287ede4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -185,15 +185,14 @@
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
-import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
-import com.android.systemui.statusbar.notification.NotificationAlertingManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.init.NotificationsController;
+import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -404,10 +403,9 @@
private final NotificationGutsManager mGutsManager;
private final NotificationLogger mNotificationLogger;
- private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
private final NotificationViewHierarchyManager mViewHierarchyManager;
private final KeyguardViewMediator mKeyguardViewMediator;
- private final NotificationAlertingManager mNotificationAlertingManager;
+ protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
// for disabling the status bar
private int mDisabled1 = 0;
@@ -621,10 +619,9 @@
RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
- NotificationInterruptionStateProvider notificationInterruptionStateProvider,
+ NotificationInterruptStateProvider notificationInterruptStateProvider,
NotificationViewHierarchyManager notificationViewHierarchyManager,
KeyguardViewMediator keyguardViewMediator,
- NotificationAlertingManager notificationAlertingManager,
DisplayMetrics displayMetrics,
MetricsLogger metricsLogger,
@UiBackground Executor uiBgExecutor,
@@ -701,10 +698,9 @@
mRemoteInputQuickSettingsDisabler = remoteInputQuickSettingsDisabler;
mGutsManager = notificationGutsManager;
mNotificationLogger = notificationLogger;
- mNotificationInterruptionStateProvider = notificationInterruptionStateProvider;
+ mNotificationInterruptStateProvider = notificationInterruptStateProvider;
mViewHierarchyManager = notificationViewHierarchyManager;
mKeyguardViewMediator = keyguardViewMediator;
- mNotificationAlertingManager = notificationAlertingManager;
mDisplayMetrics = displayMetrics;
mMetricsLogger = metricsLogger;
mUiBgExecutor = uiBgExecutor;
@@ -1238,9 +1234,9 @@
mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanelViewController,
mHeadsUpManager, mNotificationShadeWindowView, mStackScroller, mDozeScrimController,
mScrimController, mActivityLaunchAnimator, mDynamicPrivacyController,
- mNotificationAlertingManager, mKeyguardStateController,
- mKeyguardIndicationController,
- this /* statusBar */, mShadeController, mCommandQueue, mInitController);
+ mKeyguardStateController, mKeyguardIndicationController,
+ this /* statusBar */, mShadeController, mCommandQueue, mInitController,
+ mNotificationInterruptStateProvider);
mNotificationShelf.setOnActivatedListener(mPresenter);
mRemoteInputManager.getController().addCallback(mNotificationShadeWindowController);
@@ -1589,8 +1585,9 @@
}
if ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
- mNotificationInterruptionStateProvider.setDisableNotificationAlerts(
- (state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0);
+ if (areNotificationAlertsDisabled()) {
+ mHeadsUpManager.releaseAllImmediately();
+ }
}
if ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) != 0) {
@@ -1605,6 +1602,10 @@
}
}
+ boolean areNotificationAlertsDisabled() {
+ return (mDisabled1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
+ }
+
protected H createHandler() {
return new StatusBar.H();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 0644a42..31db8eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -42,6 +42,7 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.keyguard.KeyguardViewController;
import com.android.keyguard.ViewMediatorCallback;
import com.android.settingslib.animation.AppearAnimationUtils;
import com.android.systemui.DejankUtils;
@@ -77,7 +78,8 @@
@Singleton
public class StatusBarKeyguardViewManager implements RemoteInputController.Callback,
StatusBarStateController.StateListener, ConfigurationController.ConfigurationListener,
- PanelExpansionListener, NavigationModeController.ModeChangedListener {
+ PanelExpansionListener, NavigationModeController.ModeChangedListener,
+ KeyguardViewController {
// When hiding the Keyguard with timing supplied from WindowManager, better be early than late.
private static final long HIDE_TIMING_CORRECTION_MS = - 16 * 3;
@@ -221,6 +223,7 @@
mDockManager = dockManager;
}
+ @Override
public void registerStatusBar(StatusBar statusBar,
ViewGroup container,
NotificationPanelViewController notificationPanelViewController,
@@ -326,6 +329,7 @@
* Show the keyguard. Will handle creating and attaching to the view manager
* lazily.
*/
+ @Override
public void show(Bundle options) {
mShowing = true;
mNotificationShadeWindowController.setKeyguardShowing(true);
@@ -430,9 +434,7 @@
mAfterKeyguardGoneRunnables.add(runnable);
}
- /**
- * Reset the state of the view.
- */
+ @Override
public void reset(boolean hideBouncerWhenShowing) {
if (mShowing) {
if (mOccluded && !mDozing) {
@@ -452,23 +454,28 @@
return mGoingToSleepVisibleNotOccluded;
}
+ @Override
public void onStartedGoingToSleep() {
mGoingToSleepVisibleNotOccluded = isShowing() && !isOccluded();
}
+ @Override
public void onFinishedGoingToSleep() {
mGoingToSleepVisibleNotOccluded = false;
mBouncer.onScreenTurnedOff();
}
+ @Override
public void onStartedWakingUp() {
// TODO: remove
}
+ @Override
public void onScreenTurningOn() {
// TODO: remove
}
+ @Override
public void onScreenTurnedOn() {
// TODO: remove
}
@@ -503,14 +510,17 @@
}
}
+ @Override
public void setNeedsInput(boolean needsInput) {
mNotificationShadeWindowController.setKeyguardNeedsInput(needsInput);
}
+ @Override
public boolean isUnlockWithWallpaper() {
return mNotificationShadeWindowController.isShowingWallpaper();
}
+ @Override
public void setOccluded(boolean occluded, boolean animate) {
mStatusBar.setOccluded(occluded);
if (occluded && !mOccluded && mShowing) {
@@ -554,13 +564,7 @@
return mOccluded;
}
- /**
- * Starts the animation before we dismiss Keyguard, i.e. an disappearing animation on the
- * security view of the bouncer.
- *
- * @param finishRunnable the runnable to be run after the animation finished, or {@code null} if
- * no action should be run
- */
+ @Override
public void startPreHideAnimation(Runnable finishRunnable) {
if (mBouncer.isShowing()) {
mBouncer.startPreHideAnimation(finishRunnable);
@@ -572,9 +576,7 @@
updateLockIcon();
}
- /**
- * Hides the keyguard view
- */
+ @Override
public void hide(long startTime, long fadeoutDuration) {
mShowing = false;
mKeyguardStateController.notifyKeyguardState(mShowing,
@@ -728,9 +730,7 @@
mAfterKeyguardGoneRunnables.clear();
}
- /**
- * Dismisses the keyguard by going to the next screen or making it gone.
- */
+ @Override
public void dismissAndCollapse() {
mStatusBar.executeRunnableDismissingKeyguard(null, null, true, false, true);
}
@@ -742,9 +742,7 @@
return mBouncer.isSecure();
}
- /**
- * @return Whether the keyguard is showing
- */
+ @Override
public boolean isShowing() {
return mShowing;
}
@@ -921,18 +919,17 @@
mViewMediatorCallback.readyForKeyguardDone();
}
+ @Override
public boolean shouldDisableWindowAnimationsForUnlock() {
return mStatusBar.isInLaunchTransition();
}
-
- /**
- * @return Whether subtle animation should be used for unlocking the device.
- */
+ @Override
public boolean shouldSubtleWindowAnimationsForUnlock() {
return needsBypassFading();
}
+ @Override
public boolean isGoingToNotificationShade() {
return mStatusBarStateController.leaveOpenOnKeyguardHide();
}
@@ -941,13 +938,12 @@
return mBouncer.isSecure() || mLockPatternUtils.isSecure(userId);
}
+ @Override
public void keyguardGoingAway() {
mStatusBar.keyguardGoingAway();
}
- /**
- * Called when cancel button in bouncer is pressed.
- */
+ @Override
public void onCancelClicked() {
// No-op
}
@@ -964,6 +960,7 @@
mBouncer.showMessage(message, colorState);
}
+ @Override
public ViewRootImpl getViewRootImpl() {
return mStatusBar.getStatusBarView().getViewRootImpl();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index e1a20b6..53fa263 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -68,12 +68,12 @@
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.HeadsUpUtil;
@@ -108,7 +108,7 @@
private final NotifCollection mNotifCollection;
private final FeatureFlags mFeatureFlags;
private final StatusBarStateController mStatusBarStateController;
- private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
+ private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private final MetricsLogger mMetricsLogger;
private final Context mContext;
private final NotificationPanelViewController mNotificationPanel;
@@ -142,7 +142,7 @@
NotificationLockscreenUserManager lockscreenUserManager,
ShadeController shadeController, StatusBar statusBar,
KeyguardStateController keyguardStateController,
- NotificationInterruptionStateProvider notificationInterruptionStateProvider,
+ NotificationInterruptStateProvider notificationInterruptStateProvider,
MetricsLogger metricsLogger, LockPatternUtils lockPatternUtils,
Handler mainThreadHandler, Handler backgroundHandler, Executor uiBgExecutor,
ActivityIntentHelper activityIntentHelper, BubbleController bubbleController,
@@ -167,7 +167,7 @@
mActivityStarter = activityStarter;
mEntryManager = entryManager;
mStatusBarStateController = statusBarStateController;
- mNotificationInterruptionStateProvider = notificationInterruptionStateProvider;
+ mNotificationInterruptStateProvider = notificationInterruptStateProvider;
mMetricsLogger = metricsLogger;
mAssistManagerLazy = assistManagerLazy;
mGroupManager = groupManager;
@@ -436,7 +436,7 @@
}
private void handleFullScreenIntent(NotificationEntry entry) {
- if (mNotificationInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) {
+ if (mNotificationInterruptStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) {
if (shouldSuppressFullScreenIntent(entry)) {
if (DEBUG) {
Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + entry.getKey());
@@ -603,7 +603,7 @@
private final ActivityIntentHelper mActivityIntentHelper;
private final BubbleController mBubbleController;
private NotificationPanelViewController mNotificationPanelViewController;
- private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
+ private NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private final ShadeController mShadeController;
private NotificationPresenter mNotificationPresenter;
private ActivityLaunchAnimator mActivityLaunchAnimator;
@@ -626,7 +626,7 @@
NotificationGroupManager groupManager,
NotificationLockscreenUserManager lockscreenUserManager,
KeyguardStateController keyguardStateController,
- NotificationInterruptionStateProvider notificationInterruptionStateProvider,
+ NotificationInterruptStateProvider notificationInterruptStateProvider,
MetricsLogger metricsLogger,
LockPatternUtils lockPatternUtils,
@Main Handler mainThreadHandler,
@@ -654,7 +654,7 @@
mGroupManager = groupManager;
mLockscreenUserManager = lockscreenUserManager;
mKeyguardStateController = keyguardStateController;
- mNotificationInterruptionStateProvider = notificationInterruptionStateProvider;
+ mNotificationInterruptStateProvider = notificationInterruptStateProvider;
mMetricsLogger = metricsLogger;
mLockPatternUtils = lockPatternUtils;
mMainThreadHandler = mainThreadHandler;
@@ -712,7 +712,7 @@
mShadeController,
mStatusBar,
mKeyguardStateController,
- mNotificationInterruptionStateProvider,
+ mNotificationInterruptStateProvider,
mMetricsLogger,
mLockPatternUtils,
mMainThreadHandler,
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 30d6b507..79cea91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -60,13 +60,13 @@
import com.android.systemui.statusbar.notification.AboveShelfObserver;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationAlertingManager;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -98,8 +98,6 @@
(SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class);
private final NotificationEntryManager mEntryManager =
Dependency.get(NotificationEntryManager.class);
- private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider =
- Dependency.get(NotificationInterruptionStateProvider.class);
private final NotificationMediaManager mMediaManager =
Dependency.get(NotificationMediaManager.class);
private final VisualStabilityManager mVisualStabilityManager =
@@ -140,13 +138,13 @@
ScrimController scrimController,
ActivityLaunchAnimator activityLaunchAnimator,
DynamicPrivacyController dynamicPrivacyController,
- NotificationAlertingManager notificationAlertingManager,
KeyguardStateController keyguardStateController,
KeyguardIndicationController keyguardIndicationController,
StatusBar statusBar,
ShadeController shadeController,
CommandQueue commandQueue,
- InitController initController) {
+ InitController initController,
+ NotificationInterruptStateProvider notificationInterruptStateProvider) {
mContext = context;
mKeyguardStateController = keyguardStateController;
mNotificationPanel = panel;
@@ -216,8 +214,7 @@
mEntryManager.addNotificationLifetimeExtender(mGutsManager);
mEntryManager.addNotificationLifetimeExtenders(
remoteInputManager.getLifetimeExtenders());
- mNotificationInterruptionStateProvider.setUpWithPresenter(
- this, mHeadsUpManager, this::canHeadsUp);
+ notificationInterruptStateProvider.addSuppressor(mInterruptSuppressor);
mLockscreenUserManager.setUpWithPresenter(this);
mMediaManager.setUpWithPresenter(this);
mVisualStabilityManager.setUpWithPresenter(this);
@@ -336,39 +333,6 @@
return mEntryManager.hasActiveNotifications();
}
- public boolean canHeadsUp(NotificationEntry entry, StatusBarNotification sbn) {
- if (mStatusBar.isOccluded()) {
- boolean devicePublic = mLockscreenUserManager.
- isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId());
- boolean userPublic = devicePublic
- || mLockscreenUserManager.isLockscreenPublicMode(sbn.getUserId());
- boolean needsRedaction = mLockscreenUserManager.needsRedaction(entry);
- if (userPublic && needsRedaction) {
- // TODO(b/135046837): we can probably relax this with dynamic privacy
- return false;
- }
- }
-
- if (!mCommandQueue.panelsEnabled()) {
- if (DEBUG) {
- Log.d(TAG, "No heads up: disabled panel : " + sbn.getKey());
- }
- return false;
- }
-
- if (sbn.getNotification().fullScreenIntent != null) {
- if (mAccessibilityManager.isTouchExplorationEnabled()) {
- if (DEBUG) Log.d(TAG, "No heads up: accessible fullscreen: " + sbn.getKey());
- return false;
- } else {
- // we only allow head-up on the lockscreen if it doesn't have a fullscreen intent
- return !mKeyguardStateController.isShowing()
- || mStatusBar.isOccluded();
- }
- }
- return true;
- }
-
@Override
public void onUserSwitched(int newUserId) {
// Begin old BaseStatusBar.userSwitched
@@ -507,4 +471,66 @@
}
}
};
+
+ private final NotificationInterruptSuppressor mInterruptSuppressor =
+ new NotificationInterruptSuppressor() {
+ @Override
+ public String getName() {
+ return TAG;
+ }
+
+ @Override
+ public boolean suppressAwakeHeadsUp(NotificationEntry entry) {
+ final StatusBarNotification sbn = entry.getSbn();
+ if (mStatusBar.isOccluded()) {
+ boolean devicePublic = mLockscreenUserManager
+ .isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId());
+ boolean userPublic = devicePublic
+ || mLockscreenUserManager.isLockscreenPublicMode(sbn.getUserId());
+ boolean needsRedaction = mLockscreenUserManager.needsRedaction(entry);
+ if (userPublic && needsRedaction) {
+ // TODO(b/135046837): we can probably relax this with dynamic privacy
+ return true;
+ }
+ }
+
+ if (!mCommandQueue.panelsEnabled()) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: disabled panel : " + sbn.getKey());
+ }
+ return true;
+ }
+
+ if (sbn.getNotification().fullScreenIntent != null) {
+ // we don't allow head-up on the lockscreen (unless there's a
+ // "showWhenLocked" activity currently showing) if
+ // the potential HUN has a fullscreen intent
+ if (mKeyguardStateController.isShowing() && !mStatusBar.isOccluded()) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: entry has fullscreen intent on lockscreen "
+ + sbn.getKey());
+ }
+ return true;
+ }
+
+ if (mAccessibilityManager.isTouchExplorationEnabled()) {
+ if (DEBUG) {
+ Log.d(TAG, "No heads up: accessible fullscreen: " + sbn.getKey());
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean suppressAwakeInterruptions(NotificationEntry entry) {
+ return isDeviceInVrMode();
+ }
+
+ @Override
+ public boolean suppressInterruptions(NotificationEntry entry) {
+ return mStatusBar.areNotificationAlertsDisabled();
+ }
+ };
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index eec8d50..824e0f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -56,13 +56,12 @@
import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationAlertingManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.init.NotificationsController;
+import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.phone.AutoHideController;
@@ -139,10 +138,9 @@
RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
- NotificationInterruptionStateProvider notificationInterruptionStateProvider,
+ NotificationInterruptStateProvider notificationInterruptStateProvider,
NotificationViewHierarchyManager notificationViewHierarchyManager,
KeyguardViewMediator keyguardViewMediator,
- NotificationAlertingManager notificationAlertingManager,
DisplayMetrics displayMetrics,
MetricsLogger metricsLogger,
@UiBackground Executor uiBgExecutor,
@@ -218,10 +216,9 @@
remoteInputQuickSettingsDisabler,
notificationGutsManager,
notificationLogger,
- notificationInterruptionStateProvider,
+ notificationInterruptStateProvider,
notificationViewHierarchyManager,
keyguardViewMediator,
- notificationAlertingManager,
displayMetrics,
metricsLogger,
uiBgExecutor,
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
index 13ba1a3c..378dde2 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
@@ -170,7 +170,11 @@
return false;
}
- mListeners.add(listener);
+ if (mListeners.contains(listener)) {
+ Log.d(TAG, "ProxListener registered multiple times: " + listener);
+ } else {
+ mListeners.add(listener);
+ }
registerInternal();
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index af218c49..ce032e2 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -65,7 +65,6 @@
import android.util.Slog;
import android.util.SparseBooleanArray;
import android.view.ContextThemeWrapper;
-import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.AccessibilityDelegate;
@@ -224,7 +223,7 @@
lp.format = PixelFormat.TRANSLUCENT;
lp.setTitle(VolumeDialogImpl.class.getSimpleName());
lp.windowAnimations = -1;
- lp.gravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL;
+ lp.gravity = mContext.getResources().getInteger(R.integer.volume_dialog_gravity);
mWindow.setAttributes(lp);
mWindow.setLayout(WRAP_CONTENT, WRAP_CONTENT);
@@ -825,7 +824,7 @@
}
protected void updateRingerH() {
- if (mState != null) {
+ if (mRinger != null && mState != null) {
final StreamState ss = mState.states.get(AudioManager.STREAM_RING);
if (ss == null) {
return;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 742e652..977d0bb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -45,7 +45,11 @@
import android.app.Notification;
import android.app.PendingIntent;
import android.content.res.Resources;
+import android.hardware.display.AmbientDisplayConfiguration;
import android.hardware.face.FaceManager;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.service.dreams.IDreamManager;
import android.service.notification.ZenModeConfig;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -61,14 +65,12 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoveInterceptor;
import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationFilter;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
@@ -227,15 +229,17 @@
mZenModeConfig.suppressedVisualEffects = 0;
when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
- TestableNotificationInterruptionStateProvider interruptionStateProvider =
- new TestableNotificationInterruptionStateProvider(mContext,
+ TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
+ new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
+ mock(PowerManager.class),
+ mock(IDreamManager.class),
+ mock(AmbientDisplayConfiguration.class),
mock(NotificationFilter.class),
mock(StatusBarStateController.class),
- mock(BatteryController.class));
- interruptionStateProvider.setUpWithPresenter(
- mock(NotificationPresenter.class),
- mock(HeadsUpManager.class),
- mock(NotificationInterruptionStateProvider.HeadsUpSuppressor.class));
+ mock(BatteryController.class),
+ mock(HeadsUpManager.class),
+ mock(Handler.class)
+ );
mBubbleData = new BubbleData(mContext);
when(mFeatureFlagsOldPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
mBubbleController = new TestableBubbleController(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
index 22ef3f3..7fc83da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -41,7 +41,11 @@
import android.app.Notification;
import android.app.PendingIntent;
import android.content.res.Resources;
+import android.hardware.display.AmbientDisplayConfiguration;
import android.hardware.face.FaceManager;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.service.dreams.IDreamManager;
import android.service.notification.ZenModeConfig;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -57,12 +61,10 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationFilter;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
@@ -216,15 +218,17 @@
mZenModeConfig.suppressedVisualEffects = 0;
when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
- TestableNotificationInterruptionStateProvider interruptionStateProvider =
- new TestableNotificationInterruptionStateProvider(mContext,
+ TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
+ new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
+ mock(PowerManager.class),
+ mock(IDreamManager.class),
+ mock(AmbientDisplayConfiguration.class),
mock(NotificationFilter.class),
mock(StatusBarStateController.class),
- mock(BatteryController.class));
- interruptionStateProvider.setUpWithPresenter(
- mock(NotificationPresenter.class),
- mock(HeadsUpManager.class),
- mock(NotificationInterruptionStateProvider.HeadsUpSuppressor.class));
+ mock(BatteryController.class),
+ mock(HeadsUpManager.class),
+ mock(Handler.class)
+ );
mBubbleData = new BubbleData(mContext);
when(mFeatureFlagsNewPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(true);
mBubbleController = new TestableBubbleController(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
index de1fb41..d3d90c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
@@ -23,8 +23,8 @@
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.ShadeController;
@@ -44,7 +44,7 @@
ShadeController shadeController,
BubbleData data,
ConfigurationController configurationController,
- NotificationInterruptionStateProvider interruptionStateProvider,
+ NotificationInterruptStateProvider interruptionStateProvider,
ZenModeController zenModeController,
NotificationLockscreenUserManager lockscreenUserManager,
NotificationGroupManager groupManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptStateProviderImpl.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptStateProviderImpl.java
new file mode 100644
index 0000000..17dc76b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptStateProviderImpl.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bubbles;
+
+import android.content.ContentResolver;
+import android.hardware.display.AmbientDisplayConfiguration;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.service.dreams.IDreamManager;
+
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.NotificationFilter;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+
+public class TestableNotificationInterruptStateProviderImpl
+ extends NotificationInterruptStateProviderImpl {
+
+ TestableNotificationInterruptStateProviderImpl(
+ ContentResolver contentResolver,
+ PowerManager powerManager,
+ IDreamManager dreamManager,
+ AmbientDisplayConfiguration ambientDisplayConfiguration,
+ NotificationFilter filter,
+ StatusBarStateController statusBarStateController,
+ BatteryController batteryController,
+ HeadsUpManager headsUpManager,
+ Handler mainHandler) {
+ super(contentResolver,
+ powerManager,
+ dreamManager,
+ ambientDisplayConfiguration,
+ filter,
+ batteryController,
+ statusBarStateController,
+ headsUpManager,
+ mainHandler);
+ mUseHeadsUp = true;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptionStateProvider.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptionStateProvider.java
deleted file mode 100644
index 5d192b2..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptionStateProvider.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.bubbles;
-
-import android.content.Context;
-
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.notification.NotificationFilter;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
-import com.android.systemui.statusbar.policy.BatteryController;
-
-public class TestableNotificationInterruptionStateProvider
- extends NotificationInterruptionStateProvider {
-
- TestableNotificationInterruptionStateProvider(Context context,
- NotificationFilter filter, StatusBarStateController controller,
- BatteryController batteryController) {
- super(context, filter, controller, batteryController);
- mUseHeadsUp = true;
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestReceiverTest.kt
new file mode 100644
index 0000000..663f011
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestReceiverTest.kt
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls.management
+
+import android.app.ActivityManager
+import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
+import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE
+import android.content.ComponentName
+import android.content.Context
+import android.content.ContextWrapper
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.os.UserHandle
+import android.service.controls.Control
+import android.service.controls.ControlsProviderService
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
+import org.junit.Assert.fail
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class ControlsRequestReceiverTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var packageManager: PackageManager
+ @Mock
+ private lateinit var activityManager: ActivityManager
+ @Mock
+ private lateinit var control: Control
+
+ private val componentName = ComponentName("test_pkg", "test_cls")
+ private lateinit var receiver: ControlsRequestReceiver
+ private lateinit var wrapper: MyWrapper
+ private lateinit var intent: Intent
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ mContext.setMockPackageManager(packageManager)
+ mContext.addMockSystemService(ActivityManager::class.java, activityManager)
+
+ receiver = ControlsRequestReceiver()
+
+ wrapper = MyWrapper(context)
+
+ intent = Intent(ControlsProviderService.ACTION_ADD_CONTROL).apply {
+ putExtra(Intent.EXTRA_COMPONENT_NAME, componentName)
+ putExtra(ControlsProviderService.EXTRA_CONTROL, control)
+ }
+ }
+
+ @Test
+ fun testPackageVerification_nonExistentPackage() {
+ `when`(packageManager.getPackageUid(anyString(), anyInt()))
+ .thenThrow(PackageManager.NameNotFoundException::class.java)
+
+ assertFalse(ControlsRequestReceiver.isPackageInForeground(mContext, "TEST"))
+ }
+
+ @Test
+ fun testPackageVerification_uidNotInForeground() {
+ `when`(packageManager.getPackageUid(anyString(), anyInt())).thenReturn(12345)
+
+ `when`(activityManager.getUidImportance(anyInt())).thenReturn(IMPORTANCE_GONE)
+
+ assertFalse(ControlsRequestReceiver.isPackageInForeground(mContext, "TEST"))
+ }
+
+ @Test
+ fun testPackageVerification_OK() {
+ `when`(packageManager.getPackageUid(anyString(), anyInt())).thenReturn(12345)
+
+ `when`(activityManager.getUidImportance(anyInt())).thenReturn(IMPORTANCE_GONE)
+ `when`(activityManager.getUidImportance(12345)).thenReturn(IMPORTANCE_FOREGROUND)
+
+ assertTrue(ControlsRequestReceiver.isPackageInForeground(mContext, "TEST"))
+ }
+
+ @Test
+ fun testOnReceive_packageNotVerified_nameNotFound() {
+ `when`(packageManager.getPackageUid(eq(componentName.packageName), anyInt()))
+ .thenThrow(PackageManager.NameNotFoundException::class.java)
+
+ receiver.onReceive(wrapper, intent)
+
+ assertNull(wrapper.intent)
+ }
+
+ @Test
+ fun testOnReceive_packageNotVerified_notForeground() {
+ `when`(packageManager.getPackageUid(eq(componentName.packageName), anyInt()))
+ .thenReturn(12345)
+
+ `when`(activityManager.getUidImportance(anyInt())).thenReturn(IMPORTANCE_GONE)
+
+ receiver.onReceive(wrapper, intent)
+
+ assertNull(wrapper.intent)
+ }
+
+ @Test
+ fun testOnReceive_OK() {
+ `when`(packageManager.getPackageUid(eq(componentName.packageName), anyInt()))
+ .thenReturn(12345)
+
+ `when`(activityManager.getUidImportance(eq(12345))).thenReturn(IMPORTANCE_FOREGROUND)
+
+ receiver.onReceive(wrapper, intent)
+
+ wrapper.intent?.let {
+ assertEquals(ComponentName(wrapper, ControlsRequestDialog::class.java), it.component)
+
+ assertEquals(control, it.getParcelableExtra(ControlsProviderService.EXTRA_CONTROL))
+
+ assertEquals(componentName, it.getParcelableExtra(Intent.EXTRA_COMPONENT_NAME))
+ } ?: run { fail("Null start intent") }
+ }
+
+ class MyWrapper(context: Context) : ContextWrapper(context) {
+ var intent: Intent? = null
+
+ override fun startActivityAsUser(intent: Intent, user: UserHandle) {
+ // Always launch activity as system
+ assertTrue(user == UserHandle.SYSTEM)
+ this.intent = intent
+ }
+
+ override fun startActivity(intent: Intent) {
+ this.intent = intent
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
similarity index 63%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
index 1693e7f..f9c62e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.statusbar.notification.interruption;
import static android.app.Notification.FLAG_BUBBLE;
@@ -30,15 +30,14 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Notification;
import android.app.PendingIntent;
-import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Icon;
import android.hardware.display.AmbientDisplayConfiguration;
+import android.os.Handler;
import android.os.PowerManager;
import android.os.RemoteException;
import android.service.dreams.IDreamManager;
@@ -50,7 +49,6 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.NotificationFilter;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -68,7 +66,7 @@
*/
@RunWith(AndroidTestingRunner.class)
@SmallTest
-public class NotificationInterruptionStateProviderTest extends SysuiTestCase {
+public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
@Mock
PowerManager mPowerManager;
@@ -81,38 +79,36 @@
@Mock
StatusBarStateController mStatusBarStateController;
@Mock
- NotificationPresenter mPresenter;
- @Mock
HeadsUpManager mHeadsUpManager;
@Mock
- NotificationInterruptionStateProvider.HeadsUpSuppressor mHeadsUpSuppressor;
- @Mock
BatteryController mBatteryController;
+ @Mock
+ Handler mMockHandler;
- private NotificationInterruptionStateProvider mNotifInterruptionStateProvider;
+ private NotificationInterruptStateProviderImpl mNotifInterruptionStateProvider;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mNotifInterruptionStateProvider =
- new TestableNotificationInterruptionStateProvider(mContext,
+ new NotificationInterruptStateProviderImpl(
+ mContext.getContentResolver(),
mPowerManager,
mDreamManager,
mAmbientDisplayConfiguration,
mNotificationFilter,
+ mBatteryController,
mStatusBarStateController,
- mBatteryController);
+ mHeadsUpManager,
+ mMockHandler);
- mNotifInterruptionStateProvider.setUpWithPresenter(
- mPresenter,
- mHeadsUpManager,
- mHeadsUpSuppressor);
+ mNotifInterruptionStateProvider.mUseHeadsUp = true;
}
/**
* Sets up the state such that any requests to
- * {@link NotificationInterruptionStateProvider#canAlertCommon(NotificationEntry)} will
+ * {@link NotificationInterruptStateProviderImpl#canAlertCommon(NotificationEntry)} will
* pass as long its provided NotificationEntry fulfills group suppression check.
*/
private void ensureStateForAlertCommon() {
@@ -121,17 +117,16 @@
/**
* Sets up the state such that any requests to
- * {@link NotificationInterruptionStateProvider#canAlertAwakeCommon(NotificationEntry)} will
+ * {@link NotificationInterruptStateProviderImpl#canAlertAwakeCommon(NotificationEntry)} will
* pass as long its provided NotificationEntry fulfills launch fullscreen check.
*/
private void ensureStateForAlertAwakeCommon() {
- when(mPresenter.isDeviceInVrMode()).thenReturn(false);
when(mHeadsUpManager.isSnoozed(any())).thenReturn(false);
}
/**
* Sets up the state such that any requests to
- * {@link NotificationInterruptionStateProvider#shouldHeadsUp(NotificationEntry)} will
+ * {@link NotificationInterruptStateProviderImpl#shouldHeadsUp(NotificationEntry)} will
* pass as long its provided NotificationEntry fulfills importance & DND checks.
*/
private void ensureStateForHeadsUpWhenAwake() throws RemoteException {
@@ -141,12 +136,11 @@
when(mStatusBarStateController.isDozing()).thenReturn(false);
when(mDreamManager.isDreaming()).thenReturn(false);
when(mPowerManager.isScreenOn()).thenReturn(true);
- when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
}
/**
* Sets up the state such that any requests to
- * {@link NotificationInterruptionStateProvider#shouldHeadsUp(NotificationEntry)} will
+ * {@link NotificationInterruptStateProviderImpl#shouldHeadsUp(NotificationEntry)} will
* pass as long its provided NotificationEntry fulfills importance & DND checks.
*/
private void ensureStateForHeadsUpWhenDozing() {
@@ -158,7 +152,7 @@
/**
* Sets up the state such that any requests to
- * {@link NotificationInterruptionStateProvider#shouldBubbleUp(NotificationEntry)} will
+ * {@link NotificationInterruptStateProviderImpl#shouldBubbleUp(NotificationEntry)} will
* pass as long its provided NotificationEntry fulfills importance & bubble checks.
*/
private void ensureStateForBubbleUp() {
@@ -166,75 +160,53 @@
ensureStateForAlertAwakeCommon();
}
- /**
- * Ensure that the disabled state is set correctly.
- */
@Test
- public void testDisableNotificationAlerts() {
- // Enabled by default
- assertThat(mNotifInterruptionStateProvider.areNotificationAlertsDisabled()).isFalse();
-
- // Disable alerts
- mNotifInterruptionStateProvider.setDisableNotificationAlerts(true);
- assertThat(mNotifInterruptionStateProvider.areNotificationAlertsDisabled()).isTrue();
-
- // Enable alerts
- mNotifInterruptionStateProvider.setDisableNotificationAlerts(false);
- assertThat(mNotifInterruptionStateProvider.areNotificationAlertsDisabled()).isFalse();
- }
-
- /**
- * Ensure that the disabled alert state effects whether HUNs are enabled.
- */
- @Test
- public void testHunSettingsChange_enabled_butAlertsDisabled() {
- // Set up but without a mock change observer
- mNotifInterruptionStateProvider.setUpWithPresenter(
- mPresenter,
- mHeadsUpManager,
- mHeadsUpSuppressor);
-
- // HUNs enabled by default
- assertThat(mNotifInterruptionStateProvider.getUseHeadsUp()).isTrue();
-
- // Set alerts disabled
- mNotifInterruptionStateProvider.setDisableNotificationAlerts(true);
-
- // No more HUNs
- assertThat(mNotifInterruptionStateProvider.getUseHeadsUp()).isFalse();
- }
-
- /**
- * Alerts can happen.
- */
- @Test
- public void testCanAlertCommon_true() {
- ensureStateForAlertCommon();
+ public void testDefaultSuppressorDoesNotSuppress() {
+ // GIVEN a suppressor without any overrides
+ final NotificationInterruptSuppressor defaultSuppressor =
+ new NotificationInterruptSuppressor() {
+ @Override
+ public String getName() {
+ return "defaultSuppressor";
+ }
+ };
NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
- assertThat(mNotifInterruptionStateProvider.canAlertCommon(entry)).isTrue();
+
+ // THEN this suppressor doesn't suppress anything by default
+ assertThat(defaultSuppressor.suppressAwakeHeadsUp(entry)).isFalse();
+ assertThat(defaultSuppressor.suppressAwakeInterruptions(entry)).isFalse();
+ assertThat(defaultSuppressor.suppressInterruptions(entry)).isFalse();
}
- /**
- * Filtered out notifications don't alert.
- */
@Test
- public void testCanAlertCommon_false_filteredOut() {
- ensureStateForAlertCommon();
- when(mNotificationFilter.shouldFilterOut(any())).thenReturn(true);
+ public void testShouldHeadsUpAwake() throws RemoteException {
+ ensureStateForHeadsUpWhenAwake();
- NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
- assertThat(mNotifInterruptionStateProvider.canAlertCommon(entry)).isFalse();
+ NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
+ assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
}
- /**
- * Grouped notifications have different alerting behaviours, sometimes the alert for a
- * grouped notification may be suppressed {@link android.app.Notification#GROUP_ALERT_CHILDREN}.
- */
@Test
- public void testCanAlertCommon_false_suppressedForGroups() {
- ensureStateForAlertCommon();
+ public void testShouldNotHeadsUpAwake_flteredOut() throws RemoteException {
+ // GIVEN state for "heads up when awake" is true
+ ensureStateForHeadsUpWhenAwake();
+ // WHEN this entry should be filtered out
+ NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
+ when(mNotificationFilter.shouldFilterOut(entry)).thenReturn(true);
+
+ // THEN we shouldn't heads up this entry
+ assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
+ }
+
+ @Test
+ public void testShouldNotHeadsUp_suppressedForGroups() throws RemoteException {
+ // GIVEN state for "heads up when awake" is true
+ ensureStateForHeadsUpWhenAwake();
+
+ // WHEN the alert for a grouped notification is suppressed
+ // see {@link android.app.Notification#GROUP_ALERT_CHILDREN}
NotificationEntry entry = new NotificationEntryBuilder()
.setPkg("a")
.setOpPkg("a")
@@ -247,40 +219,40 @@
.setImportance(IMPORTANCE_DEFAULT)
.build();
- assertThat(mNotifInterruptionStateProvider.canAlertCommon(entry)).isFalse();
+ // THEN this entry shouldn't HUN
+ assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
}
- /**
- * HUNs while dozing can happen.
- */
@Test
- public void testShouldHeadsUpWhenDozing_true() {
+ public void testShouldHeadsUpWhenDozing() {
ensureStateForHeadsUpWhenDozing();
NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
}
- /**
- * Ambient display can show HUNs for new notifications, this may be disabled.
- */
@Test
- public void testShouldHeadsUpWhenDozing_false_pulseDisabled() {
+ public void testShouldNotHeadsUpWhenDozing_pulseDisabled() {
+ // GIVEN state for "heads up when dozing" is true
ensureStateForHeadsUpWhenDozing();
+
+ // WHEN pulsing (HUNs when dozing) is disabled
when(mAmbientDisplayConfiguration.pulseOnNotificationEnabled(anyInt())).thenReturn(false);
+ // THEN this entry shouldn't HUN
NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
}
- /**
- * If the device is not in ambient display or sleeping then we don't HUN.
- */
@Test
- public void testShouldHeadsUpWhenDozing_false_notDozing() {
+ public void testShouldNotHeadsUpWhenDozing_notDozing() {
+ // GIVEN state for "heads up when dozing" is true
ensureStateForHeadsUpWhenDozing();
+
+ // WHEN we're not dozing (in ambient display or sleeping)
when(mStatusBarStateController.isDozing()).thenReturn(false);
+ // THEN this entry shouldn't HUN
NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
}
@@ -290,7 +262,7 @@
* {@link android.app.NotificationManager.Policy#SUPPRESSED_EFFECT_AMBIENT}.
*/
@Test
- public void testShouldHeadsUpWhenDozing_false_suppressingAmbient() {
+ public void testShouldNotHeadsUpWhenDozing_suppressingAmbient() {
ensureStateForHeadsUpWhenDozing();
NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
@@ -301,23 +273,18 @@
assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
}
- /**
- * Notifications that are < {@link android.app.NotificationManager#IMPORTANCE_DEFAULT} don't
- * get to pulse.
- */
@Test
- public void testShouldHeadsUpWhenDozing_false_lessImportant() {
+ public void testShouldNotHeadsUpWhenDozing_lessImportant() {
ensureStateForHeadsUpWhenDozing();
+ // Notifications that are < {@link android.app.NotificationManager#IMPORTANCE_DEFAULT} don't
+ // get to pulse
NotificationEntry entry = createNotification(IMPORTANCE_LOW);
assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
}
- /**
- * Heads up can happen.
- */
@Test
- public void testShouldHeadsUp_true() throws RemoteException {
+ public void testShouldHeadsUp() throws RemoteException {
ensureStateForHeadsUpWhenAwake();
NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
@@ -325,38 +292,11 @@
}
/**
- * Heads up notifications can be disabled in general.
- */
- @Test
- public void testShouldHeadsUp_false_noHunsAllowed() throws RemoteException {
- ensureStateForHeadsUpWhenAwake();
-
- // Set alerts disabled, this should cause heads up to be false
- mNotifInterruptionStateProvider.setDisableNotificationAlerts(true);
- assertThat(mNotifInterruptionStateProvider.getUseHeadsUp()).isFalse();
-
- NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
- assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
- }
-
- /**
- * If the device is dozing, we don't show as heads up.
- */
- @Test
- public void testShouldHeadsUp_false_dozing() throws RemoteException {
- ensureStateForHeadsUpWhenAwake();
- when(mStatusBarStateController.isDozing()).thenReturn(true);
-
- NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
- assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
- }
-
- /**
* If the notification is a bubble, and the user is not on AOD / lockscreen, then
* the bubble is shown rather than the heads up.
*/
@Test
- public void testShouldHeadsUp_false_bubble() throws RemoteException {
+ public void testShouldNotHeadsUp_bubble() throws RemoteException {
ensureStateForHeadsUpWhenAwake();
// Bubble bit only applies to interruption when we're in the shade
@@ -369,7 +309,7 @@
* If we're not allowed to alert in general, we shouldn't be shown as heads up.
*/
@Test
- public void testShouldHeadsUp_false_alertCommonFalse() throws RemoteException {
+ public void testShouldNotHeadsUp_filtered() throws RemoteException {
ensureStateForHeadsUpWhenAwake();
// Make canAlertCommon false by saying it's filtered out
when(mNotificationFilter.shouldFilterOut(any())).thenReturn(true);
@@ -383,7 +323,7 @@
* {@link android.app.NotificationManager.Policy#SUPPRESSED_EFFECT_PEEK}.
*/
@Test
- public void testShouldHeadsUp_false_suppressPeek() throws RemoteException {
+ public void testShouldNotHeadsUp_suppressPeek() throws RemoteException {
ensureStateForHeadsUpWhenAwake();
NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
@@ -399,7 +339,7 @@
* to show as a heads up.
*/
@Test
- public void testShouldHeadsUp_false_lessImportant() throws RemoteException {
+ public void testShouldNotHeadsUp_lessImportant() throws RemoteException {
ensureStateForHeadsUpWhenAwake();
NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
@@ -410,7 +350,7 @@
* If the device is not in use then we shouldn't be shown as heads up.
*/
@Test
- public void testShouldHeadsUp_false_deviceNotInUse() throws RemoteException {
+ public void testShouldNotHeadsUp_deviceNotInUse() throws RemoteException {
ensureStateForHeadsUpWhenAwake();
NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
@@ -424,61 +364,58 @@
assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
}
- /**
- * If something wants to suppress this heads up, then it shouldn't be shown as a heads up.
- */
@Test
- public void testShouldHeadsUp_false_suppressed() throws RemoteException {
+ public void testShouldNotHeadsUp_headsUpSuppressed() throws RemoteException {
ensureStateForHeadsUpWhenAwake();
- when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(false);
+
+ // If a suppressor is suppressing heads up, then it shouldn't be shown as a heads up.
+ mNotifInterruptionStateProvider.addSuppressor(mSuppressAwakeHeadsUp);
NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
- verify(mHeadsUpSuppressor).canHeadsUp(any(), any());
}
- /**
- * On screen alerts don't happen when the device is in VR Mode.
- */
@Test
- public void testCanAlertAwakeCommon__false_vrMode() {
- ensureStateForAlertAwakeCommon();
- when(mPresenter.isDeviceInVrMode()).thenReturn(true);
+ public void testShouldNotHeadsUpAwake_awakeInterruptsSuppressed() throws RemoteException {
+ ensureStateForHeadsUpWhenAwake();
- NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
- assertThat(mNotifInterruptionStateProvider.canAlertAwakeCommon(entry)).isFalse();
+ // If a suppressor is suppressing heads up, then it shouldn't be shown as a heads up.
+ mNotifInterruptionStateProvider.addSuppressor(mSuppressAwakeInterruptions);
+
+ NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
+ assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
}
/**
* On screen alerts don't happen when the notification is snoozed.
*/
@Test
- public void testCanAlertAwakeCommon_false_snoozedPackage() {
- ensureStateForAlertAwakeCommon();
- when(mHeadsUpManager.isSnoozed(any())).thenReturn(true);
-
+ public void testShouldNotHeadsUp_snoozedPackage() {
NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
- assertThat(mNotifInterruptionStateProvider.canAlertAwakeCommon(entry)).isFalse();
+ ensureStateForAlertAwakeCommon();
+
+ when(mHeadsUpManager.isSnoozed(entry.getSbn().getPackageName())).thenReturn(true);
+
+ assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
}
- /**
- * On screen alerts don't happen when that package has just launched fullscreen.
- */
+
@Test
- public void testCanAlertAwakeCommon_false_justLaunchedFullscreen() {
+ public void testShouldNotHeadsUp_justLaunchedFullscreen() {
ensureStateForAlertAwakeCommon();
+ // On screen alerts don't happen when that package has just launched fullscreen.
NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT);
entry.notifyFullScreenIntentLaunched();
- assertThat(mNotifInterruptionStateProvider.canAlertAwakeCommon(entry)).isFalse();
+ assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
}
/**
* Bubbles can happen.
*/
@Test
- public void testShouldBubbleUp_true() {
+ public void testShouldBubbleUp() {
ensureStateForBubbleUp();
assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(createBubble())).isTrue();
}
@@ -487,7 +424,7 @@
* If the notification doesn't have permission to bubble, it shouldn't bubble.
*/
@Test
- public void shouldBubbleUp_false_notAllowedToBubble() {
+ public void shouldNotBubbleUp_notAllowedToBubble() {
ensureStateForBubbleUp();
NotificationEntry entry = createBubble();
@@ -502,7 +439,7 @@
* If the notification isn't a bubble, it should definitely not show as a bubble.
*/
@Test
- public void shouldBubbleUp_false_notABubble() {
+ public void shouldNotBubbleUp_notABubble() {
ensureStateForBubbleUp();
NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
@@ -517,7 +454,7 @@
* If the notification doesn't have bubble metadata, it shouldn't bubble.
*/
@Test
- public void shouldBubbleUp_false_invalidMetadata() {
+ public void shouldNotBubbleUp_invalidMetadata() {
ensureStateForBubbleUp();
NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
@@ -529,24 +466,18 @@
assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(entry)).isFalse();
}
- /**
- * If the notification can't heads up in general, it shouldn't bubble.
- */
@Test
- public void shouldBubbleUp_false_alertAwakeCommonFalse() {
+ public void shouldNotBubbleUp_suppressedInterruptions() {
ensureStateForBubbleUp();
- // Make alert common return false by pretending we're in VR mode
- when(mPresenter.isDeviceInVrMode()).thenReturn(true);
+ // If the notification can't heads up in general, it shouldn't bubble.
+ mNotifInterruptionStateProvider.addSuppressor(mSuppressInterruptions);
assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(createBubble())).isFalse();
}
- /**
- * If the notification can't heads up in general, it shouldn't bubble.
- */
@Test
- public void shouldBubbleUp_false_alertCommonFalse() {
+ public void shouldNotBubbleUp_filteredOut() {
ensureStateForBubbleUp();
// Make canAlertCommon false by saying it's filtered out
@@ -592,20 +523,45 @@
.build();
}
- /**
- * Testable class overriding constructor.
- */
- public static class TestableNotificationInterruptionStateProvider extends
- NotificationInterruptionStateProvider {
-
- TestableNotificationInterruptionStateProvider(Context context,
- PowerManager powerManager, IDreamManager dreamManager,
- AmbientDisplayConfiguration ambientDisplayConfiguration,
- NotificationFilter notificationFilter,
- StatusBarStateController statusBarStateController,
- BatteryController batteryController) {
- super(context, powerManager, dreamManager, ambientDisplayConfiguration,
- notificationFilter, batteryController, statusBarStateController);
+ private final NotificationInterruptSuppressor
+ mSuppressAwakeHeadsUp =
+ new NotificationInterruptSuppressor() {
+ @Override
+ public String getName() {
+ return "suppressAwakeHeadsUp";
}
- }
+
+ @Override
+ public boolean suppressAwakeHeadsUp(NotificationEntry entry) {
+ return true;
+ }
+ };
+
+ private final NotificationInterruptSuppressor
+ mSuppressAwakeInterruptions =
+ new NotificationInterruptSuppressor() {
+ @Override
+ public String getName() {
+ return "suppressAwakeInterruptions";
+ }
+
+ @Override
+ public boolean suppressAwakeInterruptions(NotificationEntry entry) {
+ return true;
+ }
+ };
+
+ private final NotificationInterruptSuppressor
+ mSuppressInterruptions =
+ new NotificationInterruptSuppressor() {
+ @Override
+ public String getName() {
+ return "suppressInterruptions";
+ }
+
+ @Override
+ public boolean suppressInterruptions(NotificationEntry entry) {
+ return true;
+ }
+ };
}
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 5d0349d..a21a047 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
@@ -56,12 +56,12 @@
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger;
import com.android.systemui.statusbar.notification.NotificationFilter;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
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.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
@@ -108,7 +108,7 @@
@Mock private NotificationEntryListener mEntryListener;
@Mock private NotificationRowBinderImpl.BindRowCallback mBindCallback;
@Mock private HeadsUpManager mHeadsUpManager;
- @Mock private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
+ @Mock private NotificationInterruptStateProvider mNotificationInterruptionStateProvider;
@Mock private NotificationLockscreenUserManager mLockscreenUserManager;
@Mock private NotificationGutsManager mGutsManager;
@Mock private NotificationRemoteInputManager mRemoteInputManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 0cb6585..ef2071ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -46,6 +46,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
@@ -139,6 +140,7 @@
private UserChangedListener mUserChangedListener;
private TestableNotificationEntryManager mEntryManager;
private int mOriginalInterruptionModelSetting;
+ private UiEventLoggerFake mUiEventLoggerFake = new UiEventLoggerFake();
@Before
@@ -214,7 +216,8 @@
mFeatureFlags,
mock(NotifPipeline.class),
mEntryManager,
- mock(NotifCollection.class)
+ mock(NotifCollection.class),
+ mUiEventLoggerFake
);
verify(mLockscreenUserManager).addUserChangedListener(userChangedCaptor.capture());
mUserChangedListener = userChangedCaptor.getValue();
@@ -506,6 +509,22 @@
MetricsProto.MetricsEvent.TYPE_ACTION));
}
+ @Test
+ public void testClearNotifications_All() {
+ mStackScroller.clearNotifications(NotificationStackScrollLayout.ROWS_ALL, true);
+ assertEquals(1, mUiEventLoggerFake.numLogs());
+ assertEquals(NotificationStackScrollLayout.NotificationPanelEvent
+ .DISMISS_ALL_NOTIFICATIONS_PANEL.getId(), mUiEventLoggerFake.eventId(0));
+ }
+
+ @Test
+ public void testClearNotifications_Gentle() {
+ mStackScroller.clearNotifications(NotificationStackScrollLayout.ROWS_GENTLE, false);
+ assertEquals(1, mUiEventLoggerFake.numLogs());
+ assertEquals(NotificationStackScrollLayout.NotificationPanelEvent
+ .DISMISS_SILENT_NOTIFICATIONS_PANEL.getId(), mUiEventLoggerFake.eventId(0));
+ }
+
private void setBarStateForTest(int state) {
// Can't inject this through the listener or we end up on the actual implementation
// rather than the mock because the spy just coppied the anonymous inner /shruggie.
@@ -517,8 +536,4 @@
mEntryManager.addActiveNotificationForTest(e);
}
}
-
- private void addActiveNotificationsToManager(List<NotificationEntry> entries) {
- mEntryManager.setActiveNotificationList(entries);
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 1e4df27..b9c5b7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -67,10 +67,10 @@
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -183,7 +183,7 @@
mock(StatusBarRemoteInputCallback.class), mock(NotificationGroupManager.class),
mock(NotificationLockscreenUserManager.class),
mKeyguardStateController,
- mock(NotificationInterruptionStateProvider.class), mock(MetricsLogger.class),
+ mock(NotificationInterruptStateProvider.class), mock(MetricsLogger.class),
mock(LockPatternUtils.class), mHandler, mHandler, mUiBgExecutor,
mActivityIntentHelper, mBubbleController, mShadeController, mFeatureFlags,
mNotifPipeline, mNotifCollection)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index b9d2d22..318e9b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -16,8 +16,9 @@
import static android.view.Display.DEFAULT_DISPLAY;
-import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Notification;
@@ -35,6 +36,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.logging.testing.FakeMetricsLogger;
+import com.android.systemui.ForegroundServiceNotificationListener;
import com.android.systemui.InitController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -48,12 +50,12 @@
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationAlertingManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -62,6 +64,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import java.util.ArrayList;
@@ -72,6 +75,9 @@
private StatusBarNotificationPresenter mStatusBarNotificationPresenter;
+ private NotificationInterruptStateProvider mNotificationInterruptStateProvider =
+ mock(NotificationInterruptStateProvider.class);
+ private NotificationInterruptSuppressor mInterruptSuppressor;
private CommandQueue mCommandQueue;
private FakeMetricsLogger mMetricsLogger;
private ShadeController mShadeController = mock(ShadeController.class);
@@ -95,11 +101,11 @@
mDependency.injectMockDependency(NotificationViewHierarchyManager.class);
mDependency.injectMockDependency(NotificationRemoteInputManager.Callback.class);
mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
- mDependency.injectMockDependency(NotificationInterruptionStateProvider.class);
mDependency.injectMockDependency(NotificationMediaManager.class);
mDependency.injectMockDependency(VisualStabilityManager.class);
mDependency.injectMockDependency(NotificationGutsManager.class);
mDependency.injectMockDependency(NotificationShadeWindowController.class);
+ mDependency.injectMockDependency(ForegroundServiceNotificationListener.class);
NotificationEntryManager entryManager =
mDependency.injectMockDependency(NotificationEntryManager.class);
when(entryManager.getActiveNotificationsForCurrentUser()).thenReturn(new ArrayList<>());
@@ -107,18 +113,25 @@
NotificationShadeWindowView notificationShadeWindowView =
mock(NotificationShadeWindowView.class);
when(notificationShadeWindowView.getResources()).thenReturn(mContext.getResources());
+
mStatusBarNotificationPresenter = new StatusBarNotificationPresenter(mContext,
mock(NotificationPanelViewController.class), mock(HeadsUpManagerPhone.class),
notificationShadeWindowView, mock(NotificationListContainerViewGroup.class),
mock(DozeScrimController.class), mock(ScrimController.class),
mock(ActivityLaunchAnimator.class), mock(DynamicPrivacyController.class),
- mock(NotificationAlertingManager.class), mock(KeyguardStateController.class),
+ mock(KeyguardStateController.class),
mock(KeyguardIndicationController.class), mStatusBar,
- mock(ShadeControllerImpl.class), mCommandQueue, mInitController);
+ mock(ShadeControllerImpl.class), mCommandQueue, mInitController,
+ mNotificationInterruptStateProvider);
+ mInitController.executePostInitTasks();
+ ArgumentCaptor<NotificationInterruptSuppressor> suppressorCaptor =
+ ArgumentCaptor.forClass(NotificationInterruptSuppressor.class);
+ verify(mNotificationInterruptStateProvider).addSuppressor(suppressorCaptor.capture());
+ mInterruptSuppressor = suppressorCaptor.getValue();
}
@Test
- public void testHeadsUp_disabledStatusBar() {
+ public void testSuppressHeadsUp_disabledStatusBar() {
Notification n = new Notification.Builder(getContext(), "a").build();
NotificationEntry entry = new NotificationEntryBuilder()
.setPkg("a")
@@ -130,12 +143,12 @@
false /* animate */);
TestableLooper.get(this).processAllMessages();
- assertFalse("The panel shouldn't allow heads up while disabled",
- mStatusBarNotificationPresenter.canHeadsUp(entry, entry.getSbn()));
+ assertTrue("The panel should suppress heads up while disabled",
+ mInterruptSuppressor.suppressAwakeHeadsUp(entry));
}
@Test
- public void testHeadsUp_disabledNotificationShade() {
+ public void testSuppressHeadsUp_disabledNotificationShade() {
Notification n = new Notification.Builder(getContext(), "a").build();
NotificationEntry entry = new NotificationEntryBuilder()
.setPkg("a")
@@ -147,8 +160,39 @@
false /* animate */);
TestableLooper.get(this).processAllMessages();
- assertFalse("The panel shouldn't allow heads up while notitifcation shade disabled",
- mStatusBarNotificationPresenter.canHeadsUp(entry, entry.getSbn()));
+ assertTrue("The panel should suppress interruptions while notification shade "
+ + "disabled",
+ mInterruptSuppressor.suppressAwakeHeadsUp(entry));
+ }
+
+ @Test
+ public void testSuppressInterruptions_vrMode() {
+ Notification n = new Notification.Builder(getContext(), "a").build();
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setPkg("a")
+ .setOpPkg("a")
+ .setTag("a")
+ .setNotification(n)
+ .build();
+ mStatusBarNotificationPresenter.mVrMode = true;
+
+ assertTrue("Vr mode should suppress interruptions",
+ mInterruptSuppressor.suppressAwakeInterruptions(entry));
+ }
+
+ @Test
+ public void testSuppressInterruptions_statusBarAlertsDisabled() {
+ Notification n = new Notification.Builder(getContext(), "a").build();
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setPkg("a")
+ .setOpPkg("a")
+ .setTag("a")
+ .setNotification(n)
+ .build();
+ when(mStatusBar.areNotificationAlertsDisabled()).thenReturn(true);
+
+ assertTrue("StatusBar alerts disabled shouldn't allow interruptions",
+ mInterruptSuppressor.suppressInterruptions(entry));
}
@Test
@@ -172,4 +216,3 @@
}
}
}
-
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 0d7734e..d9f4d4b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -42,7 +42,7 @@
import android.app.StatusBarManager;
import android.app.trust.TrustManager;
import android.content.BroadcastReceiver;
-import android.content.Context;
+import android.content.ContentResolver;
import android.content.IntentFilter;
import android.hardware.display.AmbientDisplayConfiguration;
import android.hardware.fingerprint.FingerprintManager;
@@ -109,18 +109,17 @@
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationAlertingManager;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationFilter;
-import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.init.NotificationsController;
+import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
@@ -129,6 +128,7 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.ExtensionController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
@@ -160,7 +160,7 @@
private StatusBar mStatusBar;
private FakeMetricsLogger mMetricsLogger;
private PowerManager mPowerManager;
- private TestableNotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
+ private TestableNotificationInterruptStateProviderImpl mNotificationInterruptStateProvider;
@Mock private NotificationsController mNotificationsController;
@Mock private LightBarController mLightBarController;
@@ -178,7 +178,6 @@
@Mock private DozeScrimController mDozeScrimController;
@Mock private Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
@Mock private BiometricUnlockController mBiometricUnlockController;
- @Mock private NotificationInterruptionStateProvider.HeadsUpSuppressor mHeadsUpSuppressor;
@Mock private VisualStabilityManager mVisualStabilityManager;
@Mock private NotificationListener mNotificationListener;
@Mock private KeyguardViewMediator mKeyguardViewMediator;
@@ -191,10 +190,9 @@
@Mock private StatusBarNotificationPresenter mNotificationPresenter;
@Mock private NotificationEntryListener mEntryListener;
@Mock private NotificationFilter mNotificationFilter;
- @Mock private NotificationAlertingManager mNotificationAlertingManager;
+ @Mock private AmbientDisplayConfiguration mAmbientDisplayConfiguration;
@Mock private NotificationLogger.ExpansionStateLogger mExpansionStateLogger;
@Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- @Mock private AmbientDisplayConfiguration mAmbientDisplayConfiguration;
@Mock private NotificationShadeWindowView mNotificationShadeWindowView;
@Mock private BroadcastDispatcher mBroadcastDispatcher;
@Mock private AssistManager mAssistManager;
@@ -262,10 +260,12 @@
mPowerManager = new PowerManager(mContext, powerManagerService, thermalService,
Handler.createAsync(Looper.myLooper()));
- mNotificationInterruptionStateProvider =
- new TestableNotificationInterruptionStateProvider(mContext, mPowerManager,
+ mNotificationInterruptStateProvider =
+ new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
+ mPowerManager,
mDreamManager, mAmbientDisplayConfiguration, mNotificationFilter,
- mStatusBarStateController, mBatteryController);
+ mStatusBarStateController, mBatteryController, mHeadsUpManager,
+ new Handler(TestableLooper.get(this).getLooper()));
mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class));
mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class));
@@ -298,9 +298,6 @@
return null;
}).when(mStatusBarKeyguardViewManager).addAfterKeyguardGoneRunnable(any());
- mNotificationInterruptionStateProvider.setUpWithPresenter(mNotificationPresenter,
- mHeadsUpManager, mHeadsUpSuppressor);
-
when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
WakefulnessLifecycle wakefulnessLifecycle = new WakefulnessLifecycle();
@@ -347,10 +344,9 @@
),
mNotificationGutsManager,
notificationLogger,
- mNotificationInterruptionStateProvider,
+ mNotificationInterruptStateProvider,
mNotificationViewHierarchyManager,
mKeyguardViewMediator,
- mNotificationAlertingManager,
new DisplayMetrics(),
mMetricsLogger,
mUiBgExecutor,
@@ -561,7 +557,6 @@
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false);
when(mDreamManager.isDreaming()).thenReturn(false);
- when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
Notification n = new Notification.Builder(getContext(), "a")
.setGroup("a")
@@ -577,7 +572,7 @@
.setImportance(IMPORTANCE_HIGH)
.build();
- assertTrue(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
+ assertTrue(mNotificationInterruptStateProvider.shouldHeadsUp(entry));
}
@Test
@@ -586,7 +581,6 @@
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false);
when(mDreamManager.isDreaming()).thenReturn(false);
- when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
Notification n = new Notification.Builder(getContext(), "a")
.setGroup("a")
@@ -602,7 +596,7 @@
.setImportance(IMPORTANCE_HIGH)
.build();
- assertFalse(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
+ assertFalse(mNotificationInterruptStateProvider.shouldHeadsUp(entry));
}
@Test
@@ -611,7 +605,6 @@
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false);
when(mDreamManager.isDreaming()).thenReturn(false);
- when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
Notification n = new Notification.Builder(getContext(), "a").build();
@@ -624,7 +617,7 @@
.setSuppressedVisualEffects(SUPPRESSED_EFFECT_PEEK)
.build();
- assertFalse(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
+ assertFalse(mNotificationInterruptStateProvider.shouldHeadsUp(entry));
}
@Test
@@ -633,7 +626,6 @@
when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
when(mNotificationFilter.shouldFilterOut(any())).thenReturn(false);
when(mDreamManager.isDreaming()).thenReturn(false);
- when(mHeadsUpSuppressor.canHeadsUp(any(), any())).thenReturn(true);
Notification n = new Notification.Builder(getContext(), "a").build();
@@ -645,7 +637,7 @@
.setImportance(IMPORTANCE_HIGH)
.build();
- assertTrue(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
+ assertTrue(mNotificationInterruptStateProvider.shouldHeadsUp(entry));
}
@Test
@@ -871,19 +863,21 @@
verify(mDozeServiceHost).setDozeSuppressed(false);
}
- public static class TestableNotificationInterruptionStateProvider extends
- NotificationInterruptionStateProvider {
+ public static class TestableNotificationInterruptStateProviderImpl extends
+ NotificationInterruptStateProviderImpl {
- TestableNotificationInterruptionStateProvider(
- Context context,
+ TestableNotificationInterruptStateProviderImpl(
+ ContentResolver contentResolver,
PowerManager powerManager,
IDreamManager dreamManager,
AmbientDisplayConfiguration ambientDisplayConfiguration,
NotificationFilter filter,
StatusBarStateController controller,
- BatteryController batteryController) {
- super(context, powerManager, dreamManager, ambientDisplayConfiguration, filter,
- batteryController, controller);
+ BatteryController batteryController,
+ HeadsUpManager headsUpManager,
+ Handler mainHandler) {
+ super(contentResolver, powerManager, dreamManager, ambientDisplayConfiguration, filter,
+ batteryController, controller, headsUpManager, mainHandler);
mUseHeadsUp = true;
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java
index 37b315f..526fba7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java
@@ -104,6 +104,30 @@
}
@Test
+ public void testDuplicateListener() {
+ TestableListener listenerA = new TestableListener();
+
+ assertFalse(mProximitySensor.isRegistered());
+
+ mProximitySensor.register(listenerA);
+ waitForSensorManager();
+ assertTrue(mProximitySensor.isRegistered());
+ mProximitySensor.register(listenerA);
+ waitForSensorManager();
+ assertTrue(mProximitySensor.isRegistered());
+ assertNull(listenerA.mLastEvent);
+
+ mFakeProximitySensor.sendProximityResult(true);
+ assertFalse(listenerA.mLastEvent.getNear());
+ assertEquals(listenerA.mCallCount, 1);
+ mFakeProximitySensor.sendProximityResult(false);
+ assertTrue(listenerA.mLastEvent.getNear());
+ assertEquals(listenerA.mCallCount, 2);
+
+ mProximitySensor.unregister(listenerA);
+ waitForSensorManager();
+ }
+ @Test
public void testUnregister() {
TestableListener listener = new TestableListener();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/EventsTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/EventsTest.java
index 701b2fa..a853f1d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/EventsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/EventsTest.java
@@ -102,9 +102,9 @@
assertEquals(mExpectedMetrics[1], logs.remove().getCategory());
}
}
- Queue<UiEventLoggerFake.FakeUiEvent> events = mUiEventLogger.getLogs();
if (mUiEvent != null) {
- assertEquals(mUiEvent.getId(), events.remove().eventId);
+ assertEquals(1, mUiEventLogger.numLogs());
+ assertEquals(mUiEvent.getId(), mUiEventLogger.eventId(0));
}
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index e484ca0..968528c 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -52,7 +52,6 @@
import android.net.IpPrefix;
import android.net.IpSecManager;
import android.net.IpSecManager.IpSecTunnelInterface;
-import android.net.IpSecManager.UdpEncapsulationSocket;
import android.net.IpSecTransform;
import android.net.LinkAddress;
import android.net.LinkProperties;
@@ -2201,7 +2200,6 @@
/** Signal to ensure shutdown is honored even if a new Network is connected. */
private boolean mIsRunning = true;
- @Nullable private UdpEncapsulationSocket mEncapSocket;
@Nullable private IpSecTunnelInterface mTunnelIface;
@Nullable private IkeSession mSession;
@Nullable private Network mActiveNetwork;
@@ -2352,29 +2350,21 @@
resetIkeState();
mActiveNetwork = network;
- // TODO(b/149356682): Update this based on new IKE API
- mEncapSocket = mIpSecManager.openUdpEncapsulationSocket();
-
- // TODO(b/149356682): Update this based on new IKE API
final IkeSessionParams ikeSessionParams =
- VpnIkev2Utils.buildIkeSessionParams(mProfile, mEncapSocket);
+ VpnIkev2Utils.buildIkeSessionParams(mContext, mProfile, network);
final ChildSessionParams childSessionParams =
VpnIkev2Utils.buildChildSessionParams();
// TODO: Remove the need for adding two unused addresses with
// IPsec tunnels.
+ final InetAddress address = InetAddress.getLocalHost();
mTunnelIface =
mIpSecManager.createIpSecTunnelInterface(
- ikeSessionParams.getServerAddress() /* unused */,
- ikeSessionParams.getServerAddress() /* unused */,
+ address /* unused */,
+ address /* unused */,
network);
mNetd.setInterfaceUp(mTunnelIface.getInterfaceName());
- // Socket must be bound to prevent network switches from causing
- // the IKE teardown to fail/timeout.
- // TODO(b/149356682): Update this based on new IKE API
- network.bindSocket(mEncapSocket.getFileDescriptor());
-
mSession = mIkev2SessionCreator.createIkeSession(
mContext,
ikeSessionParams,
@@ -2459,16 +2449,6 @@
mSession.kill(); // Kill here to make sure all resources are released immediately
mSession = null;
}
-
- // TODO(b/149356682): Update this based on new IKE API
- if (mEncapSocket != null) {
- try {
- mEncapSocket.close();
- } catch (IOException e) {
- Log.e(TAG, "Failed to close encap socket", e);
- }
- mEncapSocket = null;
- }
}
/**
diff --git a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
index 33fc32b..3da304c 100644
--- a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
+++ b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
@@ -35,10 +35,10 @@
import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1;
import android.annotation.NonNull;
+import android.content.Context;
import android.net.Ikev2VpnProfile;
import android.net.InetAddresses;
import android.net.IpPrefix;
-import android.net.IpSecManager.UdpEncapsulationSocket;
import android.net.IpSecTransform;
import android.net.Network;
import android.net.RouteInfo;
@@ -84,18 +84,14 @@
*/
public class VpnIkev2Utils {
static IkeSessionParams buildIkeSessionParams(
- @NonNull Ikev2VpnProfile profile, @NonNull UdpEncapsulationSocket socket) {
- // TODO(b/149356682): Update this based on new IKE API. Only numeric addresses supported
- // until then. All others throw IAE (caught by caller).
- final InetAddress serverAddr = InetAddresses.parseNumericAddress(profile.getServerAddr());
+ @NonNull Context context, @NonNull Ikev2VpnProfile profile, @NonNull Network network) {
final IkeIdentification localId = parseIkeIdentification(profile.getUserIdentity());
final IkeIdentification remoteId = parseIkeIdentification(profile.getServerAddr());
- // TODO(b/149356682): Update this based on new IKE API.
final IkeSessionParams.Builder ikeOptionsBuilder =
- new IkeSessionParams.Builder()
- .setServerAddress(serverAddr)
- .setUdpEncapsulationSocket(socket)
+ new IkeSessionParams.Builder(context)
+ .setServerHostname(profile.getServerAddr())
+ .setNetwork(network)
.setLocalIdentification(localId)
.setRemoteIdentification(remoteId);
setIkeAuth(profile, ikeOptionsBuilder);
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryManager.java b/services/core/java/com/android/server/notification/NotificationHistoryManager.java
index 7d68012..f7fb9b7 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryManager.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryManager.java
@@ -358,7 +358,9 @@
false, this, UserHandle.USER_ALL);
synchronized (mLock) {
for (UserInfo userInfo : mUserManager.getUsers()) {
- update(null, userInfo.id);
+ if (!userInfo.isProfile()) {
+ update(null, userInfo.id);
+ }
}
}
}
@@ -379,7 +381,10 @@
boolean historyEnabled = Settings.Secure.getIntForUser(resolver,
Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0, userId)
!= 0;
- onHistoryEnabledChanged(userId, historyEnabled);
+ int[] profiles = mUserManager.getProfileIds(userId, true);
+ for (int profileId : profiles) {
+ onHistoryEnabledChanged(profileId, historyEnabled);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index e7553b6..69a5b35 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2684,6 +2684,7 @@
mHistoryManager.addNotification(new HistoricalNotification.Builder()
.setPackage(r.getSbn().getPackageName())
.setUid(r.getSbn().getUid())
+ .setUserId(r.getUserId())
.setChannelId(r.getChannel().getId())
.setChannelName(r.getChannel().getName().toString())
.setPostedTimeMs(System.currentTimeMillis())
diff --git a/services/core/java/com/android/server/om/OverlayActorEnforcer.java b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
index 40efb7c..9197956 100644
--- a/services/core/java/com/android/server/om/OverlayActorEnforcer.java
+++ b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
@@ -29,7 +29,6 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
-import com.android.server.SystemConfig;
import java.io.IOException;
import java.util.List;
@@ -38,6 +37,8 @@
/**
* Performs verification that a calling UID can act on a target package's overlayable.
*
+ * Actors requirements are specified in {@link android.content.om.OverlayManager}.
+ *
* @hide
*/
public class OverlayActorEnforcer {
@@ -99,13 +100,7 @@
}
/**
- * An actor is valid if any of the following is true:
- * - is {@link Process#ROOT_UID}, {@link Process#SYSTEM_UID}
- * - is the target overlay package
- * - has the CHANGE_OVERLAY_PACKAGES permission and an actor is not defined
- * - is the same the as the package defined in {@link SystemConfig#getNamedActors()} for a given
- * namespace and actor name
- *
+ * See {@link OverlayActorEnforcer} class comment for actor requirements.
* @return true if the actor is allowed to act on the target overlayInfo
*/
private ActorState isAllowedActor(String methodName, OverlayInfo overlayInfo,
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 2bd1a26..a83a847 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -321,6 +321,8 @@
Set<String> mimeGroupNames = other.mimeGroups != null ? other.mimeGroups.keySet() : null;
updateMimeGroups(mimeGroupNames);
+
+ getPkgState().updateFrom(other.getPkgState());
}
@NonNull
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java b/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
index e27bf48..edb6d65 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
@@ -27,6 +27,7 @@
import com.android.internal.util.DataClass;
import com.android.server.pm.PackageSetting;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -88,6 +89,22 @@
return latestUse;
}
+ public void updateFrom(PackageStateUnserialized other) {
+ this.hiddenUntilInstalled = other.hiddenUntilInstalled;
+
+ if (!other.usesLibraryInfos.isEmpty()) {
+ this.usesLibraryInfos = new ArrayList<>(other.usesLibraryInfos);
+ }
+
+ if (!other.usesLibraryFiles.isEmpty()) {
+ this.usesLibraryFiles = new ArrayList<>(other.usesLibraryFiles);
+ }
+
+ this.updatedSystemApp = other.updatedSystemApp;
+ this.lastPackageUsageTimeInMills = other.lastPackageUsageTimeInMills;
+ this.overrideSeInfo = other.overrideSeInfo;
+ }
+
// Code below generated by codegen v1.0.14.
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index bc12923..420675c 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -1469,11 +1469,11 @@
if (resumeNext) {
final ActivityStack topStack = mRootWindowContainer.getTopDisplayFocusedStack();
- if (!topStack.shouldSleepOrShutDownActivities()) {
+ if (topStack != null && !topStack.shouldSleepOrShutDownActivities()) {
mRootWindowContainer.resumeFocusedStacksTopActivities(topStack, prev, null);
} else {
checkReadyForSleep();
- ActivityRecord top = topStack.topRunningActivity();
+ final ActivityRecord top = topStack != null ? topStack.topRunningActivity() : null;
if (top == null || (prev != null && top != prev)) {
// If there are no more activities available to run, do resume anyway to start
// something. Also if the top activity on the stack is not the just paused
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 3210304..984ae21 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -615,12 +615,15 @@
int res;
synchronized (mService.mGlobalLock) {
- final ActivityStack stack = mRootWindowContainer.getTopDisplayFocusedStack();
- stack.mConfigWillChange = mRequest.globalConfig != null
+ final boolean globalConfigWillChange = mRequest.globalConfig != null
&& mService.getGlobalConfiguration().diff(mRequest.globalConfig) != 0;
+ final ActivityStack stack = mRootWindowContainer.getTopDisplayFocusedStack();
+ if (stack != null) {
+ stack.mConfigWillChange = globalConfigWillChange;
+ }
if (DEBUG_CONFIGURATION) {
Slog.v(TAG_CONFIGURATION, "Starting activity when config will change = "
- + stack.mConfigWillChange);
+ + globalConfigWillChange);
}
final long origId = Binder.clearCallingIdentity();
@@ -633,7 +636,7 @@
Binder.restoreCallingIdentity(origId);
- if (stack.mConfigWillChange) {
+ if (globalConfigWillChange) {
// If the caller also wants to switch to a new configuration, do so now.
// This allows a clean switch, as we are waiting for the current activity
// to pause (so we will not destroy it), and have not yet started the
@@ -641,7 +644,9 @@
mService.mAmInternal.enforceCallingPermission(
android.Manifest.permission.CHANGE_CONFIGURATION,
"updateConfiguration()");
- stack.mConfigWillChange = false;
+ if (stack != null) {
+ stack.mConfigWillChange = false;
+ }
if (DEBUG_CONFIGURATION) {
Slog.v(TAG_CONFIGURATION,
"Updating to new configuration after starting activity.");
@@ -1536,9 +1541,11 @@
// If the activity being launched is the same as the one currently at the top, then
// we need to check if it should only be launched once.
final ActivityStack topStack = mRootWindowContainer.getTopDisplayFocusedStack();
- startResult = deliverToCurrentTopIfNeeded(topStack);
- if (startResult != START_SUCCESS) {
- return startResult;
+ if (topStack != null) {
+ startResult = deliverToCurrentTopIfNeeded(topStack);
+ if (startResult != START_SUCCESS) {
+ return startResult;
+ }
}
if (mTargetStack == null) {
@@ -2126,10 +2133,13 @@
if ((startFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
ActivityRecord checkedCaller = sourceRecord;
if (checkedCaller == null) {
- checkedCaller = mRootWindowContainer.getTopDisplayFocusedStack()
- .topRunningNonDelayedActivityLocked(mNotTop);
+ ActivityStack topFocusedStack = mRootWindowContainer.getTopDisplayFocusedStack();
+ if (topFocusedStack != null) {
+ checkedCaller = topFocusedStack.topRunningNonDelayedActivityLocked(mNotTop);
+ }
}
- if (!checkedCaller.mActivityComponent.equals(r.mActivityComponent)) {
+ if (checkedCaller == null
+ || !checkedCaller.mActivityComponent.equals(r.mActivityComponent)) {
// Caller is not the same as launcher, so always needed.
mStartFlags &= ~START_FLAG_ONLY_IF_NEEDED;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 98c8b61..911812b 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1104,8 +1104,8 @@
// If this is coming from the currently resumed activity, it is
// effectively saying that app switches are allowed at this point.
final ActivityStack stack = getTopDisplayFocusedStack();
- if (stack.mResumedActivity != null &&
- stack.mResumedActivity.info.applicationInfo.uid == Binder.getCallingUid()) {
+ if (stack != null && stack.mResumedActivity != null
+ && stack.mResumedActivity.info.applicationInfo.uid == Binder.getCallingUid()) {
mAppSwitchesAllowedTime = 0;
}
}
@@ -1951,8 +1951,13 @@
public boolean isTopActivityImmersive() {
enforceNotIsolatedCaller("isTopActivityImmersive");
synchronized (mGlobalLock) {
- final ActivityRecord r = getTopDisplayFocusedStack().topRunningActivity();
- return (r != null) ? r.immersive : false;
+ final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
+ if (topFocusedStack == null) {
+ return false;
+ }
+
+ final ActivityRecord r = topFocusedStack.topRunningActivity();
+ return r != null && r.immersive;
}
}
@@ -1981,7 +1986,8 @@
public int getFrontActivityScreenCompatMode() {
enforceNotIsolatedCaller("getFrontActivityScreenCompatMode");
synchronized (mGlobalLock) {
- final ActivityRecord r = getTopDisplayFocusedStack().topRunningActivity();
+ final ActivityStack stack = getTopDisplayFocusedStack();
+ final ActivityRecord r = stack != null ? stack.topRunningActivity() : null;
if (r == null) {
return ActivityManager.COMPAT_MODE_UNKNOWN;
}
@@ -1995,7 +2001,8 @@
"setFrontActivityScreenCompatMode");
ApplicationInfo ai;
synchronized (mGlobalLock) {
- final ActivityRecord r = getTopDisplayFocusedStack().topRunningActivity();
+ final ActivityStack stack = getTopDisplayFocusedStack();
+ final ActivityRecord r = stack != null ? stack.topRunningActivity() : null;
if (r == null) {
Slog.w(TAG, "setFrontActivityScreenCompatMode failed: no top activity");
return;
@@ -2383,7 +2390,10 @@
synchronized (mGlobalLock) {
final long origId = Binder.clearCallingIdentity();
try {
- getTopDisplayFocusedStack().unhandledBackLocked();
+ final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
+ if (topFocusedStack != null) {
+ topFocusedStack.unhandledBackLocked();
+ }
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -3616,7 +3626,8 @@
"enqueueAssistContext()");
synchronized (mGlobalLock) {
- ActivityRecord activity = getTopDisplayFocusedStack().getTopNonFinishingActivity();
+ final ActivityStack stack = getTopDisplayFocusedStack();
+ ActivityRecord activity = stack != null ? stack.getTopNonFinishingActivity() : null;
if (activity == null) {
Slog.w(TAG, "getAssistContextExtras failed: no top activity");
return null;
@@ -7037,9 +7048,9 @@
mRootWindowContainer.dumpDisplayConfigs(pw, " ");
}
if (dumpAll) {
- if (dumpPackage == null) {
- pw.println(" mConfigWillChange: "
- + getTopDisplayFocusedStack().mConfigWillChange);
+ final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
+ if (dumpPackage == null && topFocusedStack != null) {
+ pw.println(" mConfigWillChange: " + topFocusedStack.mConfigWillChange);
}
if (mCompatModePackages.getPackages().size() > 0) {
boolean printed = false;
@@ -7120,7 +7131,10 @@
synchronized (mGlobalLock) {
if (dumpPackage == null) {
getGlobalConfiguration().dumpDebug(proto, GLOBAL_CONFIGURATION);
- proto.write(CONFIG_WILL_CHANGE, getTopDisplayFocusedStack().mConfigWillChange);
+ final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
+ if (topFocusedStack != null) {
+ proto.write(CONFIG_WILL_CHANGE, topFocusedStack.mConfigWillChange);
+ }
writeSleepStateToProto(proto, wakeFullness, testPssMode);
if (mRunningVoice != null) {
final long vrToken = proto.start(
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 00f892f..d5a0d05 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -42,7 +42,10 @@
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
+import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
@@ -3269,11 +3272,19 @@
mService.getStackBounds(inSplitScreen ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
: WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_UNDEFINED, mNonDockedStackBounds);
- final Pair<Integer, Boolean> result =
+ final Pair<Integer, WindowState> result =
updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility);
final int visibility = result.first;
- final int appearance = win.mAttrs.insetsFlags.appearance
- | InsetsFlags.getAppearance(visibility);
+ final WindowState navColorWin = result.second;
+ final boolean isNavbarColorManagedByIme =
+ navColorWin != null && navColorWin == mDisplayContent.mInputMethodWindow;
+ final int opaqueAppearance = InsetsFlags.getAppearance(visibility)
+ & (APPEARANCE_OPAQUE_STATUS_BARS | APPEARANCE_OPAQUE_NAVIGATION_BARS);
+ final int appearance = ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL
+ ? updateLightNavigationBarAppearanceLw(win.mAttrs.insetsFlags.appearance,
+ mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState,
+ mDisplayContent.mInputMethodWindow, navColorWin) | opaqueAppearance
+ : InsetsFlags.getAppearance(visibility);
final int diff = visibility ^ mLastSystemUiFlags;
final InsetsPolicy insetsPolicy = getInsetsPolicy();
final boolean isFullscreen = (visibility & (View.SYSTEM_UI_FLAG_FULLSCREEN
@@ -3320,7 +3331,6 @@
new AppearanceRegion(dockedAppearance, dockedStackBounds)}
: new AppearanceRegion[]{
new AppearanceRegion(fullscreenAppearance, fullscreenStackBounds)};
- final boolean isNavbarColorManagedByIme = result.second;
String cause = win.toString();
mHandler.post(() -> {
StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
@@ -3448,7 +3458,25 @@
return vis;
}
- private Pair<Integer, Boolean> updateSystemBarsLw(WindowState win, int oldVis, int vis) {
+ @VisibleForTesting
+ static int updateLightNavigationBarAppearanceLw(int appearance, WindowState opaque,
+ WindowState opaqueOrDimming, WindowState imeWindow, WindowState navColorWin) {
+
+ if (navColorWin != null) {
+ if (navColorWin == imeWindow || navColorWin == opaque) {
+ // Respect the light flag.
+ appearance &= ~APPEARANCE_LIGHT_NAVIGATION_BARS;
+ appearance |= navColorWin.mAttrs.insetsFlags.appearance
+ & APPEARANCE_LIGHT_NAVIGATION_BARS;
+ } else if (navColorWin == opaqueOrDimming && navColorWin.isDimming()) {
+ // Clear the light flag for dimming window.
+ appearance &= ~APPEARANCE_LIGHT_NAVIGATION_BARS;
+ }
+ }
+ return appearance;
+ }
+
+ private Pair<Integer, WindowState> updateSystemBarsLw(WindowState win, int oldVis, int vis) {
final boolean dockedStackVisible =
mDisplayContent.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
final boolean freeformStackVisible =
@@ -3587,11 +3615,8 @@
vis = updateLightNavigationBarLw(vis, mTopFullscreenOpaqueWindowState,
mTopFullscreenOpaqueOrDimmingWindowState,
mDisplayContent.mInputMethodWindow, navColorWin);
- // Navbar color is controlled by the IME.
- final boolean isManagedByIme =
- navColorWin != null && navColorWin == mDisplayContent.mInputMethodWindow;
- return Pair.create(vis, isManagedByIme);
+ return Pair.create(vis, navColorWin);
}
private boolean drawsBarBackground(int vis, WindowState win, BarController controller,
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index ada5685..74c3044 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1980,7 +1980,9 @@
}
boolean switchUser(int userId, UserState uss) {
- final int focusStackId = getTopDisplayFocusedStack().getRootTaskId();
+ final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
+ final int focusStackId = topFocusedStack != null
+ ? topFocusedStack.getRootTaskId() : INVALID_TASK_ID;
// We dismiss the docked stack whenever we switch users.
final ActivityStack dockedStack = getDefaultDisplay().getRootSplitScreenPrimaryTask();
if (dockedStack != null) {
@@ -3455,7 +3457,12 @@
ArrayList<ActivityRecord> getDumpActivities(String name, boolean dumpVisibleStacksOnly,
boolean dumpFocusedStackOnly) {
if (dumpFocusedStackOnly) {
- return getTopDisplayFocusedStack().getDumpActivitiesLocked(name);
+ final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
+ if (topFocusedStack != null) {
+ return topFocusedStack.getDumpActivitiesLocked(name);
+ } else {
+ return new ArrayList<>();
+ }
} else {
ArrayList<ActivityRecord> activities = new ArrayList<>();
int numDisplays = getChildCount();
diff --git a/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreConfigTest.java b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreConfigTest.java
new file mode 100644
index 0000000..ad19a48
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreConfigTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.blob;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static junit.framework.Assert.fail;
+
+import android.content.Context;
+import android.os.Environment;
+import android.platform.test.annotations.Presubmit;
+import android.provider.DeviceConfig;
+import android.util.DataUnit;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.testables.TestableDeviceConfig;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class BlobStoreConfigTest {
+ private static final long TIMEOUT_UPDATE_PROPERTIES_MS = 1_000;
+
+ @Rule
+ public TestableDeviceConfig.TestableDeviceConfigRule
+ mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule();
+
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ BlobStoreConfig.initialize(mContext);
+ }
+
+ @Test
+ public void testGetAppDataBytesLimit() throws Exception {
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_BLOBSTORE,
+ BlobStoreConfig.DeviceConfigProperties.KEY_TOTAL_BYTES_PER_APP_LIMIT_FLOOR,
+ String.valueOf(DataUnit.MEBIBYTES.toBytes(1000)),
+ false /* makeDefault */);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_BLOBSTORE,
+ BlobStoreConfig.DeviceConfigProperties.KEY_TOTAL_BYTES_PER_APP_LIMIT_FRACTION,
+ String.valueOf(0.002f),
+ false /* makeDefault */);
+ waitForListenerToHandle();
+ assertThat(BlobStoreConfig.getAppDataBytesLimit()).isEqualTo(
+ DataUnit.MEBIBYTES.toBytes(1000));
+
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_BLOBSTORE,
+ BlobStoreConfig.DeviceConfigProperties.KEY_TOTAL_BYTES_PER_APP_LIMIT_FLOOR,
+ String.valueOf(DataUnit.MEBIBYTES.toBytes(100)),
+ false /* makeDefault */);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_BLOBSTORE,
+ BlobStoreConfig.DeviceConfigProperties.KEY_TOTAL_BYTES_PER_APP_LIMIT_FRACTION,
+ String.valueOf(0.1f),
+ false /* makeDefault */);
+ waitForListenerToHandle();
+ final long expectedLimit = (long) (Environment.getDataDirectory().getTotalSpace() * 0.1f);
+ assertThat(BlobStoreConfig.getAppDataBytesLimit()).isEqualTo(expectedLimit);
+ }
+
+ private void waitForListenerToHandle() throws Exception {
+ final CountDownLatch latch = new CountDownLatch(1);
+ mContext.getMainExecutor().execute(latch::countDown);
+ if (!latch.await(TIMEOUT_UPDATE_PROPERTIES_MS, TimeUnit.MILLISECONDS)) {
+ fail("Timed out waiting for properties to get updated");
+ }
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
index 6e0df3e..cd39144 100644
--- a/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
@@ -303,6 +303,37 @@
assertThat(mService.getKnownIdsForTest()).containsExactly(blobId1, blobId2, blobId3);
}
+ @Test
+ public void testGetTotalUsageBytes() throws Exception {
+ // Setup blobs
+ final BlobMetadata blobMetadata1 = mock(BlobMetadata.class);
+ final long size1 = 4567;
+ doReturn(size1).when(blobMetadata1).getSize();
+ doReturn(true).when(blobMetadata1).isALeasee(TEST_PKG1, TEST_UID1);
+ doReturn(true).when(blobMetadata1).isALeasee(TEST_PKG2, TEST_UID2);
+ mUserBlobs.put(mock(BlobHandle.class), blobMetadata1);
+
+ final BlobMetadata blobMetadata2 = mock(BlobMetadata.class);
+ final long size2 = 89475;
+ doReturn(size2).when(blobMetadata2).getSize();
+ doReturn(false).when(blobMetadata2).isALeasee(TEST_PKG1, TEST_UID1);
+ doReturn(true).when(blobMetadata2).isALeasee(TEST_PKG2, TEST_UID2);
+ mUserBlobs.put(mock(BlobHandle.class), blobMetadata2);
+
+ final BlobMetadata blobMetadata3 = mock(BlobMetadata.class);
+ final long size3 = 328732;
+ doReturn(size3).when(blobMetadata3).getSize();
+ doReturn(true).when(blobMetadata3).isALeasee(TEST_PKG1, TEST_UID1);
+ doReturn(false).when(blobMetadata3).isALeasee(TEST_PKG2, TEST_UID2);
+ mUserBlobs.put(mock(BlobHandle.class), blobMetadata3);
+
+ // Verify usage is calculated correctly
+ assertThat(mService.getTotalUsageBytesLocked(TEST_UID1, TEST_PKG1))
+ .isEqualTo(size1 + size3);
+ assertThat(mService.getTotalUsageBytesLocked(TEST_UID2, TEST_PKG2))
+ .isEqualTo(size1 + size2);
+ }
+
private BlobStoreSession createBlobStoreSessionMock(String ownerPackageName, int ownerUid,
long sessionId, File sessionFile) {
return createBlobStoreSessionMock(ownerPackageName, ownerUid, sessionId, sessionFile,
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java
index 32631be..64b24c1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java
+++ b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java
@@ -24,15 +24,18 @@
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;
@@ -109,6 +112,27 @@
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()));
}
/**
@@ -124,15 +148,25 @@
return namespace + "/" + name;
}
+ private Pair<String, String> getNameSpaceAndName(String key) {
+ final String[] values = key.split("/");
+ return Pair.create(values[0], values[1]);
+ }
+
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(Collections.singleton(name));
+ when(properties.getKeyset()).thenReturn(keyValues.keySet());
when(properties.getBoolean(anyString(), anyBoolean())).thenAnswer(
invocation -> {
String key = invocation.getArgument(0);
boolean defaultValue = invocation.getArgument(1);
- if (name.equalsIgnoreCase(key) && value != null) {
+ final String value = keyValues.get(key.toLowerCase());
+ if (value != null) {
return Boolean.parseBoolean(value);
} else {
return defaultValue;
@@ -143,7 +177,8 @@
invocation -> {
String key = invocation.getArgument(0);
float defaultValue = invocation.getArgument(1);
- if (name.equalsIgnoreCase(key) && value != null) {
+ final String value = keyValues.get(key.toLowerCase());
+ if (value != null) {
try {
return Float.parseFloat(value);
} catch (NumberFormatException e) {
@@ -158,7 +193,8 @@
invocation -> {
String key = invocation.getArgument(0);
int defaultValue = invocation.getArgument(1);
- if (name.equalsIgnoreCase(key) && value != null) {
+ final String value = keyValues.get(key.toLowerCase());
+ if (value != null) {
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
@@ -173,7 +209,8 @@
invocation -> {
String key = invocation.getArgument(0);
long defaultValue = invocation.getArgument(1);
- if (name.equalsIgnoreCase(key) && value != null) {
+ final String value = keyValues.get(key.toLowerCase());
+ if (value != null) {
try {
return Long.parseLong(value);
} catch (NumberFormatException e) {
@@ -184,11 +221,12 @@
}
}
);
- when(properties.getString(anyString(), anyString())).thenAnswer(
+ when(properties.getString(anyString(), nullable(String.class))).thenAnswer(
invocation -> {
String key = invocation.getArgument(0);
String defaultValue = invocation.getArgument(1);
- if (name.equalsIgnoreCase(key) && value != null) {
+ final String value = keyValues.get(key.toLowerCase());
+ if (value != null) {
return value;
} else {
return defaultValue;
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java
index d76c938..ba995e4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java
@@ -23,6 +23,7 @@
import android.app.ActivityThread;
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
+import android.provider.DeviceConfig.Properties;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -91,6 +92,39 @@
}
@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);
+ }
+
+ @Test
public void testListener() throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(1);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java
index f7c2609..2341c10 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java
@@ -31,7 +31,6 @@
import android.app.NotificationHistory.HistoricalNotification;
import android.content.pm.UserInfo;
import android.graphics.drawable.Icon;
-import android.os.Handler;
import android.os.UserManager;
import android.provider.Settings;
@@ -40,7 +39,6 @@
import com.android.server.UiServiceTestCase;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -58,9 +56,9 @@
UserManager mUserManager;
@Mock
NotificationHistoryDatabase mDb;
- @Mock
- Handler mHandler;
List<UserInfo> mUsers;
+ int[] mProfiles;
+ int mProfileId = 11;
NotificationHistoryManager mHistoryManager;
@@ -98,26 +96,32 @@
UserInfo userSystem = new UserInfo();
userSystem.id = USER_SYSTEM;
mUsers.add(userSystem);
- UserInfo userAll = new UserInfo();
- userAll.id = MIN_SECONDARY_USER_ID;
- mUsers.add(userAll);
- mUsers.add(userAll);
+ UserInfo userFullSecondary = new UserInfo();
+ userFullSecondary.id = MIN_SECONDARY_USER_ID;
+ mUsers.add(userFullSecondary);
+ UserInfo userProfile = new UserInfo();
+ userProfile.id = mProfileId;
+ mUsers.add(userProfile);
when(mUserManager.getUsers()).thenReturn(mUsers);
+ mProfiles = new int[] {userSystem.id, userProfile.id};
+ when(mUserManager.getProfileIds(userSystem.id, true)).thenReturn(mProfiles);
+ when(mUserManager.getProfileIds(userFullSecondary.id, true))
+ .thenReturn(new int[] {userFullSecondary.id});
+ when(mUserManager.getProfileIds(userProfile.id, true))
+ .thenReturn(new int[] {userProfile.id});
+
+ when(mUserManager.getProfileParent(userProfile.id)).thenReturn(userSystem);
+
+ NotificationHistoryDatabaseFactory.setTestingNotificationHistoryDatabase(mDb);
+
+ mHistoryManager = new NotificationHistoryManager(getContext(), null);
+
for (UserInfo info : mUsers) {
Settings.Secure.putIntForUser(getContext().getContentResolver(),
Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 1, info.id);
+ mHistoryManager.mSettingsObserver.update(null, info.id);
}
-
- NotificationHistoryDatabaseFactory.setTestingNotificationHistoryDatabase(mDb);
-
- mHistoryManager = new NotificationHistoryManager(getContext(), mHandler);
- mHistoryManager.onBootPhaseAppsCanStart();
- }
-
- @After
- public void tearDown() {
- mHistoryManager.onDestroy();
}
@Test
@@ -151,6 +155,31 @@
}
@Test
+ public void testOnUserUnlocked_historyDisabled_withProfile() {
+ // create a history
+ mHistoryManager.onUserUnlocked(USER_SYSTEM);
+ assertThat(mHistoryManager.doesHistoryExistForUser(USER_SYSTEM)).isTrue();
+ mHistoryManager.onUserUnlocked(mProfileId);
+ assertThat(mHistoryManager.doesHistoryExistForUser(mProfileId)).isTrue();
+ // lock user
+ mHistoryManager.onUserStopped(USER_SYSTEM);
+ mHistoryManager.onUserStopped(mProfileId);
+
+ // turn off history
+ Settings.Secure.putIntForUser(getContext().getContentResolver(),
+ Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0, USER_SYSTEM);
+ mHistoryManager.mSettingsObserver.update(null, USER_SYSTEM);
+
+ // unlock user, verify that history is disabled for self and profile
+ mHistoryManager.onUserUnlocked(USER_SYSTEM);
+ mHistoryManager.onUserUnlocked(mProfileId);
+
+ assertThat(mHistoryManager.doesHistoryExistForUser(USER_SYSTEM)).isFalse();
+ assertThat(mHistoryManager.doesHistoryExistForUser(mProfileId)).isFalse();
+ verify(mDb, times(2)).disableHistory();
+ }
+
+ @Test
public void testOnUserUnlocked_historyDisabledThenEnabled() {
// create a history
mHistoryManager.onUserUnlocked(USER_SYSTEM);
@@ -177,6 +206,37 @@
}
@Test
+ public void testOnUserUnlocked_historyDisabledThenEnabled_multiProfile() {
+ // create a history
+ mHistoryManager.onUserUnlocked(USER_SYSTEM);
+ assertThat(mHistoryManager.doesHistoryExistForUser(USER_SYSTEM)).isTrue();
+ mHistoryManager.onUserUnlocked(mProfileId);
+ assertThat(mHistoryManager.doesHistoryExistForUser(mProfileId)).isTrue();
+
+ // lock user
+ mHistoryManager.onUserStopped(USER_SYSTEM);
+ mHistoryManager.onUserStopped(mProfileId);
+
+ // turn off history
+ Settings.Secure.putIntForUser(getContext().getContentResolver(),
+ Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0, USER_SYSTEM);
+ mHistoryManager.mSettingsObserver.update(null, USER_SYSTEM);
+
+ // turn on history
+ Settings.Secure.putIntForUser(getContext().getContentResolver(),
+ Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 1, USER_SYSTEM);
+ mHistoryManager.mSettingsObserver.update(null, USER_SYSTEM);
+
+ // unlock user, verify that history is NOT disabled
+ mHistoryManager.onUserUnlocked(USER_SYSTEM);
+ mHistoryManager.onUserUnlocked(mProfileId);
+
+ assertThat(mHistoryManager.doesHistoryExistForUser(USER_SYSTEM)).isTrue();
+ assertThat(mHistoryManager.doesHistoryExistForUser(mProfileId)).isTrue();
+ verify(mDb, never()).disableHistory();
+ }
+
+ @Test
public void testOnUserUnlocked_cleansUpRemovedPackages() {
String pkg = "pkg";
mHistoryManager.onPackageRemoved(USER_SYSTEM, pkg);
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 b6cdbfb..64d481a 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -4394,6 +4394,7 @@
verify(mAppUsageStats, times(1)).reportInterruptiveNotification(
anyString(), anyString(), anyInt());
+ verify(mHistoryManager, times(1)).addNotification(any());
}
@Test
diff --git a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
index 61f3dba..4ecca2d 100644
--- a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
+++ b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
@@ -78,13 +78,12 @@
return false;
}
String res = mTestDevice.executeShellCommand("kill -s SIGUSR1 " + pid).trim();
- assertTrue("kill SIGUSR1: " + res, res.length() == 0);
- return true;
+ return res.length() == 0;
}
@Test
public void testSystemServerProfile() throws Exception {
- final int numIterations = 20;
+ final int numIterations = 30;
String res;
// Set properties and wait for them to be readable.
for (int i = 1; i <= numIterations; ++i) {