Merge "Remove use of TaskTile" into rvc-dev
diff --git a/api/system-current.txt b/api/system-current.txt
index a1fd0db..7e25382 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6061,7 +6061,7 @@
method public void release();
}
- public class InvalidPacketException extends java.lang.Exception {
+ public final class InvalidPacketException extends java.lang.Exception {
ctor public InvalidPacketException(int);
method public int getError();
field public static final int ERROR_INVALID_IP_ADDRESS = -21; // 0xffffffeb
@@ -6255,7 +6255,7 @@
field public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24; // 0x18
}
- public static class NetworkCapabilities.Builder {
+ public static final class NetworkCapabilities.Builder {
ctor public NetworkCapabilities.Builder();
ctor public NetworkCapabilities.Builder(@NonNull android.net.NetworkCapabilities);
method @NonNull public android.net.NetworkCapabilities.Builder addCapability(int);
diff --git a/api/test-current.txt b/api/test-current.txt
index 0bd8a19..ba6feed 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -535,6 +535,7 @@
method public void setWindowingMode(int);
method public void writeToParcel(android.os.Parcel, int);
field public static final int ACTIVITY_TYPE_ASSISTANT = 4; // 0x4
+ field public static final int ACTIVITY_TYPE_DREAM = 5; // 0x5
field public static final int ACTIVITY_TYPE_HOME = 2; // 0x2
field public static final int ACTIVITY_TYPE_RECENTS = 3; // 0x3
field public static final int ACTIVITY_TYPE_STANDARD = 1; // 0x1
@@ -1833,7 +1834,7 @@
field public static final int TRANSPORT_TEST = 7; // 0x7
}
- public static class NetworkCapabilities.Builder {
+ public static final class NetworkCapabilities.Builder {
ctor public NetworkCapabilities.Builder();
ctor public NetworkCapabilities.Builder(@NonNull android.net.NetworkCapabilities);
method @NonNull public android.net.NetworkCapabilities.Builder addCapability(int);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index bd3fee2d..21b56d3 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4974,10 +4974,8 @@
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason) {
ActivityClientRecord r = mActivities.get(token);
- Class<? extends Activity> activityClass = null;
if (localLOGV) Slog.v(TAG, "Performing finish of " + r);
if (r != null) {
- activityClass = r.activity.getClass();
r.activity.mConfigChangeFlags |= configChanges;
if (finishing) {
r.activity.mFinished = true;
@@ -5030,7 +5028,6 @@
synchronized (mResourcesManager) {
mActivities.remove(token);
}
- StrictMode.decrementExpectedActivityCount(activityClass);
return r;
}
@@ -5050,6 +5047,7 @@
ActivityClientRecord r = performDestroyActivity(token, finishing,
configChanges, getNonConfigInstance, reason);
if (r != null) {
+ Class<? extends Activity> activityClass = r.activity.getClass();
cleanUpPendingRemoveWindows(r, finishing);
WindowManager wm = r.activity.getWindowManager();
View v = r.activity.mDecor;
@@ -5074,14 +5072,14 @@
}
if (wtoken != null && r.mPendingRemoveWindow == null) {
WindowManagerGlobal.getInstance().closeAll(wtoken,
- r.activity.getClass().getName(), "Activity");
+ activityClass.getName(), "Activity");
} else if (r.mPendingRemoveWindow != null) {
// We're preserving only one window, others should be closed so app views
// will be detached before the final tear down. It should be done now because
// some components (e.g. WebView) rely on detach callbacks to perform receiver
// unregister and other cleanup.
WindowManagerGlobal.getInstance().closeAllExceptView(token, v,
- r.activity.getClass().getName(), "Activity");
+ activityClass.getName(), "Activity");
}
r.activity.mDecor = null;
}
@@ -5093,18 +5091,23 @@
// about leaking windows, because that is a bug, so if they are
// using this recreate facility then they get to live with leaks.
WindowManagerGlobal.getInstance().closeAll(token,
- r.activity.getClass().getName(), "Activity");
+ activityClass.getName(), "Activity");
}
// Mocked out contexts won't be participating in the normal
// process lifecycle, but if we're running with a proper
// ApplicationContext we need to have it tear down things
// cleanly.
- Context c = r.activity.getBaseContext();
- if (c instanceof ContextImpl) {
- ((ContextImpl) c).scheduleFinalCleanup(
- r.activity.getClass().getName(), "Activity");
+ final ContextImpl impl = ContextImpl.getImpl(r.activity);
+ if (impl != null) {
+ impl.scheduleFinalCleanup(activityClass.getName(), "Activity");
}
+
+ r.activity = null;
+ r.window = null;
+ r.hideForNow = false;
+ r.nextIdle = null;
+ StrictMode.decrementExpectedActivityCount(activityClass);
}
if (finishing) {
try {
@@ -5334,10 +5337,6 @@
handleDestroyActivity(r.token, false, configChanges, true, reason);
- r.activity = null;
- r.window = null;
- r.hideForNow = false;
- r.nextIdle = null;
// Merge any pending results and pending intents; don't just replace them
if (pendingResults != null) {
if (r.pendingResults == null) {
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index f590eb9..78d3581 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -48,8 +48,6 @@
void clearData(String pkg, int uid, boolean fromApp);
void enqueueTextToast(String pkg, IBinder token, CharSequence text, int duration, int displayId, @nullable ITransientNotificationCallback callback);
void enqueueToast(String pkg, IBinder token, ITransientNotification callback, int duration, int displayId);
- // TODO(b/144152069): Remove this after assessing impact on dogfood.
- void enqueueTextOrCustomToast(String pkg, IBinder token, ITransientNotification callback, int duration, int displayId, boolean isCustom);
void cancelToast(String pkg, IBinder token);
void finishToken(String pkg, IBinder token);
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 6b40890..37e07de 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -141,6 +141,8 @@
public static final int ACTIVITY_TYPE_RECENTS = 3;
/** Assistant activity type. */
public static final int ACTIVITY_TYPE_ASSISTANT = 4;
+ /** Dream activity type. */
+ public static final int ACTIVITY_TYPE_DREAM = 5;
/** @hide */
@IntDef(prefix = { "ACTIVITY_TYPE_" }, value = {
@@ -149,6 +151,7 @@
ACTIVITY_TYPE_HOME,
ACTIVITY_TYPE_RECENTS,
ACTIVITY_TYPE_ASSISTANT,
+ ACTIVITY_TYPE_DREAM,
})
public @interface ActivityType {}
@@ -746,9 +749,11 @@
* @hide
*/
public boolean isAlwaysOnTop() {
- return mWindowingMode == WINDOWING_MODE_PINNED || (mAlwaysOnTop == ALWAYS_ON_TOP_ON
- && (mWindowingMode == WINDOWING_MODE_FREEFORM
- || mWindowingMode == WINDOWING_MODE_MULTI_WINDOW));
+ if (mWindowingMode == WINDOWING_MODE_PINNED) return true;
+ if (mActivityType == ACTIVITY_TYPE_DREAM) return true;
+ if (mAlwaysOnTop != ALWAYS_ON_TOP_ON) return false;
+ return mWindowingMode == WINDOWING_MODE_FREEFORM
+ || mWindowingMode == WINDOWING_MODE_MULTI_WINDOW;
}
/**
@@ -798,7 +803,7 @@
/** @hide */
public static boolean supportSplitScreenWindowingMode(int activityType) {
- return activityType != ACTIVITY_TYPE_ASSISTANT;
+ return activityType != ACTIVITY_TYPE_ASSISTANT && activityType != ACTIVITY_TYPE_DREAM;
}
/** @hide */
@@ -823,6 +828,7 @@
case ACTIVITY_TYPE_HOME: return "home";
case ACTIVITY_TYPE_RECENTS: return "recents";
case ACTIVITY_TYPE_ASSISTANT: return "assistant";
+ case ACTIVITY_TYPE_DREAM: return "dream";
}
return String.valueOf(applicationType);
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 95385ee..b1d6c83 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -11215,6 +11215,17 @@
&& hasWebURI();
}
+ private boolean isImageCaptureIntent() {
+ return (MediaStore.ACTION_IMAGE_CAPTURE.equals(mAction)
+ || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(mAction)
+ || MediaStore.ACTION_VIDEO_CAPTURE.equals(mAction));
+ }
+
+ /** @hide */
+ public boolean isImplicitImageCaptureIntent() {
+ return mPackage == null && mComponent == null && isImageCaptureIntent();
+ }
+
/**
* @hide
*/
@@ -11241,9 +11252,7 @@
}
putParcelableArrayListExtra(EXTRA_STREAM, newStreams);
}
- } else if (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
- || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(action)
- || MediaStore.ACTION_VIDEO_CAPTURE.equals(action)) {
+ } else if (isImageCaptureIntent()) {
final Uri output = getParcelableExtra(MediaStore.EXTRA_OUTPUT);
if (output != null) {
putExtra(MediaStore.EXTRA_OUTPUT, maybeAddUserId(output, contentUserHint));
@@ -11349,9 +11358,7 @@
}
} catch (ClassCastException e) {
}
- } else if (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
- || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(action)
- || MediaStore.ACTION_VIDEO_CAPTURE.equals(action)) {
+ } else if (isImageCaptureIntent()) {
final Uri output;
try {
output = getParcelableExtra(MediaStore.EXTRA_OUTPUT);
diff --git a/core/java/android/inputmethodservice/InlineSuggestionSession.java b/core/java/android/inputmethodservice/InlineSuggestionSession.java
index c31cb4e..9b3e8c9 100644
--- a/core/java/android/inputmethodservice/InlineSuggestionSession.java
+++ b/core/java/android/inputmethodservice/InlineSuggestionSession.java
@@ -166,9 +166,14 @@
}
return;
}
-
+ // The IME doesn't have information about the virtual view id for the child views in the
+ // web view, so we are only comparing the parent view id here. This means that for cases
+ // where there are two input fields in the web view, they will have the same view id
+ // (although different virtual child id), and we will not be able to distinguish them.
+ final AutofillId imeClientFieldId = mClientAutofillIdSupplier.get();
if (!mComponentName.getPackageName().equals(mClientPackageNameSupplier.get())
- || !fieldId.equalsIgnoreSession(mClientAutofillIdSupplier.get())) {
+ || imeClientFieldId == null
+ || fieldId.getViewId() != imeClientFieldId.getViewId()) {
if (DEBUG) {
Log.d(TAG,
"handleOnInlineSuggestionsResponse() called on the wrong package/field "
diff --git a/core/java/android/net/InvalidPacketException.java b/core/java/android/net/InvalidPacketException.java
index b3b0f11..1873d77 100644
--- a/core/java/android/net/InvalidPacketException.java
+++ b/core/java/android/net/InvalidPacketException.java
@@ -27,7 +27,7 @@
* @hide
*/
@SystemApi
-public class InvalidPacketException extends Exception {
+public final class InvalidPacketException extends Exception {
private final int mError;
// Must match SocketKeepalive#ERROR_INVALID_IP_ADDRESS.
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index fcfcebd..05d7860 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -2000,7 +2000,7 @@
*/
@SystemApi
@TestApi
- public static class Builder {
+ public static final class Builder {
private final NetworkCapabilities mCaps;
/**
diff --git a/core/java/android/os/incremental/V4Signature.java b/core/java/android/os/incremental/V4Signature.java
index 71f931d..5cc73ca 100644
--- a/core/java/android/os/incremental/V4Signature.java
+++ b/core/java/android/os/incremental/V4Signature.java
@@ -72,16 +72,16 @@
* V4 signature data.
*/
public static class SigningInfo {
- public final byte[] v3Digest; // used to match with the corresponding APK
+ public final byte[] apkDigest; // used to match with the corresponding APK
public final byte[] certificate; // ASN.1 DER form
public final byte[] additionalData; // a free-form binary data blob
public final byte[] publicKey; // ASN.1 DER, must match the certificate
public final int signatureAlgorithmId; // see the APK v2 doc for the list
public final byte[] signature;
- SigningInfo(byte[] v3Digest, byte[] certificate, byte[] additionalData,
+ SigningInfo(byte[] apkDigest, byte[] certificate, byte[] additionalData,
byte[] publicKey, int signatureAlgorithmId, byte[] signature) {
- this.v3Digest = v3Digest;
+ this.apkDigest = apkDigest;
this.certificate = certificate;
this.additionalData = additionalData;
this.publicKey = publicKey;
@@ -94,13 +94,13 @@
*/
public static SigningInfo fromByteArray(byte[] bytes) throws IOException {
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
- byte[] v3Digest = readBytes(buffer);
+ byte[] apkDigest = readBytes(buffer);
byte[] certificate = readBytes(buffer);
byte[] additionalData = readBytes(buffer);
byte[] publicKey = readBytes(buffer);
int signatureAlgorithmId = buffer.getInt();
byte[] signature = readBytes(buffer);
- return new SigningInfo(v3Digest, certificate, additionalData, publicKey,
+ return new SigningInfo(apkDigest, certificate, additionalData, publicKey,
signatureAlgorithmId, signature);
}
}
@@ -150,7 +150,7 @@
final int size =
4/*size*/ + 8/*fileSize*/ + 4/*hash_algorithm*/ + 1/*log2_blocksize*/ + bytesSize(
hashingInfo.salt) + bytesSize(hashingInfo.rawRootHash) + bytesSize(
- signingInfo.v3Digest) + bytesSize(signingInfo.certificate) + bytesSize(
+ signingInfo.apkDigest) + bytesSize(signingInfo.certificate) + bytesSize(
signingInfo.additionalData);
ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN);
buffer.putInt(size);
@@ -159,7 +159,7 @@
buffer.put(hashingInfo.log2BlockSize);
writeBytes(buffer, hashingInfo.salt);
writeBytes(buffer, hashingInfo.rawRootHash);
- writeBytes(buffer, signingInfo.v3Digest);
+ writeBytes(buffer, signingInfo.apkDigest);
writeBytes(buffer, signingInfo.certificate);
writeBytes(buffer, signingInfo.additionalData);
return buffer.array();
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
index 04be71f..346fe29 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -24,6 +24,7 @@
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm;
+import static android.util.apk.ApkSigningBlockUtils.pickBestDigestForV4;
import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray;
import android.util.ArrayMap;
@@ -117,7 +118,10 @@
return vSigner.certs;
}
- private static VerifiedSigner verify(String apkFile, boolean verifyIntegrity)
+ /**
+ * Same as above returns the full signer object, containing additional info e.g. digest.
+ */
+ public static VerifiedSigner verify(String apkFile, boolean verifyIntegrity)
throws SignatureNotFoundException, SecurityException, IOException {
try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) {
return verify(apk, verifyIntegrity);
@@ -209,9 +213,11 @@
verityDigest, apk.length(), signatureInfo);
}
+ byte[] digest = pickBestDigestForV4(contentDigests);
+
return new VerifiedSigner(
signerCerts.toArray(new X509Certificate[signerCerts.size()][]),
- verityRootHash);
+ verityRootHash, digest);
}
private static X509Certificate[] verifySigner(
@@ -426,11 +432,14 @@
*/
public static class VerifiedSigner {
public final X509Certificate[][] certs;
- public final byte[] verityRootHash;
- public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash) {
+ public final byte[] verityRootHash;
+ public final byte[] digest;
+
+ public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash, byte[] digest) {
this.certs = certs;
this.verityRootHash = verityRootHash;
+ this.digest = digest;
}
}
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
index 2437af2..4ab541b 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
@@ -16,8 +16,6 @@
package android.util.apk;
-import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256;
-import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512;
import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
import static android.util.apk.ApkSigningBlockUtils.compareSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.getContentDigestAlgorithmJcaDigestAlgorithm;
@@ -26,6 +24,7 @@
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm;
import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm;
+import static android.util.apk.ApkSigningBlockUtils.pickBestDigestForV4;
import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray;
import android.os.Build;
@@ -213,24 +212,11 @@
verityDigest, apk.length(), signatureInfo);
}
- result.digest = pickBestV3DigestForV4(contentDigests);
+ result.digest = pickBestDigestForV4(contentDigests);
return result;
}
- // Keep in sync with pickBestV3DigestForV4 in apksigner.V3SchemeVerifier.
- private static byte[] pickBestV3DigestForV4(Map<Integer, byte[]> contentDigests) {
- final int[] orderedContentDigestTypes =
- {CONTENT_DIGEST_CHUNKED_SHA512, CONTENT_DIGEST_VERITY_CHUNKED_SHA256,
- CONTENT_DIGEST_CHUNKED_SHA256};
- for (int contentDigestType : orderedContentDigestTypes) {
- if (contentDigests.containsKey(contentDigestType)) {
- return contentDigests.get(contentDigestType);
- }
- }
- return null;
- }
-
private static VerifiedSigner verifySigner(
ByteBuffer signerBlock,
Map<Integer, byte[]> contentDigests,
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
index 8c240d9..d40efce 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
@@ -145,7 +145,7 @@
"Public key mismatch between certificate and signature record");
}
- return new VerifiedSigner(new Certificate[]{certificate}, signingInfo.v3Digest);
+ return new VerifiedSigner(new Certificate[]{certificate}, signingInfo.apkDigest);
}
/**
@@ -155,11 +155,11 @@
*/
public static class VerifiedSigner {
public final Certificate[] certs;
- public byte[] v3Digest;
+ public byte[] apkDigest;
- public VerifiedSigner(Certificate[] certs, byte[] v3Digest) {
+ public VerifiedSigner(Certificate[] certs, byte[] apkDigest) {
this.certs = certs;
- this.v3Digest = v3Digest;
+ this.apkDigest = apkDigest;
}
}
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index c1cee48..ab8f80d3 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -184,27 +184,45 @@
Signature[] signerSigs = convertToSignatures(signerCerts);
if (verifyFull) {
- // v4 is an add-on and requires v3 signature to validate against its certificates
- ApkSignatureSchemeV3Verifier.VerifiedSigner nonstreaming =
- ApkSignatureSchemeV3Verifier.unsafeGetCertsWithoutVerification(apkPath);
- Certificate[][] nonstreamingCerts = new Certificate[][]{nonstreaming.certs};
- Signature[] nonstreamingSigs = convertToSignatures(nonstreamingCerts);
+ byte[] nonstreamingDigest = null;
+ Certificate[][] nonstreamingCerts = null;
+ try {
+ // v4 is an add-on and requires v2 or v3 signature to validate against its
+ // certificate and digest
+ ApkSignatureSchemeV3Verifier.VerifiedSigner v3Signer =
+ ApkSignatureSchemeV3Verifier.unsafeGetCertsWithoutVerification(apkPath);
+ nonstreamingDigest = v3Signer.digest;
+ nonstreamingCerts = new Certificate[][]{v3Signer.certs};
+ } catch (SignatureNotFoundException e) {
+ try {
+ ApkSignatureSchemeV2Verifier.VerifiedSigner v2Signer =
+ ApkSignatureSchemeV2Verifier.verify(apkPath, false);
+ nonstreamingDigest = v2Signer.digest;
+ nonstreamingCerts = v2Signer.certs;
+ } catch (SignatureNotFoundException ee) {
+ throw new SecurityException(
+ "V4 verification failed to collect V2/V3 certificates from : "
+ + apkPath, ee);
+ }
+ }
+
+ Signature[] nonstreamingSigs = convertToSignatures(nonstreamingCerts);
if (nonstreamingSigs.length != signerSigs.length) {
throw new SecurityException(
- "Invalid number of certificates: " + nonstreaming.certs.length);
+ "Invalid number of certificates: " + nonstreamingSigs.length);
}
for (int i = 0, size = signerSigs.length; i < size; ++i) {
if (!nonstreamingSigs[i].equals(signerSigs[i])) {
- throw new SecurityException("V4 signature certificate does not match V3");
+ throw new SecurityException(
+ "V4 signature certificate does not match V2/V3");
}
}
- // TODO(b/151240006): add support for v2 digest and make it mandatory.
- if (!ArrayUtils.isEmpty(vSigner.v3Digest) && !ArrayUtils.equals(vSigner.v3Digest,
- nonstreaming.digest, vSigner.v3Digest.length)) {
- throw new SecurityException("V3 digest in V4 signature does not match V3");
+ if (!ArrayUtils.equals(vSigner.apkDigest, nonstreamingDigest,
+ vSigner.apkDigest.length)) {
+ throw new SecurityException("APK digest in V4 signature does not match V2/V3");
}
}
diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java
index 4fe8515..2a4b65d 100644
--- a/core/java/android/util/apk/ApkSigningBlockUtils.java
+++ b/core/java/android/util/apk/ApkSigningBlockUtils.java
@@ -421,6 +421,10 @@
static final int CONTENT_DIGEST_CHUNKED_SHA512 = 2;
static final int CONTENT_DIGEST_VERITY_CHUNKED_SHA256 = 3;
+ private static final int[] V4_CONTENT_DIGEST_ALGORITHMS =
+ {CONTENT_DIGEST_CHUNKED_SHA512, CONTENT_DIGEST_VERITY_CHUNKED_SHA256,
+ CONTENT_DIGEST_CHUNKED_SHA256};
+
static int compareSignatureAlgorithm(int sigAlgorithm1, int sigAlgorithm2) {
int digestAlgorithm1 = getSignatureAlgorithmContentDigestAlgorithm(sigAlgorithm1);
int digestAlgorithm2 = getSignatureAlgorithmContentDigestAlgorithm(sigAlgorithm2);
@@ -572,6 +576,21 @@
}
/**
+ * Returns the best digest from the map of available digests.
+ * similarly to compareContentDigestAlgorithm.
+ *
+ * Keep in sync with pickBestDigestForV4 in apksigner's ApkSigningBlockUtils.
+ */
+ static byte[] pickBestDigestForV4(Map<Integer, byte[]> contentDigests) {
+ for (int algo : V4_CONTENT_DIGEST_ALGORITHMS) {
+ if (contentDigests.containsKey(algo)) {
+ return contentDigests.get(algo);
+ }
+ }
+ return null;
+ }
+
+ /**
* Returns new byte buffer whose content is a shared subsequence of this buffer's content
* between the specified start (inclusive) and end (exclusive) positions. As opposed to
* {@link ByteBuffer#slice()}, the returned buffer's byte order is the same as the source
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 4aeea10..62dd192 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -405,6 +405,13 @@
// The actual zoom value may changes based on this initial zoom value.
private float mInitialZoom = 1f;
+ // For calculating the line change slops while moving cursor/selection.
+ // The slop max/min value include line height and the slop on the upper/lower line.
+ private static final int LINE_CHANGE_SLOP_MAX_DP = 45;
+ private static final int LINE_CHANGE_SLOP_MIN_DP = 12;
+ private int mLineChangeSlopMax;
+ private int mLineChangeSlopMin;
+
Editor(TextView textView) {
mTextView = textView;
// Synchronize the filter list, which places the undo input filter at the end.
@@ -430,6 +437,14 @@
logCursor("Editor", "New magnifier is %s.",
mNewMagnifierEnabled ? "enabled" : "disabled");
}
+
+ mLineChangeSlopMax = (int) TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP, LINE_CHANGE_SLOP_MAX_DP,
+ mTextView.getContext().getResources().getDisplayMetrics());
+ mLineChangeSlopMin = (int) TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP, LINE_CHANGE_SLOP_MIN_DP,
+ mTextView.getContext().getResources().getDisplayMetrics());
+
}
@VisibleForTesting
@@ -6018,7 +6033,14 @@
}
}
- private int getCurrentLineAdjustedForSlop(Layout layout, int prevLine, float y) {
+ @VisibleForTesting
+ public void setLineChangeSlopMinMaxForTesting(final int min, final int max) {
+ mLineChangeSlopMin = min;
+ mLineChangeSlopMax = max;
+ }
+
+ @VisibleForTesting
+ public int getCurrentLineAdjustedForSlop(Layout layout, int prevLine, float y) {
final int trueLine = mTextView.getLineAtCoordinate(y);
if (layout == null || prevLine > layout.getLineCount()
|| layout.getLineCount() <= 0 || prevLine < 0) {
@@ -6031,28 +6053,21 @@
return trueLine;
}
+ final int lineHeight = layout.getLineBottom(prevLine) - layout.getLineTop(prevLine);
+ int slop = (int)(LINE_SLOP_MULTIPLIER_FOR_HANDLEVIEWS
+ * (layout.getLineBottom(trueLine) - layout.getLineTop(trueLine)));
+ slop = Math.max(mLineChangeSlopMin,
+ Math.min(mLineChangeSlopMax, lineHeight + slop)) - lineHeight;
+ slop = Math.max(0, slop);
+
final float verticalOffset = mTextView.viewportToContentVerticalOffset();
- final int lineCount = layout.getLineCount();
- final float slop = mTextView.getLineHeight() * LINE_SLOP_MULTIPLIER_FOR_HANDLEVIEWS;
-
- final float firstLineTop = layout.getLineTop(0) + verticalOffset;
- final float prevLineTop = layout.getLineTop(prevLine) + verticalOffset;
- final float yTopBound = Math.max(prevLineTop - slop, firstLineTop + slop);
-
- final float lastLineBottom = layout.getLineBottom(lineCount - 1) + verticalOffset;
- final float prevLineBottom = layout.getLineBottom(prevLine) + verticalOffset;
- final float yBottomBound = Math.min(prevLineBottom + slop, lastLineBottom - slop);
-
- // Determine if we've moved lines based on y position and previous line.
- int currLine;
- if (y <= yTopBound) {
- currLine = Math.max(prevLine - 1, 0);
- } else if (y >= yBottomBound) {
- currLine = Math.min(prevLine + 1, lineCount - 1);
- } else {
- currLine = prevLine;
+ if (trueLine > prevLine && y >= layout.getLineBottom(prevLine) + slop + verticalOffset) {
+ return trueLine;
}
- return currLine;
+ if (trueLine < prevLine && y <= layout.getLineTop(prevLine) - slop + verticalOffset) {
+ return trueLine;
+ }
+ return prevLine;
}
/**
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 8943da4..4f14539 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -144,9 +144,6 @@
@Nullable
private CharSequence mText;
- // TODO(b/144152069): Remove this after assessing impact on dogfood.
- private boolean mIsCustomToast;
-
/**
* Construct an empty Toast object. You must call {@link #setView} before you
* can call {@link #show}.
@@ -214,8 +211,7 @@
service.enqueueTextToast(pkg, mToken, mText, mDuration, displayId, callback);
}
} else {
- service.enqueueTextOrCustomToast(pkg, mToken, tn, mDuration, displayId,
- mIsCustomToast);
+ service.enqueueToast(pkg, mToken, tn, mDuration, displayId);
}
} catch (RemoteException e) {
// Empty
@@ -253,7 +249,6 @@
*/
@Deprecated
public void setView(View view) {
- mIsCustomToast = true;
mNextView = view;
}
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index ed2c5b2..ab57e3d 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2665,4 +2665,8 @@
// Open: Settings > Sound > Do Not Disturb > People > Messages
// OS: R
DND_MESSAGES = 1839;
+
+ // Open: Settings > Sound > Do Not Disturb > Apps > <Choose App>
+ // OS: R
+ DND_APPS_BYPASSING = 1840;
}
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java
index ba27fac..eae1bbc 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java
@@ -15,11 +15,12 @@
*/
package android.view.contentcapture;
+import static org.mockito.Mockito.mock;
import static org.testng.Assert.assertThrows;
+import android.content.ContentCaptureOptions;
import android.content.Context;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -37,16 +38,20 @@
@Mock
private Context mMockContext;
- private ContentCaptureManager mManager;
-
- @Before
- public void before() {
- mManager = new ContentCaptureManager(mMockContext, /* service= */ null,
- /* options= */ null);
+ @Test
+ public void testConstructor_invalidParametersThrowsException() {
+ assertThrows(NullPointerException.class,
+ () -> new ContentCaptureManager(mMockContext, /* service= */ null, /* options= */
+ null));
}
@Test
- public void testRemoveData_invalid() {
- assertThrows(NullPointerException.class, () -> mManager.removeData(null));
+ public void testRemoveData_invalidParametersThrowsException() {
+ final IContentCaptureManager mockService = mock(IContentCaptureManager.class);
+ final ContentCaptureOptions options = new ContentCaptureOptions(null);
+ final ContentCaptureManager manager =
+ new ContentCaptureManager(mMockContext, mockService, options);
+
+ assertThrows(NullPointerException.class, () -> manager.removeData(null));
}
}
diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
index f81964c..1b5ce8fd 100644
--- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
+++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
@@ -16,6 +16,7 @@
package android.widget;
+import static android.text.Spanned.SPAN_INCLUSIVE_EXCLUSIVE;
import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
import static android.widget.espresso.TextViewActions.dragOnText;
import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex;
@@ -37,6 +38,9 @@
import android.app.Instrumentation;
import android.graphics.Rect;
import android.text.Layout;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.style.AbsoluteSizeSpan;
import android.util.ArraySet;
import android.util.Log;
import android.view.InputDevice;
@@ -489,6 +493,38 @@
}
@Test
+ public void testLineChangeSlop() throws Throwable {
+ TextView tv = mActivity.findViewById(R.id.textview);
+ Spannable s = new SpannableString("a\nb\nc");
+ s.setSpan(new AbsoluteSizeSpan(10), 2, 4, SPAN_INCLUSIVE_EXCLUSIVE);
+ s.setSpan(new AbsoluteSizeSpan(32), 4, 5, SPAN_INCLUSIVE_EXCLUSIVE);
+ mInstrumentation.runOnMainSync(() -> tv.setText(s));
+
+ Layout layout = tv.getLayout();
+ Editor editor = tv.getEditorForTesting();
+ final float verticalOffset = tv.getExtendedPaddingTop();
+ editor.setLineChangeSlopMinMaxForTesting(30, 65);
+ // Hit top part of upper line, jump to upper line.
+ assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 5 + verticalOffset))
+ .isEqualTo(0);
+ // Hit bottom part of upper line, stay at current line.
+ assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 40 + verticalOffset))
+ .isEqualTo(1);
+ // Hit current line, stay at current line.
+ assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 70 + verticalOffset))
+ .isEqualTo(1);
+ // Hit top part of lower line, stay at current line.
+ assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 85 + verticalOffset))
+ .isEqualTo(1);
+ // Hit bottom part of lower line, jump to lower line.
+ assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 110 + verticalOffset))
+ .isEqualTo(2);
+ // Hit lower line of lower line, jump to target line.
+ assertThat(editor.getCurrentLineAdjustedForSlop(layout, 0, 110 + verticalOffset))
+ .isEqualTo(2);
+ }
+
+ @Test
public void testCursorDrag_snapDistance() throws Throwable {
String text = "line1: This is the 1st line: A\n"
+ "line2: This is the 2nd line: B\n"
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index b1300a9..922caeb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -66,6 +66,7 @@
private LocalBluetoothManager mLocalBluetoothManager;
private InfoMediaManager mInfoMediaManager;
private String mPackageName;
+ private MediaDevice mOnTransferBluetoothDevice;
@VisibleForTesting
List<MediaDevice> mMediaDevices = new ArrayList<>();
@@ -143,7 +144,7 @@
final CachedBluetoothDevice cachedDevice =
((BluetoothMediaDevice) device).getCachedDevice();
if (!cachedDevice.isConnected() && !cachedDevice.isBusy()) {
- device.setState(MediaDeviceState.STATE_CONNECTING);
+ mOnTransferBluetoothDevice = connectDevice;
cachedDevice.connect();
return;
}
@@ -389,6 +390,10 @@
mCurrentConnectedDevice = infoMediaDevice != null
? infoMediaDevice : updateCurrentConnectedDevice();
dispatchDeviceListUpdate();
+ if (mOnTransferBluetoothDevice != null && mOnTransferBluetoothDevice.isConnected()) {
+ connectDevice(mOnTransferBluetoothDevice);
+ mOnTransferBluetoothDevice = null;
+ }
}
private List<MediaDevice> buildDisconnectedBluetoothDevice() {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index 6b3a97f..4c61ef5 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -23,6 +23,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -107,8 +108,8 @@
when(mLocalProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
when(mLocalProfileManager.getHearingAidProfile()).thenReturn(mHapProfile);
- mInfoMediaDevice1 = new InfoMediaDevice(mContext, mMediaRouter2Manager, mRouteInfo1,
- TEST_PACKAGE_NAME);
+ mInfoMediaDevice1 = spy(new InfoMediaDevice(mContext, mMediaRouter2Manager, mRouteInfo1,
+ TEST_PACKAGE_NAME));
mInfoMediaDevice2 = new InfoMediaDevice(mContext, mMediaRouter2Manager, mRouteInfo2,
TEST_PACKAGE_NAME);
mLocalMediaManager = new LocalMediaManager(mContext, mLocalBluetoothManager,
@@ -565,6 +566,34 @@
}
@Test
+ public void onDeviceListAdded_transferToDisconnectedBluetooth_verifyConnectDevice() {
+ final List<MediaDevice> devices = new ArrayList<>();
+ final MediaDevice currentDevice = mock(MediaDevice.class);
+ final MediaDevice device = mock(BluetoothMediaDevice.class);
+ final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
+ mLocalMediaManager.mMediaDevices.add(device);
+ mLocalMediaManager.mMediaDevices.add(currentDevice);
+
+ when(device.getId()).thenReturn(TEST_DEVICE_ID_1);
+ when(currentDevice.getId()).thenReturn(TEST_CURRENT_DEVICE_ID);
+ when(((BluetoothMediaDevice) device).getCachedDevice()).thenReturn(cachedDevice);
+ when(cachedDevice.isConnected()).thenReturn(false);
+ when(cachedDevice.isBusy()).thenReturn(false);
+
+ mLocalMediaManager.registerCallback(mCallback);
+ mLocalMediaManager.connectDevice(device);
+
+ verify(cachedDevice).connect();
+ when(device.isConnected()).thenReturn(true);
+ mLocalMediaManager.mCurrentConnectedDevice = currentDevice;
+ devices.add(mInfoMediaDevice1);
+ devices.add(currentDevice);
+ mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
+
+ verify(mInfoMediaDevice1).connect();
+ }
+
+ @Test
public void onRequestFailed_shouldDispatchOnRequestFailed() {
mLocalMediaManager.registerCallback(mCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 09d7d26..b329991 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -490,6 +490,9 @@
public void onPowerSaveChanged(boolean active) {
if (mDozeHost.isPowerSaveActive()) {
mMachine.requestState(DozeMachine.State.DOZE);
+ } else if (mMachine.getState() == DozeMachine.State.DOZE
+ && mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT)) {
+ mMachine.requestState(DozeMachine.State.DOZE_AOD);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserObservable.java b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserObservable.java
index 3cdc01d..dea8c32 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserObservable.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserObservable.java
@@ -38,7 +38,7 @@
@Override
protected void onInactive() {
super.onInactive();
- mTracker.startTracking();
+ mTracker.stopTracking();
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index c6d84ff..d2f781d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -651,7 +651,7 @@
*/
public void updateNotifications(String reason) {
reapplyFilterAndSort(reason);
- if (mPresenter != null) {
+ if (mPresenter != null && !mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
mPresenter.updateNotificationViews();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt
index 0437877..cf670bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt
@@ -142,9 +142,11 @@
// To attach rows we can use _this one weird trick_: if the intended view to add does not
// have a parent, then simply add it (and its children).
entries.forEach { entry ->
- val listItem = rowRegistry.requireView(entry)
+ // TODO: We should eventually map GroupEntry's themselves to views so that we don't
+ // depend on representativeEntry here which may actually be null in the future
+ val listItem = rowRegistry.requireView(entry.representativeEntry!!)
- if (listItem.view.parent != null) {
+ if (listItem.view.parent == null) {
listContainer.addListItem(listItem)
stabilityManager.notifyViewAddition(listItem.view)
}
@@ -153,7 +155,8 @@
for ((idx, childEntry) in entry.children.withIndex()) {
val childListItem = rowRegistry.requireView(childEntry)
// Child hasn't been added yet. add it!
- if (!listItem.notificationChildren.contains(childListItem)) {
+ if (listItem.notificationChildren == null ||
+ !listItem.notificationChildren.contains(childListItem)) {
// TODO: old code here just Log.wtf()'d here. This might wreak havoc
if (childListItem.view.parent != null) {
throw IllegalStateException("trying to add a notification child that " +
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
index 7e9e760..e9cbf32 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
@@ -45,7 +45,8 @@
public void attach(NotifPipeline pipeline) {
mStatusBarStateController.addCallback(mStatusBarStateCallback);
- pipeline.addPreGroupFilter(mNotifFilter);
+ pipeline.addPreGroupFilter(mSuspendedFilter);
+ pipeline.addPreGroupFilter(mDozingFilter);
}
/**
@@ -53,33 +54,30 @@
* NotifListBuilder invalidates the notification list each time the ranking is updated,
* so we don't need to explicitly invalidate this filter on ranking update.
*/
- private final NotifFilter mNotifFilter = new NotifFilter(TAG) {
+ private final NotifFilter mSuspendedFilter = new NotifFilter("IsSuspendedFilter") {
@Override
public boolean shouldFilterOut(NotificationEntry entry, long now) {
- // App suspended from Ranking
- if (entry.getRanking().isSuspended()) {
- return true;
- }
+ return entry.getRanking().isSuspended();
+ }
+ };
+ private final NotifFilter mDozingFilter = new NotifFilter("IsDozingFilter") {
+ @Override
+ public boolean shouldFilterOut(NotificationEntry entry, long now) {
// Dozing + DND Settings from Ranking object
if (mStatusBarStateController.isDozing() && entry.shouldSuppressAmbient()) {
return true;
}
- if (!mStatusBarStateController.isDozing() && entry.shouldSuppressNotificationList()) {
- return true;
- }
-
- return false;
+ return !mStatusBarStateController.isDozing() && entry.shouldSuppressNotificationList();
}
};
-
private final StatusBarStateController.StateListener mStatusBarStateCallback =
new StatusBarStateController.StateListener() {
@Override
public void onDozingChanged(boolean isDozing) {
- mNotifFilter.invalidateList();
+ mDozingFilter.invalidateList();
}
};
}
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 be8af82..823b186 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
@@ -6478,7 +6478,7 @@
private boolean hasActiveNotifications() {
if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
- return mNotifPipeline.getShadeList().isEmpty();
+ return !mNotifPipeline.getShadeList().isEmpty();
} else {
return mEntryManager.hasActiveNotifications();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
index e84f9cf..85acbe6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
@@ -19,9 +19,8 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -42,6 +41,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -51,20 +51,23 @@
@Mock private StatusBarStateController mStatusBarStateController;
@Mock private NotifPipeline mNotifPipeline;
+
+ @Captor private ArgumentCaptor<NotifFilter> mNotifFilterCaptor;
+
private NotificationEntry mEntry;
- private RankingCoordinator mRankingCoordinator;
- private NotifFilter mRankingFilter;
+ private NotifFilter mCapturedSuspendedFilter;
+ private NotifFilter mCapturedDozingFilter;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mRankingCoordinator = new RankingCoordinator(mStatusBarStateController);
+ RankingCoordinator rankingCoordinator = new RankingCoordinator(mStatusBarStateController);
mEntry = new NotificationEntryBuilder().build();
- ArgumentCaptor<NotifFilter> filterCaptor = ArgumentCaptor.forClass(NotifFilter.class);
- mRankingCoordinator.attach(mNotifPipeline);
- verify(mNotifPipeline, times(1)).addPreGroupFilter(filterCaptor.capture());
- mRankingFilter = filterCaptor.getValue();
+ rankingCoordinator.attach(mNotifPipeline);
+ verify(mNotifPipeline, times(2)).addPreGroupFilter(mNotifFilterCaptor.capture());
+ mCapturedSuspendedFilter = mNotifFilterCaptor.getAllValues().get(0);
+ mCapturedDozingFilter = mNotifFilterCaptor.getAllValues().get(1);
}
@Test
@@ -73,7 +76,7 @@
mEntry.setRanking(getRankingForUnfilteredNotif().build());
// THEN don't filter out the notification
- assertFalse(mRankingFilter.shouldFilterOut(mEntry, 0));
+ assertFalse(mCapturedSuspendedFilter.shouldFilterOut(mEntry, 0));
}
@Test
@@ -84,7 +87,7 @@
.build());
// THEN filter out the notification
- assertTrue(mRankingFilter.shouldFilterOut(mEntry, 0));
+ assertTrue(mCapturedSuspendedFilter.shouldFilterOut(mEntry, 0));
}
@Test
@@ -98,13 +101,13 @@
when(mStatusBarStateController.isDozing()).thenReturn(true);
// THEN filter out the notification
- assertTrue(mRankingFilter.shouldFilterOut(mEntry, 0));
+ assertTrue(mCapturedDozingFilter.shouldFilterOut(mEntry, 0));
// WHEN it's not dozing (showing the notification list)
when(mStatusBarStateController.isDozing()).thenReturn(false);
// THEN don't filter out the notification
- assertFalse(mRankingFilter.shouldFilterOut(mEntry, 0));
+ assertFalse(mCapturedDozingFilter.shouldFilterOut(mEntry, 0));
}
@Test
@@ -118,13 +121,13 @@
when(mStatusBarStateController.isDozing()).thenReturn(true);
// THEN don't filter out the notification
- assertFalse(mRankingFilter.shouldFilterOut(mEntry, 0));
+ assertFalse(mCapturedDozingFilter.shouldFilterOut(mEntry, 0));
// WHEN it's not dozing (showing the notification list)
when(mStatusBarStateController.isDozing()).thenReturn(false);
// THEN filter out the notification
- assertTrue(mRankingFilter.shouldFilterOut(mEntry, 0));
+ assertTrue(mCapturedDozingFilter.shouldFilterOut(mEntry, 0));
}
private RankingBuilder getRankingForUnfilteredNotif() {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index e49c1ed..c6a54fc 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -50,6 +50,7 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.DeviceConfig;
@@ -63,6 +64,7 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import android.util.TimeUtils;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillManager.SmartSuggestionMode;
@@ -151,6 +153,7 @@
private final LocalLog mWtfHistory = new LocalLog(50);
private final AutofillCompatState mAutofillCompatState = new AutofillCompatState();
+ private final DisabledInfoCache mDisabledInfoCache = new DisabledInfoCache();
private final LocalService mLocalService = new LocalService();
private final ActivityManagerInternal mAm;
@@ -302,14 +305,15 @@
@Override // from AbstractMasterSystemService
protected AutofillManagerServiceImpl newServiceLocked(@UserIdInt int resolvedUserId,
boolean disabled) {
- return new AutofillManagerServiceImpl(this, mLock, mUiLatencyHistory,
- mWtfHistory, resolvedUserId, mUi, mAutofillCompatState, disabled);
+ return new AutofillManagerServiceImpl(this, mLock, mUiLatencyHistory, mWtfHistory,
+ resolvedUserId, mUi, mAutofillCompatState, disabled, mDisabledInfoCache);
}
@Override // AbstractMasterSystemService
protected void onServiceRemoved(@NonNull AutofillManagerServiceImpl service,
@UserIdInt int userId) {
service.destroyLocked();
+ mDisabledInfoCache.remove(userId);
mAutofillCompatState.removeCompatibilityModeRequests(userId);
}
@@ -835,15 +839,10 @@
private void injectDisableAppInfo(@NonNull AutofillOptions options, int userId,
String packageName) {
- synchronized (mLock) {
- final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
- if (service != null) {
- options.appDisabledExpiration = service.getAppDisabledExpirationLocked(
- packageName);
- options.disabledActivities = service.getAppDisabledActivitiesLocked(
- packageName);
- }
- }
+ options.appDisabledExpiration =
+ mDisabledInfoCache.getAppDisabledExpiration(userId, packageName);
+ options.disabledActivities =
+ mDisabledInfoCache.getAppDisabledActivities(userId, packageName);
}
}
@@ -867,6 +866,234 @@
}
/**
+ * Stores autofill disable information, i.e. {@link AutofillDisabledInfo}, keyed by user id.
+ * The information is cleaned up when the service is removed.
+ */
+ static final class DisabledInfoCache {
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private final SparseArray<AutofillDisabledInfo> mCache = new SparseArray<>();
+
+ void remove(@UserIdInt int userId) {
+ synchronized (mLock) {
+ mCache.remove(userId);
+ }
+ }
+
+ void addDisabledAppLocked(@UserIdInt int userId, @NonNull String packageName,
+ long expiration) {
+ Preconditions.checkNotNull(packageName);
+ synchronized (mLock) {
+ AutofillDisabledInfo info =
+ getOrCreateAutofillDisabledInfoByUserIdLocked(userId);
+ info.putDisableAppsLocked(packageName, expiration);
+ }
+ }
+
+ void addDisabledActivityLocked(@UserIdInt int userId, @NonNull ComponentName componentName,
+ long expiration) {
+ Preconditions.checkNotNull(componentName);
+ synchronized (mLock) {
+ AutofillDisabledInfo info =
+ getOrCreateAutofillDisabledInfoByUserIdLocked(userId);
+ info.putDisableActivityLocked(componentName, expiration);
+ }
+ }
+
+ boolean isAutofillDisabledLocked(@UserIdInt int userId,
+ @NonNull ComponentName componentName) {
+ Preconditions.checkNotNull(componentName);
+ final boolean disabled;
+ synchronized (mLock) {
+ final AutofillDisabledInfo info = mCache.get(userId);
+ disabled = info != null ? info.isAutofillDisabledLocked(componentName) : false;
+ }
+ return disabled;
+ }
+
+ long getAppDisabledExpiration(@UserIdInt int userId, @NonNull String packageName) {
+ Preconditions.checkNotNull(packageName);
+ final Long expiration;
+ synchronized (mLock) {
+ final AutofillDisabledInfo info = mCache.get(userId);
+ expiration = info != null ? info.getAppDisabledExpirationLocked(packageName) : 0;
+ }
+ return expiration;
+ }
+
+ @Nullable
+ ArrayMap<String, Long> getAppDisabledActivities(@UserIdInt int userId,
+ @NonNull String packageName) {
+ Preconditions.checkNotNull(packageName);
+ final ArrayMap<String, Long> disabledList;
+ synchronized (mLock) {
+ final AutofillDisabledInfo info = mCache.get(userId);
+ disabledList =
+ info != null ? info.getAppDisabledActivitiesLocked(packageName) : null;
+ }
+ return disabledList;
+ }
+
+ void dump(@UserIdInt int userId, String prefix, PrintWriter pw) {
+ synchronized (mLock) {
+ final AutofillDisabledInfo info = mCache.get(userId);
+ if (info != null) {
+ info.dumpLocked(prefix, pw);
+ }
+ }
+ }
+
+ @NonNull
+ private AutofillDisabledInfo getOrCreateAutofillDisabledInfoByUserIdLocked(
+ @UserIdInt int userId) {
+ AutofillDisabledInfo info = mCache.get(userId);
+ if (info == null) {
+ info = new AutofillDisabledInfo();
+ mCache.put(userId, info);
+ }
+ return info;
+ }
+ }
+
+ /**
+ * The autofill disable information.
+ * <p>
+ * This contains disable information set by the AutofillService, e.g. disabled application
+ * expiration, disable activity expiration.
+ */
+ private static final class AutofillDisabledInfo {
+ /**
+ * Apps disabled by the service; key is package name, value is when they will be enabled
+ * again.
+ */
+ private ArrayMap<String, Long> mDisabledApps;
+ /**
+ * Activities disabled by the service; key is component name, value is when they will be
+ * enabled again.
+ */
+ private ArrayMap<ComponentName, Long> mDisabledActivities;
+
+ void putDisableAppsLocked(@NonNull String packageName, long expiration) {
+ if (mDisabledApps == null) {
+ mDisabledApps = new ArrayMap<>(1);
+ }
+ mDisabledApps.put(packageName, expiration);
+ }
+
+ void putDisableActivityLocked(@NonNull ComponentName componentName, long expiration) {
+ if (mDisabledActivities == null) {
+ mDisabledActivities = new ArrayMap<>(1);
+ }
+ mDisabledActivities.put(componentName, expiration);
+ }
+
+ long getAppDisabledExpirationLocked(@NonNull String packageName) {
+ if (mDisabledApps == null) {
+ return 0;
+ }
+ final Long expiration = mDisabledApps.get(packageName);
+ return expiration != null ? expiration : 0;
+ }
+
+ ArrayMap<String, Long> getAppDisabledActivitiesLocked(@NonNull String packageName) {
+ if (mDisabledActivities != null) {
+ final int size = mDisabledActivities.size();
+ ArrayMap<String, Long> disabledList = null;
+ for (int i = 0; i < size; i++) {
+ final ComponentName component = mDisabledActivities.keyAt(i);
+ if (packageName.equals(component.getPackageName())) {
+ if (disabledList == null) {
+ disabledList = new ArrayMap<>();
+ }
+ final long expiration = mDisabledActivities.valueAt(i);
+ disabledList.put(component.flattenToShortString(), expiration);
+ }
+ }
+ return disabledList;
+ }
+ return null;
+ }
+
+ boolean isAutofillDisabledLocked(@NonNull ComponentName componentName) {
+ // Check activities first.
+ long elapsedTime = 0;
+ if (mDisabledActivities != null) {
+ elapsedTime = SystemClock.elapsedRealtime();
+ final Long expiration = mDisabledActivities.get(componentName);
+ if (expiration != null) {
+ if (expiration >= elapsedTime) return true;
+ // Restriction expired - clean it up.
+ if (sVerbose) {
+ Slog.v(TAG, "Removing " + componentName.toShortString()
+ + " from disabled list");
+ }
+ mDisabledActivities.remove(componentName);
+ }
+ }
+
+ // Then check apps.
+ final String packageName = componentName.getPackageName();
+ if (mDisabledApps == null) return false;
+
+ final Long expiration = mDisabledApps.get(packageName);
+ if (expiration == null) return false;
+
+ if (elapsedTime == 0) {
+ elapsedTime = SystemClock.elapsedRealtime();
+ }
+
+ if (expiration >= elapsedTime) return true;
+
+ // Restriction expired - clean it up.
+ if (sVerbose) Slog.v(TAG, "Removing " + packageName + " from disabled list");
+ mDisabledApps.remove(packageName);
+ return false;
+ }
+
+ void dumpLocked(String prefix, PrintWriter pw) {
+ pw.print(prefix); pw.print("Disabled apps: ");
+ if (mDisabledApps == null) {
+ pw.println("N/A");
+ } else {
+ final int size = mDisabledApps.size();
+ pw.println(size);
+ final StringBuilder builder = new StringBuilder();
+ final long now = SystemClock.elapsedRealtime();
+ for (int i = 0; i < size; i++) {
+ final String packageName = mDisabledApps.keyAt(i);
+ final long expiration = mDisabledApps.valueAt(i);
+ builder.append(prefix).append(prefix)
+ .append(i).append(". ").append(packageName).append(": ");
+ TimeUtils.formatDuration((expiration - now), builder);
+ builder.append('\n');
+ }
+ pw.println(builder);
+ }
+
+ pw.print(prefix); pw.print("Disabled activities: ");
+ if (mDisabledActivities == null) {
+ pw.println("N/A");
+ } else {
+ final int size = mDisabledActivities.size();
+ pw.println(size);
+ final StringBuilder builder = new StringBuilder();
+ final long now = SystemClock.elapsedRealtime();
+ for (int i = 0; i < size; i++) {
+ final ComponentName component = mDisabledActivities.keyAt(i);
+ final long expiration = mDisabledActivities.valueAt(i);
+ builder.append(prefix).append(prefix)
+ .append(i).append(". ").append(component).append(": ");
+ TimeUtils.formatDuration((expiration - now), builder);
+ builder.append('\n');
+ }
+ pw.println(builder);
+ }
+ }
+ }
+
+ /**
* Compatibility mode metadata associated with all services.
*
* <p>This object is defined here instead of on each {@link AutofillManagerServiceImpl} because
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 6fbe141..d1805d9 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -67,7 +67,6 @@
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.TimeUtils;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillManager.SmartSuggestionMode;
@@ -80,6 +79,7 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.server.LocalServices;
import com.android.server.autofill.AutofillManagerService.AutofillCompatState;
+import com.android.server.autofill.AutofillManagerService.DisabledInfoCache;
import com.android.server.autofill.RemoteAugmentedAutofillService.RemoteAugmentedAutofillServiceCallbacks;
import com.android.server.autofill.ui.AutoFillUI;
import com.android.server.contentcapture.ContentCaptureManagerInternal;
@@ -90,7 +90,6 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
-
/**
* Bridge between the {@code system_server}'s {@link AutofillManagerService} and the
* app's {@link IAutoFillService} implementation.
@@ -125,19 +124,6 @@
private RemoteInlineSuggestionRenderService mRemoteInlineSuggestionRenderService;
/**
- * Apps disabled by the service; key is package name, value is when they will be enabled again.
- */
- @GuardedBy("mLock")
- private ArrayMap<String, Long> mDisabledApps;
-
- /**
- * Activities disabled by the service; key is component name, value is when they will be enabled
- * again.
- */
- @GuardedBy("mLock")
- private ArrayMap<ComponentName, Long> mDisabledActivities;
-
- /**
* Data used for field classification.
*/
@GuardedBy("mLock")
@@ -186,10 +172,12 @@
private final ContentCaptureManagerInternal mContentCaptureManagerInternal;
+ private final DisabledInfoCache mDisabledInfoCache;
+
AutofillManagerServiceImpl(AutofillManagerService master, Object lock,
LocalLog uiLatencyHistory, LocalLog wtfHistory, int userId, AutoFillUI ui,
AutofillCompatState autofillCompatState,
- boolean disabled) {
+ boolean disabled, DisabledInfoCache disableCache) {
super(master, lock, userId);
mUiLatencyHistory = uiLatencyHistory;
@@ -200,7 +188,7 @@
mInputMethodManagerInternal = LocalServices.getService(InputMethodManagerInternal.class);
mContentCaptureManagerInternal = LocalServices.getService(
ContentCaptureManagerInternal.class);
-
+ mDisabledInfoCache = disableCache;
updateLocked(disabled);
}
@@ -1045,45 +1033,7 @@
pw.println(isInlineSuggestionsEnabled());
pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune);
- pw.print(prefix); pw.print("Disabled apps: ");
-
- if (mDisabledApps == null) {
- pw.println("N/A");
- } else {
- final int size = mDisabledApps.size();
- pw.println(size);
- final StringBuilder builder = new StringBuilder();
- final long now = SystemClock.elapsedRealtime();
- for (int i = 0; i < size; i++) {
- final String packageName = mDisabledApps.keyAt(i);
- final long expiration = mDisabledApps.valueAt(i);
- builder.append(prefix).append(prefix)
- .append(i).append(". ").append(packageName).append(": ");
- TimeUtils.formatDuration((expiration - now), builder);
- builder.append('\n');
- }
- pw.println(builder);
- }
-
- pw.print(prefix); pw.print("Disabled activities: ");
-
- if (mDisabledActivities == null) {
- pw.println("N/A");
- } else {
- final int size = mDisabledActivities.size();
- pw.println(size);
- final StringBuilder builder = new StringBuilder();
- final long now = SystemClock.elapsedRealtime();
- for (int i = 0; i < size; i++) {
- final ComponentName component = mDisabledActivities.keyAt(i);
- final long expiration = mDisabledActivities.valueAt(i);
- builder.append(prefix).append(prefix)
- .append(i).append(". ").append(component).append(": ");
- TimeUtils.formatDuration((expiration - now), builder);
- builder.append('\n');
- }
- pw.println(builder);
- }
+ mDisabledInfoCache.dump(mUserId, prefix, pw);
final int size = mSessions.size();
if (size == 0) {
@@ -1480,15 +1430,13 @@
void disableAutofillForApp(@NonNull String packageName, long duration, int sessionId,
boolean compatMode) {
synchronized (mLock) {
- if (mDisabledApps == null) {
- mDisabledApps = new ArrayMap<>(1);
- }
long expiration = SystemClock.elapsedRealtime() + duration;
// Protect it against overflow
if (expiration < 0) {
expiration = Long.MAX_VALUE;
}
- mDisabledApps.put(packageName, expiration);
+ mDisabledInfoCache.addDisabledAppLocked(mUserId, packageName, expiration);
+
int intDuration = duration > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) duration;
mMetricsLogger.write(Helper.newLogMaker(MetricsEvent.AUTOFILL_SERVICE_DISABLED_APP,
packageName, getServicePackageName(), sessionId, compatMode)
@@ -1502,15 +1450,12 @@
void disableAutofillForActivity(@NonNull ComponentName componentName, long duration,
int sessionId, boolean compatMode) {
synchronized (mLock) {
- if (mDisabledActivities == null) {
- mDisabledActivities = new ArrayMap<>(1);
- }
long expiration = SystemClock.elapsedRealtime() + duration;
// Protect it against overflow
if (expiration < 0) {
expiration = Long.MAX_VALUE;
}
- mDisabledActivities.put(componentName, expiration);
+ mDisabledInfoCache.addDisabledActivityLocked(mUserId, componentName, expiration);
final int intDuration = duration > Integer.MAX_VALUE
? Integer.MAX_VALUE
: (int) duration;
@@ -1528,74 +1473,12 @@
}
}
- // Called by AutofillManagerService
- long getAppDisabledExpirationLocked(@NonNull String packageName) {
- if (mDisabledApps == null) {
- return 0;
- }
- final Long expiration = mDisabledApps.get(packageName);
- return expiration != null ? expiration : 0;
- }
-
- // Called by AutofillManagerService
- @Nullable
- ArrayMap<String, Long> getAppDisabledActivitiesLocked(@NonNull String packageName) {
- if (mDisabledActivities != null) {
- final int size = mDisabledActivities.size();
- ArrayMap<String, Long> disabledList = null;
- for (int i = 0; i < size; i++) {
- final ComponentName component = mDisabledActivities.keyAt(i);
- if (packageName.equals(component.getPackageName())) {
- if (disabledList == null) {
- disabledList = new ArrayMap<>();
- }
- final long expiration = mDisabledActivities.valueAt(i);
- disabledList.put(component.flattenToShortString(), expiration);
- }
- }
- return disabledList;
- }
- return null;
- }
-
/**
* Checks if autofill is disabled by service to the given activity.
*/
@GuardedBy("mLock")
private boolean isAutofillDisabledLocked(@NonNull ComponentName componentName) {
- // Check activities first.
- long elapsedTime = 0;
- if (mDisabledActivities != null) {
- elapsedTime = SystemClock.elapsedRealtime();
- final Long expiration = mDisabledActivities.get(componentName);
- if (expiration != null) {
- if (expiration >= elapsedTime) return true;
- // Restriction expired - clean it up.
- if (sVerbose) {
- Slog.v(TAG, "Removing " + componentName.toShortString()
- + " from disabled list");
- }
- mDisabledActivities.remove(componentName);
- }
- }
-
- // Then check apps.
- final String packageName = componentName.getPackageName();
- if (mDisabledApps == null) return false;
-
- final Long expiration = mDisabledApps.get(packageName);
- if (expiration == null) return false;
-
- if (elapsedTime == 0) {
- elapsedTime = SystemClock.elapsedRealtime();
- }
-
- if (expiration >= elapsedTime) return true;
-
- // Restriction expired - clean it up.
- if (sVerbose) Slog.v(TAG, "Removing " + packageName + " from disabled list");
- mDisabledApps.remove(packageName);
- return false;
+ return mDisabledInfoCache.isAutofillDisabledLocked(mUserId, componentName);
}
// Called by AutofillManager, checks UID.
diff --git a/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java b/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
index 5de8171..4ba2c3d 100644
--- a/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
+++ b/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
@@ -199,7 +199,10 @@
return false;
}
- if (!mImeInputViewStarted || !autofillId.equalsIgnoreSession(mImeFieldId)) {
+ // TODO(b/151846600): IME doesn't have access to the virtual id of the webview, so we
+ // only compare the view id for now.
+ if (!mImeInputViewStarted || mImeFieldId == null
+ || autofillId.getViewId() != mImeFieldId.getViewId()) {
if (sDebug) {
Log.d(TAG,
"onInlineSuggestionsResponseLocked not sent because input view is not "
diff --git a/services/core/java/com/android/server/am/BugReportHandlerUtil.java b/services/core/java/com/android/server/am/BugReportHandlerUtil.java
index 03f4a54..ba89fce 100644
--- a/services/core/java/com/android/server/am/BugReportHandlerUtil.java
+++ b/services/core/java/com/android/server/am/BugReportHandlerUtil.java
@@ -16,20 +16,15 @@
package com.android.server.am;
-import static android.app.AppOpsManager.OP_NONE;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import android.app.Activity;
import android.app.BroadcastOptions;
-import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Binder;
-import android.os.BugreportManager;
-import android.os.BugreportParams;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
@@ -115,17 +110,9 @@
options.setBackgroundActivityStartsAllowed(true);
final long identity = Binder.clearCallingIdentity();
try {
- // Handler app's BroadcastReceiver should call setResultCode(Activity.RESULT_OK) to
- // let ResultBroadcastReceiver know the handler app is available.
- context.sendOrderedBroadcastAsUser(intent,
- UserHandle.of(handlerUser),
+ context.sendBroadcastAsUser(intent, UserHandle.of(handlerUser),
android.Manifest.permission.DUMP,
- OP_NONE, options.toBundle(),
- new ResultBroadcastReceiver(),
- /* scheduler= */ null,
- Activity.RESULT_CANCELED,
- /* initialData= */ null,
- /* initialExtras= */ null);
+ options.toBundle());
} catch (RuntimeException e) {
Slog.e(TAG, "Error while trying to launch bugreport handler app.", e);
return false;
@@ -189,19 +176,4 @@
Binder.restoreCallingIdentity(identity);
}
}
-
- private static class ResultBroadcastReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (getResultCode() == Activity.RESULT_OK) {
- return;
- }
-
- Slog.w(TAG, "Request bug report because handler app seems to be not available.");
- BugreportManager bugreportManager = context.getSystemService(BugreportManager.class);
- bugreportManager.requestBugreport(
- new BugreportParams(BugreportParams.BUGREPORT_MODE_INTERACTIVE),
- /* shareTitle= */null, /* shareDescription= */ null);
- }
- }
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 60c3875..7f805be 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -139,6 +139,7 @@
import android.app.usage.UsageStatsManagerInternal;
import android.companion.ICompanionDeviceManager;
import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentProvider;
@@ -387,10 +388,9 @@
* still post toasts created with
* {@link android.widget.Toast#makeText(Context, CharSequence, int)} and its variants while
* in the background.
- *
- * TODO(b/144152069): Add @EnabledAfter(Q) to target R+ after assessing impact on dogfood
*/
@ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
private static final long CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK = 128611929L;
private IActivityManager mAm;
@@ -2751,24 +2751,18 @@
@Override
public void enqueueTextToast(String pkg, IBinder token, CharSequence text, int duration,
int displayId, @Nullable ITransientNotificationCallback callback) {
- enqueueToast(pkg, token, text, null, duration, displayId, callback, false);
+ enqueueToast(pkg, token, text, null, duration, displayId, callback);
}
@Override
public void enqueueToast(String pkg, IBinder token, ITransientNotification callback,
int duration, int displayId) {
- enqueueToast(pkg, token, null, callback, duration, displayId, null, true);
- }
-
- @Override
- public void enqueueTextOrCustomToast(String pkg, IBinder token,
- ITransientNotification callback, int duration, int displayId, boolean isCustom) {
- enqueueToast(pkg, token, null, callback, duration, displayId, null, isCustom);
+ enqueueToast(pkg, token, null, callback, duration, displayId, null);
}
private void enqueueToast(String pkg, IBinder token, @Nullable CharSequence text,
@Nullable ITransientNotification callback, int duration, int displayId,
- @Nullable ITransientNotificationCallback textCallback, boolean isCustom) {
+ @Nullable ITransientNotificationCallback textCallback) {
if (DBG) {
Slog.i(TAG, "enqueueToast pkg=" + pkg + " token=" + token
+ " duration=" + duration + " displayId=" + displayId);
@@ -2807,11 +2801,15 @@
}
boolean isAppRenderedToast = (callback != null);
- if (isAppRenderedToast && isCustom && !isSystemToast
- && !isPackageInForegroundForToast(pkg, callingUid)) {
+ if (isAppRenderedToast && !isSystemToast && !isPackageInForegroundForToast(pkg,
+ callingUid)) {
boolean block;
long id = Binder.clearCallingIdentity();
try {
+ // CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK is gated on targetSdk, so block will be
+ // false for apps with targetSdk < R. For apps with targetSdk R+, text toasts
+ // are not app-rendered, so isAppRenderedToast == true means it's a custom
+ // toast.
block = mPlatformCompat.isChangeEnabledByPackageName(
CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK, pkg,
callingUser.getIdentifier());
@@ -2824,11 +2822,6 @@
Binder.restoreCallingIdentity(id);
}
if (block) {
- // TODO(b/144152069): Remove informative toast
- mUiHandler.post(() -> Toast.makeText(getContext(),
- "Background custom toast blocked for package " + pkg + ".\n"
- + "See g.co/dev/toast.",
- Toast.LENGTH_SHORT).show());
Slog.w(TAG, "Blocking custom toast from package " + pkg
+ " due to package not in the foreground");
return;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 4e5d6c4..e605eeb 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -5070,15 +5070,16 @@
* action and a {@code android.intent.category.BROWSABLE} category</li>
* </ul>
*/
- int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps) {
+ int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps,
+ boolean matchSystemOnly) {
return updateFlagsForResolve(flags, userId, callingUid,
- wantInstantApps, false /*onlyExposedExplicitly*/);
+ wantInstantApps, matchSystemOnly, false /*onlyExposedExplicitly*/);
}
int updateFlagsForResolve(int flags, int userId, int callingUid,
- boolean wantInstantApps, boolean onlyExposedExplicitly) {
+ boolean wantInstantApps, boolean onlyExposedExplicitly, boolean matchSystemOnly) {
// Safe mode means we shouldn't match any third-party components
- if (mSafeMode) {
+ if (mSafeMode || matchSystemOnly) {
flags |= PackageManager.MATCH_SYSTEM_ONLY;
}
if (getInstantAppPackageName(callingUid) != null) {
@@ -6170,7 +6171,8 @@
if (!mUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
- flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart);
+ flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart,
+ intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/, "resolve intent");
@@ -6202,7 +6204,8 @@
intent = updateIntentForResolve(intent);
final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver());
final int flags = updateFlagsForResolve(
- 0, userId, callingUid, false /*includeInstantApps*/);
+ 0, userId, callingUid, false /*includeInstantApps*/,
+ intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, flags,
userId);
synchronized (mLock) {
@@ -6523,7 +6526,8 @@
android.provider.Settings.Global.getInt(mContext.getContentResolver(),
android.provider.Settings.Global.DEVICE_PROVISIONED, 0) == 1;
flags = updateFlagsForResolve(
- flags, userId, callingUid, false /*includeInstantApps*/);
+ flags, userId, callingUid, false /*includeInstantApps*/,
+ intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
intent = updateIntentForResolve(intent);
// writer
synchronized (mLock) {
@@ -6735,7 +6739,8 @@
}
synchronized (mLock) {
int flags = updateFlagsForResolve(0, parent.id, callingUid,
- false /*includeInstantApps*/);
+ false /*includeInstantApps*/,
+ intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
CrossProfileDomainInfo xpDomainInfo = getCrossProfileDomainPreferredLpr(
intent, resolvedType, flags, sourceUserId, parent.id);
return xpDomainInfo != null;
@@ -6821,7 +6826,8 @@
}
flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart,
- comp != null || pkgName != null /*onlyExposedExplicitly*/);
+ comp != null || pkgName != null /*onlyExposedExplicitly*/,
+ intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
if (comp != null) {
final List<ResolveInfo> list = new ArrayList<>(1);
final ActivityInfo ai = getActivityInfo(comp, flags, userId);
@@ -7606,7 +7612,8 @@
String resolvedType, int flags, int userId) {
if (!mUserManager.exists(userId)) return Collections.emptyList();
final int callingUid = Binder.getCallingUid();
- flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/);
+ flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
+ intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
mPermissionManager.enforceCrossUserPermission(callingUid, userId,
false /*requireFullPermission*/, false /*checkShell*/,
"query intent activity options");
@@ -7792,7 +7799,8 @@
false /*requireFullPermission*/, false /*checkShell*/,
"query intent receivers");
final String instantAppPkgName = getInstantAppPackageName(callingUid);
- flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/);
+ flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
+ intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
@@ -7882,7 +7890,8 @@
private ResolveInfo resolveServiceInternal(Intent intent, String resolvedType, int flags,
int userId, int callingUid) {
if (!mUserManager.exists(userId)) return null;
- flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/);
+ flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
+ false /* matchSystemOnly */);
List<ResolveInfo> query = queryIntentServicesInternal(
intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/);
if (query != null) {
@@ -7913,7 +7922,8 @@
false /*checkShell*/,
"query intent receivers");
final String instantAppPkgName = getInstantAppPackageName(callingUid);
- flags = updateFlagsForResolve(flags, userId, callingUid, includeInstantApps);
+ flags = updateFlagsForResolve(flags, userId, callingUid, includeInstantApps,
+ false /* matchSystemOnly */);
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
@@ -8050,7 +8060,8 @@
if (!mUserManager.exists(userId)) return Collections.emptyList();
final int callingUid = Binder.getCallingUid();
final String instantAppPkgName = getInstantAppPackageName(callingUid);
- flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/);
+ flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
+ false /* matchSystemOnly */);
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 6308d25..c6f375e 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -36,7 +36,6 @@
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
@@ -3743,20 +3742,6 @@
return super.checkCompleteDeferredRemoval();
}
- @Override
- int getOrientation() {
- return (canSpecifyOrientation()) ? super.getOrientation() : SCREEN_ORIENTATION_UNSET;
- }
-
- private boolean canSpecifyOrientation() {
- final int windowingMode = getWindowingMode();
- final int activityType = getActivityType();
- return windowingMode == WINDOWING_MODE_FULLSCREEN
- || activityType == ACTIVITY_TYPE_HOME
- || activityType == ACTIVITY_TYPE_RECENTS
- || activityType == ACTIVITY_TYPE_ASSISTANT;
- }
-
public DisplayInfo getDisplayInfo() {
return mDisplayContent.getDisplayInfo();
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 35492f4..a5b0026 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -31,6 +31,7 @@
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -1273,9 +1274,13 @@
a.colorMode = ActivityInfo.COLOR_MODE_DEFAULT;
a.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchActivityType(ACTIVITY_TYPE_DREAM);
+
try {
getActivityStartController().obtainStarter(intent, "dream")
.setActivityInfo(a)
+ .setActivityOptions(options.toBundle())
.setIsDream(true)
.execute();
return true;
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 33dd9cf..1036af6 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -467,6 +468,10 @@
return getActivityType() == ACTIVITY_TYPE_ASSISTANT;
}
+ public boolean isActivityTypeDream() {
+ return getActivityType() == ACTIVITY_TYPE_DREAM;
+ }
+
public boolean isActivityTypeStandard() {
return getActivityType() == ACTIVITY_TYPE_STANDARD;
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 6365144..ede4569 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -4485,6 +4485,10 @@
*/
private int findPositionForStack(int requestedPosition, ActivityStack stack,
boolean adding) {
+ if (stack.isActivityTypeDream()) {
+ return POSITION_TOP;
+ }
+
if (stack.inPinnedWindowingMode()) {
return POSITION_TOP;
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 5e88fb0..4da4a79 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1527,25 +1527,7 @@
&& (mNotificationShade.getAttrs().privateFlags
& PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0;
- // When the navigation bar isn't visible, we put up a fake input window to catch all
- // touch events. This way we can detect when the user presses anywhere to bring back the
- // nav bar and ensure the application doesn't see the event.
- if (navVisible || navAllowedHidden) {
- if (mInputConsumer != null) {
- mInputConsumer.dismiss();
- mHandler.sendMessage(
- mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer));
- mInputConsumer = null;
- }
- } else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) {
- mInputConsumer = mDisplayContent.getInputMonitor().createInputConsumer(
- mHandler.getLooper(),
- INPUT_CONSUMER_NAVIGATION,
- HideNavInputEventReceiver::new);
- // As long as mInputConsumer is active, hover events are not dispatched to the app
- // and the pointer icon is likely to become stale. Hide it to avoid confusion.
- InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL);
- }
+ updateHideNavInputEventReceiver(navVisible, navAllowedHidden);
// For purposes of positioning and showing the nav bar, if we have decided that it can't
// be hidden (because of the screen aspect ratio), then take that into account.
@@ -1567,6 +1549,28 @@
mLastNotificationShadeForcesShowingNavigation = notificationShadeForcesShowingNavigation;
}
+ void updateHideNavInputEventReceiver(boolean navVisible, boolean navAllowedHidden) {
+ // When the navigation bar isn't visible, we put up a fake input window to catch all
+ // touch events. This way we can detect when the user presses anywhere to bring back the
+ // nav bar and ensure the application doesn't see the event.
+ if (navVisible || navAllowedHidden) {
+ if (mInputConsumer != null) {
+ mInputConsumer.dismiss();
+ mHandler.sendMessage(
+ mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer));
+ mInputConsumer = null;
+ }
+ } else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) {
+ mInputConsumer = mDisplayContent.getInputMonitor().createInputConsumer(
+ mHandler.getLooper(),
+ INPUT_CONSUMER_NAVIGATION,
+ HideNavInputEventReceiver::new);
+ // As long as mInputConsumer is active, hover events are not dispatched to the app
+ // and the pointer icon is likely to become stale. Hide it to avoid confusion.
+ InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL);
+ }
+ }
+
private static void updateInsetsStateForDisplayCutout(DisplayFrames displayFrames,
InsetsState state) {
if (displayFrames.mDisplayCutout.getDisplayCutout().isEmpty()) {
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index bb02789..ac6e75c 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -25,6 +25,7 @@
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.SyncRtSurfaceTransactionApplier.applyParams;
+import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_TOUCH;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
@@ -91,6 +92,12 @@
|| focusedWin != getNavControlTarget(focusedWin)
|| focusedWin.getRequestedInsetsState().getSource(ITYPE_NAVIGATION_BAR)
.isVisible());
+ updateHideNavInputEventReceiver();
+ }
+
+ private void updateHideNavInputEventReceiver() {
+ mPolicy.updateHideNavInputEventReceiver(!isHidden(ITYPE_NAVIGATION_BAR),
+ mFocusedWin.mAttrs.insetsFlags.behavior != BEHAVIOR_SHOW_BARS_BY_TOUCH);
}
boolean isHidden(@InternalInsetsType int type) {
@@ -169,6 +176,7 @@
if (windowState == getNavControlTarget(mFocusedWin)) {
mNavBar.setVisible(state.getSource(ITYPE_NAVIGATION_BAR).isVisible());
}
+ updateHideNavInputEventReceiver();
}
/**
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index bd5666d..244ba82 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -21,6 +21,7 @@
import static android.app.ActivityManager.RECENT_WITH_EXCLUDED;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
@@ -1298,6 +1299,7 @@
switch (task.getActivityType()) {
case ACTIVITY_TYPE_HOME:
case ACTIVITY_TYPE_RECENTS:
+ case ACTIVITY_TYPE_DREAM:
// Ignore certain activity types completely
return false;
case ACTIVITY_TYPE_ASSISTANT:
diff --git a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
index 45f8a15..420997a 100644
--- a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
+++ b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
@@ -20,7 +20,6 @@
import static com.android.server.wm.ActivityStack.TAG_TASKS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.wm.Task.REPARENT_LEAVE_STACK_IN_PLACE;
import android.app.ActivityOptions;
import android.content.Intent;
@@ -233,29 +232,6 @@
}
final ActivityTaskManagerService atmService = mTargetStack.mAtmService;
- final ArrayList<Task> createdTasks = new ArrayList<>();
- while (!mPendingReparentActivities.isEmpty()) {
- final ActivityRecord r = mPendingReparentActivities.remove(0);
- final ActivityRecord bottom = mTargetStack.getBottomMostActivity();
- final Task targetTask;
- if (bottom != null && r.taskAffinity.equals(bottom.getTask().affinity)) {
- // If the activity currently at the bottom has the same task affinity as
- // the one we are moving, then merge it into the same task.
- targetTask = bottom.getTask();
- if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity "
- + r + " out to bottom task " + targetTask);
- } else {
- targetTask = mTargetStack.reuseOrCreateTask(
- r.info, null /*intent*/, false /*toTop*/);
- targetTask.affinityIntent = r.intent;
- createdTasks.add(targetTask);
- if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity "
- + r + " out to new task " + targetTask);
- }
- r.reparent(targetTask, 0 /* position */, "resetTargetTaskIfNeeded");
- atmService.mStackSupervisor.mRecentTasks.add(targetTask);
- }
-
DisplayContent display = mTargetStack.getDisplay();
final boolean singleTaskInstanceDisplay = display.isSingleTaskInstance();
if (singleTaskInstanceDisplay) {
@@ -264,16 +240,33 @@
final int windowingMode = mTargetStack.getWindowingMode();
final int activityType = mTargetStack.getActivityType();
- if (!singleTaskInstanceDisplay && !display.alwaysCreateStack(windowingMode, activityType)) {
- return;
- }
- while (!createdTasks.isEmpty()) {
- final Task targetTask = createdTasks.remove(createdTasks.size() - 1);
- final ActivityStack targetStack = display.getOrCreateStack(
- windowingMode, activityType, false /* onTop */);
- targetTask.reparent(targetStack, false /* toTop */, REPARENT_LEAVE_STACK_IN_PLACE,
- false /* animate */, true /* deferResume */, "resetTargetTask");
+ while (!mPendingReparentActivities.isEmpty()) {
+ final ActivityRecord r = mPendingReparentActivities.remove(0);
+ final boolean alwaysCreateTask = DisplayContent.alwaysCreateStack(windowingMode,
+ activityType);
+ final Task task = alwaysCreateTask
+ ? display.getBottomMostTask() : mTargetStack.getBottomMostTask();
+ Task targetTask = null;
+ if (task != null && r.taskAffinity.equals(task.affinity)) {
+ // If the activity currently at the bottom has the same task affinity as
+ // the one we are moving, then merge it into the same task.
+ targetTask = task;
+ if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity "
+ + r + " out to bottom task " + targetTask);
+ }
+ if (targetTask == null) {
+ if (alwaysCreateTask) {
+ targetTask = display.getOrCreateStack(windowingMode, activityType,
+ false /* onTop */);
+ } else {
+ targetTask = mTargetStack.reuseOrCreateTask(r.info, null /*intent*/,
+ false /*toTop*/);
+ }
+ targetTask.affinityIntent = r.intent;
+ }
+ r.reparent(targetTask, 0 /* position */, "resetTargetTaskIfNeeded");
+ atmService.mStackSupervisor.mRecentTasks.add(targetTask);
}
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index d2ed48f..b2920ee 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -18,6 +18,7 @@
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -2919,6 +2920,7 @@
case ACTIVITY_TYPE_HOME: return r.isActivityTypeHome();
case ACTIVITY_TYPE_RECENTS: return r.isActivityTypeRecents();
case ACTIVITY_TYPE_ASSISTANT: return r.isActivityTypeAssistant();
+ case ACTIVITY_TYPE_DREAM: return r.isActivityTypeDream();
}
if (stack.mCreatedByOrganizer) {
// Don't launch directly into task created by organizer...but why can't we?
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 7dd38e1..f19c106 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -20,6 +20,9 @@
import static android.app.ActivityTaskManager.RESIZE_MODE_FORCED;
import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM;
import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP;
@@ -3244,6 +3247,20 @@
}
@Override
+ int getOrientation(int candidate) {
+ return canSpecifyOrientation() ? super.getOrientation(candidate) : SCREEN_ORIENTATION_UNSET;
+ }
+
+ private boolean canSpecifyOrientation() {
+ final int windowingMode = getWindowingMode();
+ final int activityType = getActivityType();
+ return windowingMode == WINDOWING_MODE_FULLSCREEN
+ || activityType == ACTIVITY_TYPE_HOME
+ || activityType == ACTIVITY_TYPE_RECENTS
+ || activityType == ACTIVITY_TYPE_ASSISTANT;
+ }
+
+ @Override
boolean fillsParent() {
return matchParentBounds();
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 023a1e8..96cdbb3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -13070,26 +13070,25 @@
final boolean addingProfileRestricted = mUserManager.hasUserRestriction(
UserManager.DISALLOW_ADD_MANAGED_PROFILE, callingUserHandle);
- UserInfo parentUser = mUserManager.getProfileParent(callingUserId);
- final boolean addingProfileRestrictedOnParent = (parentUser != null)
- && mUserManager.hasUserRestriction(
- UserManager.DISALLOW_ADD_MANAGED_PROFILE,
- UserHandle.of(parentUser.id));
+ if (mUserManager.getUserInfo(callingUserId).isProfile()) {
+ Slog.i(LOG_TAG,
+ String.format("Calling user %d is a profile, cannot add another.",
+ callingUserId));
+ // The check is called from inside a managed profile. A managed profile cannot
+ // be provisioned from within another managed profile.
+ return CODE_CANNOT_ADD_MANAGED_PROFILE;
+ }
- Slog.i(LOG_TAG, String.format(
- "When checking for managed profile provisioning: Has device owner? %b, adding"
- + " profile restricted? %b, adding profile restricted on parent? %b",
- hasDeviceOwner, addingProfileRestricted, addingProfileRestrictedOnParent));
-
- // If there's a device owner, the restriction on adding a managed profile must be set
- // somewhere.
- if (hasDeviceOwner && !addingProfileRestricted && !addingProfileRestrictedOnParent) {
+ // If there's a device owner, the restriction on adding a managed profile must be set.
+ if (hasDeviceOwner && !addingProfileRestricted) {
Slog.wtf(LOG_TAG, "Has a device owner but no restriction on adding a profile.");
}
- // Do not allow adding a managed profile if there's a restriction, either on the current
- // user or its parent user.
- if (addingProfileRestricted || addingProfileRestrictedOnParent) {
+ // Do not allow adding a managed profile if there's a restriction.
+ if (addingProfileRestricted) {
+ Slog.i(LOG_TAG, String.format(
+ "Adding a profile is restricted: User %s Has device owner? %b",
+ callingUserHandle, hasDeviceOwner));
return CODE_CANNOT_ADD_MANAGED_PROFILE;
}
// If there's a restriction on removing the managed profile then we have to take it
@@ -13098,6 +13097,8 @@
!mUserManager.hasUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
callingUserHandle);
if (!mUserManager.canAddMoreManagedProfiles(callingUserId, canRemoveProfile)) {
+ Slog.i(LOG_TAG, String.format(
+ "Cannot add more profiles: Can remove current? %b", canRemoveProfile));
return CODE_CANNOT_ADD_MANAGED_PROFILE;
}
} finally {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 7c6ac17..baf551e 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -3205,6 +3205,7 @@
when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true))
.thenReturn(true);
setUserSetupCompleteForUser(false, UserHandle.USER_SYSTEM);
+ when(getServices().userManager.getProfileParent(UserHandle.USER_SYSTEM)).thenReturn(null);
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
}
@@ -3246,6 +3247,7 @@
when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true))
.thenReturn(true);
setUserSetupCompleteForUser(true, UserHandle.USER_SYSTEM);
+ when(getServices().userManager.getProfileParent(UserHandle.USER_SYSTEM)).thenReturn(null);
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
}
@@ -3617,14 +3619,14 @@
when(getServices().ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
.thenReturn(true);
- when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(true);
+ when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(false);
when(getServices().userManager.getProfileParent(DpmMockContext.CALLER_USER_HANDLE))
.thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
when(getServices().userManager.canAddMoreManagedProfiles(DpmMockContext.CALLER_USER_HANDLE,
true)).thenReturn(true);
setUserSetupCompleteForUser(false, DpmMockContext.CALLER_USER_HANDLE);
- mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+ mContext.binder.callingUid = DpmMockContext.ANOTHER_UID;
}
public void testIsProvisioningAllowed_provisionManagedProfileWithDeviceOwner_primaryUser()
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index e72f7df..56c19a4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -27,6 +27,7 @@
import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.util.DisplayMetrics.DENSITY_DEFAULT;
import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_ENABLED;
@@ -939,6 +940,20 @@
verify(persister, never()).saveTask(same(task), any());
}
+ @Test
+ public void testNotSpecifyOrientationByFloatingTask() {
+ final Task task = getTestTask();
+ final ActivityRecord activity = task.getTopMostActivity();
+ final WindowContainer<?> taskContainer = task.getParent();
+ activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE, taskContainer.getOrientation());
+
+ task.setWindowingMode(WINDOWING_MODE_PINNED);
+
+ assertEquals(SCREEN_ORIENTATION_UNSET, taskContainer.getOrientation());
+ }
+
private Task getTestTask() {
final ActivityStack stack = new StackBuilder(mRootWindowContainer).build();
return stack.getBottomMostTask();
diff --git a/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java b/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java
index 488ee78..47bf148 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java
@@ -23,6 +23,9 @@
import com.android.server.wm.ActivityMetricsLaunchObserver;
+import java.io.StringWriter;
+import java.io.PrintWriter;
+
/**
* A validator to check the correctness of event sequence during app startup.
*
@@ -100,7 +103,8 @@
@Override
public void onIntentStarted(@NonNull Intent intent, long timestampNs) {
if (state == State.UNKNOWN) {
- Log.wtf(TAG, "IntentStarted during UNKNOWN." + intent);
+ logWarningWithStackTrace(
+ String.format("IntentStarted during UNKNOWN. " + intent));
incAccIntentStartedEvents();
return;
}
@@ -110,7 +114,7 @@
state != State.ACTIVITY_CANCELLED &&
state != State.ACTIVITY_FINISHED &&
state != State.REPORT_FULLY_DRAWN) {
- Log.wtf(TAG,
+ logWarningWithStackTrace(
String.format("Cannot transition from %s to %s", state, State.INTENT_STARTED));
incAccIntentStartedEvents();
incAccIntentStartedEvents();
@@ -124,12 +128,12 @@
@Override
public void onIntentFailed() {
if (state == State.UNKNOWN) {
- Log.wtf(TAG, "IntentFailed during UNKNOWN.");
+ logWarningWithStackTrace(String.format("onIntentFailed during UNKNOWN."));
decAccIntentStartedEvents();
return;
}
if (state != State.INTENT_STARTED) {
- Log.wtf(TAG,
+ logWarningWithStackTrace(
String.format("Cannot transition from %s to %s", state, State.INTENT_FAILED));
incAccIntentStartedEvents();
return;
@@ -143,11 +147,12 @@
public void onActivityLaunched(@NonNull @ActivityRecordProto byte[] activity,
@Temperature int temperature) {
if (state == State.UNKNOWN) {
- Log.wtf(TAG, "onActivityLaunched during UNKNOWN.");
+ logWarningWithStackTrace(
+ String.format("onActivityLaunched during UNKNOWN."));
return;
}
if (state != State.INTENT_STARTED) {
- Log.wtf(TAG,
+ logWarningWithStackTrace(
String.format("Cannot transition from %s to %s", state, State.ACTIVITY_LAUNCHED));
incAccIntentStartedEvents();
return;
@@ -160,12 +165,13 @@
@Override
public void onActivityLaunchCancelled(@Nullable @ActivityRecordProto byte[] activity) {
if (state == State.UNKNOWN) {
- Log.wtf(TAG, "onActivityLaunchCancelled during UNKNOWN.");
+ logWarningWithStackTrace(
+ String.format("onActivityLaunchCancelled during UNKNOWN."));
decAccIntentStartedEvents();
return;
}
if (state != State.ACTIVITY_LAUNCHED) {
- Log.wtf(TAG,
+ logWarningWithStackTrace(
String.format("Cannot transition from %s to %s", state, State.ACTIVITY_CANCELLED));
incAccIntentStartedEvents();
return;
@@ -179,13 +185,14 @@
public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] activity,
long timestampNs) {
if (state == State.UNKNOWN) {
- Log.wtf(TAG, "onActivityLaunchFinished during UNKNOWN.");
+ logWarningWithStackTrace(
+ String.format("onActivityLaunchFinished during UNKNOWN."));
decAccIntentStartedEvents();
return;
}
if (state != State.ACTIVITY_LAUNCHED) {
- Log.wtf(TAG,
+ logWarningWithStackTrace(
String.format("Cannot transition from %s to %s", state, State.ACTIVITY_FINISHED));
incAccIntentStartedEvents();
return;
@@ -199,7 +206,8 @@
public void onReportFullyDrawn(@NonNull @ActivityRecordProto byte[] activity,
long timestampNs) {
if (state == State.UNKNOWN) {
- Log.wtf(TAG, "onReportFullyDrawn during UNKNOWN.");
+ logWarningWithStackTrace(
+ String.format("onReportFullyDrawn during UNKNOWN."));
return;
}
if (state == State.INIT) {
@@ -207,7 +215,7 @@
}
if (state != State.ACTIVITY_FINISHED) {
- Log.wtf(TAG,
+ logWarningWithStackTrace(
String.format("Cannot transition from %s to %s", state, State.REPORT_FULLY_DRAWN));
return;
}
@@ -252,4 +260,11 @@
Log.i(TAG,
String.format("dec AccIntentStartedEvents to %d", accIntentStartedEvents));
}
+
+ private void logWarningWithStackTrace(String log) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ new Throwable("EventSequenceValidator#getStackTrace").printStackTrace(pw);
+ Log.w(TAG, String.format("%s\n%s", log, sw));
+ }
}
diff --git a/tests/RollbackTest/RollbackTest.xml b/tests/RollbackTest/RollbackTest.xml
index 269cec1..7b85cc8 100644
--- a/tests/RollbackTest/RollbackTest.xml
+++ b/tests/RollbackTest/RollbackTest.xml
@@ -23,6 +23,10 @@
<option name="run-command" value="am broadcast -a 'com.google.android.gms.phenotype.FLAG_OVERRIDE' --es package "com.google.android.gms.platformconfigurator" --es user '\\*' --esa flags "ModuleConfig__versioned_immediate_commit_packages" --esa types "bytes" --esa values "Cm5vdGFwYWNrYWdlOgA=" com.google.android.gms" />
<option name="teardown-command" value="am broadcast -a 'com.google.android.gms.phenotype.FLAG_OVERRIDE' --es action delete --es package "com.google.android.gms.platformconfigurator" --es user '\*' --esa flag "ModuleConfig__immediate_commit_packages" com.google.android.gms" />
<option name="teardown-command" value="am broadcast -a 'com.google.android.gms.phenotype.FLAG_OVERRIDE' --es action delete --es package "com.google.android.gms.platformconfigurator" --es user '\*' --esa flag "ModuleConfig__versioned_immediate_commit_packages" com.google.android.gms" />
+ <option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.A" />
+ <option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
+ <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.A" />
+ <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.tests.rollback" />
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index 5a92d68..cab8b42 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -75,6 +75,12 @@
private static final String PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS =
"enable_rollback_timeout";
+ private static boolean hasRollbackInclude(List<RollbackInfo> rollbacks, String packageName) {
+ return rollbacks.stream().anyMatch(
+ ri -> ri.getPackages().stream().anyMatch(
+ pri -> packageName.equals(pri.getPackageName())));
+ }
+
/**
* Test basic rollbacks.
*/
@@ -113,18 +119,14 @@
// Uninstall TestApp.A
Uninstall.packages(TestApp.A);
assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
- // TODO: There is currently a race condition between when the app is
- // uninstalled and when rollback manager deletes the rollback. Fix it
- // so that's not the case!
for (int i = 0; i < 5; ++i) {
- RollbackInfo rollback = getUniqueRollbackInfoForPackage(
- rm.getRecentlyCommittedRollbacks(), TestApp.A);
- if (rollback != null) {
+ if (hasRollbackInclude(rm.getRecentlyCommittedRollbacks(), TestApp.A)) {
Log.i(TAG, "Sleeping 1 second to wait for uninstall to take effect.");
Thread.sleep(1000);
}
}
+ assertThat(hasRollbackInclude(rm.getRecentlyCommittedRollbacks(), TestApp.A)).isFalse();
// The app should not be available for rollback.
waitForUnavailableRollback(TestApp.A);
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index b2e8c37..916c339 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -463,7 +463,9 @@
nc1.setSSID(TEST_SSID);
nc2.combineCapabilities(nc1);
- assertTrue(TEST_SSID.equals(nc2.getSsid()));
+ if (isAtLeastR()) {
+ assertTrue(TEST_SSID.equals(nc2.getSsid()));
+ }
// Because they now have the same SSID, the following call should not throw
nc2.combineCapabilities(nc1);
@@ -601,12 +603,16 @@
// from nc2.
assertFalse(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING));
assertTrue(nc2.hasUnwantedCapability(NET_CAPABILITY_NOT_ROAMING));
- assertTrue(TEST_SSID.equals(nc2.getSsid()));
+ if (isAtLeastR()) {
+ assertTrue(TEST_SSID.equals(nc2.getSsid()));
+ }
nc1.setSSID(DIFFERENT_TEST_SSID);
nc2.set(nc1);
assertEquals(nc1, nc2);
- assertTrue(DIFFERENT_TEST_SSID.equals(nc2.getSsid()));
+ if (isAtLeastR()) {
+ assertTrue(DIFFERENT_TEST_SSID.equals(nc2.getSsid()));
+ }
nc1.setUids(uidRange(10, 13));
nc2.set(nc1); // Overwrites, as opposed to combineCapabilities