Merge "Import translations. DO NOT MERGE"
diff --git a/Android.bp b/Android.bp
index 2318f7b..c899179 100644
--- a/Android.bp
+++ b/Android.bp
@@ -551,17 +551,25 @@
java_library {
name: "framework-annotation-proc",
- srcs: [":framework-all-sources"],
+ srcs: [
+ ":framework-all-sources",
+ "core/java/**/*.logtags",
+ ],
+ sdk_version: "core_platform",
libs: [
"app-compat-annotations",
+ "ext",
+ "icing-java-proto-lite",
"unsupportedappusage",
],
+
installable: false,
plugins: [
"unsupportedappusage-annotation-processor",
"compat-changeid-annotation-processor",
],
static_libs: [
+ "framework-internal-utils",
"exoplayer2-extractor",
"android.hardware.wifi-V1.0-java-constants",
]
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobHandle.java b/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
index ee0ee98..f110b36 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
@@ -214,12 +214,16 @@
}
/** @hide */
- public void dump(IndentingPrintWriter fout) {
- fout.println("algo: " + algorithm);
- fout.println("digest: " + Base64.encodeToString(digest, Base64.NO_WRAP));
- fout.println("label: " + label);
- fout.println("expiryMs: " + expiryTimeMillis);
- fout.println("tag: " + tag);
+ public void dump(IndentingPrintWriter fout, boolean dumpFull) {
+ if (dumpFull) {
+ fout.println("algo: " + algorithm);
+ fout.println("digest: " + (dumpFull ? encodeDigest() : safeDigest()));
+ fout.println("label: " + label);
+ fout.println("expiryMs: " + expiryTimeMillis);
+ fout.println("tag: " + tag);
+ } else {
+ fout.println(toString());
+ }
}
/** @hide */
@@ -233,6 +237,26 @@
Preconditions.checkArgument(tag.length() <= LIMIT_BLOB_TAG_LENGTH, "tag too long");
}
+ @Override
+ public String toString() {
+ return "BlobHandle {"
+ + "algo:" + algorithm + ","
+ + "digest:" + safeDigest() + ","
+ + "label:" + label + ","
+ + "expiryMs:" + expiryTimeMillis + ","
+ + "tag:" + tag
+ + "}";
+ }
+
+ private String safeDigest() {
+ final String digestStr = encodeDigest();
+ return digestStr.substring(0, 2) + ".." + digestStr.substring(digestStr.length() - 2);
+ }
+
+ private String encodeDigest() {
+ return Base64.encodeToString(digest, Base64.NO_WRAP);
+ }
+
public static final @NonNull Creator<BlobHandle> CREATOR = new Creator<BlobHandle>() {
@Override
public @NonNull BlobHandle createFromParcel(@NonNull Parcel source) {
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
index 7052d60..aba3e8c 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
@@ -48,6 +48,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.XmlUtils;
+import com.android.server.blob.BlobStoreManagerService.DumpArgs;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -240,10 +241,10 @@
return revocableFd.getRevocableFileDescriptor();
}
- void dump(IndentingPrintWriter fout) {
+ void dump(IndentingPrintWriter fout, DumpArgs dumpArgs) {
fout.println("blobHandle:");
fout.increaseIndent();
- blobHandle.dump(fout);
+ blobHandle.dump(fout, dumpArgs.shouldDumpFull());
fout.decreaseIndent();
fout.println("Committers:");
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
index 215e9c1..13f095e 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -40,6 +40,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.blob.BlobHandle;
import android.app.blob.IBlobStoreManager;
import android.app.blob.IBlobStoreSession;
@@ -69,6 +70,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
@@ -121,6 +123,9 @@
private PackageManagerInternal mPackageManagerInternal;
+ private final Runnable mSaveBlobsInfoRunnable = this::writeBlobsInfo;
+ private final Runnable mSaveSessionsRunnable = this::writeBlobSessions;
+
public BlobStoreManagerService(Context context) {
this(context, new Injector());
}
@@ -533,9 +538,9 @@
}
private void writeBlobsInfoAsync() {
- mHandler.post(PooledLambda.obtainRunnable(
- BlobStoreManagerService::writeBlobsInfo,
- BlobStoreManagerService.this).recycleOnUse());
+ if (!mHandler.hasCallbacks(mSaveBlobsInfoRunnable)) {
+ mHandler.post(mSaveBlobsInfoRunnable);
+ }
}
private void writeBlobSessions() {
@@ -549,9 +554,9 @@
}
private void writeBlobSessionsAsync() {
- mHandler.post(PooledLambda.obtainRunnable(
- BlobStoreManagerService::writeBlobSessions,
- BlobStoreManagerService.this).recycleOnUse());
+ if (!mHandler.hasCallbacks(mSaveSessionsRunnable)) {
+ mHandler.post(mSaveSessionsRunnable);
+ }
}
private int getPackageUid(String packageName, int userId) {
@@ -597,6 +602,7 @@
return new AtomicFile(file, "blobs_index" /* commitLogTag */);
}
+ @VisibleForTesting
void handlePackageRemoved(String packageName, int uid) {
synchronized (mBlobsLock) {
// Clean up any pending sessions
@@ -659,6 +665,80 @@
}
}
+ void runClearAllSessions(@UserIdInt int userId) {
+ synchronized (mBlobsLock) {
+ if (userId == UserHandle.USER_ALL) {
+ mSessions.clear();
+ } else {
+ mSessions.remove(userId);
+ }
+ writeBlobSessionsAsync();
+ }
+ }
+
+ void runClearAllBlobs(@UserIdInt int userId) {
+ synchronized (mBlobsLock) {
+ if (userId == UserHandle.USER_ALL) {
+ mBlobsMap.clear();
+ } else {
+ mBlobsMap.remove(userId);
+ }
+ writeBlobsInfoAsync();
+ }
+ }
+
+ @GuardedBy("mBlobsLock")
+ private void dumpSessionsLocked(IndentingPrintWriter fout, DumpArgs dumpArgs) {
+ for (int i = 0, userCount = mSessions.size(); i < userCount; ++i) {
+ final int userId = mSessions.keyAt(i);
+ if (!dumpArgs.shouldDumpUser(userId)) {
+ continue;
+ }
+ final LongSparseArray<BlobStoreSession> userSessions = mSessions.valueAt(i);
+ fout.println("List of sessions in user #"
+ + userId + " (" + userSessions.size() + "):");
+ fout.increaseIndent();
+ for (int j = 0, sessionsCount = userSessions.size(); j < sessionsCount; ++j) {
+ final long sessionId = userSessions.keyAt(j);
+ final BlobStoreSession session = userSessions.valueAt(j);
+ if (!dumpArgs.shouldDumpSession(session.getOwnerPackageName(),
+ session.getOwnerUid(), session.getSessionId())) {
+ continue;
+ }
+ fout.println("Session #" + sessionId);
+ fout.increaseIndent();
+ session.dump(fout, dumpArgs);
+ fout.decreaseIndent();
+ }
+ fout.decreaseIndent();
+ }
+ }
+
+ @GuardedBy("mBlobsLock")
+ private void dumpBlobsLocked(IndentingPrintWriter fout, DumpArgs dumpArgs) {
+ for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) {
+ final int userId = mBlobsMap.keyAt(i);
+ if (!dumpArgs.shouldDumpUser(userId)) {
+ continue;
+ }
+ final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i);
+ fout.println("List of blobs in user #"
+ + userId + " (" + userBlobs.size() + "):");
+ fout.increaseIndent();
+ for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) {
+ final BlobMetadata blobMetadata = userBlobs.valueAt(j);
+ if (!dumpArgs.shouldDumpBlob(blobMetadata.blobId)) {
+ continue;
+ }
+ fout.println("Blob #" + blobMetadata.blobId);
+ fout.increaseIndent();
+ blobMetadata.dump(fout, dumpArgs);
+ fout.decreaseIndent();
+ }
+ fout.decreaseIndent();
+ }
+ }
+
private class PackageChangedReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
@@ -808,48 +888,158 @@
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer,
@Nullable String[] args) {
// TODO: add proto-based version of this.
- if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return;
+ if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, writer)) return;
+
+ final DumpArgs dumpArgs = DumpArgs.parse(args);
final IndentingPrintWriter fout = new IndentingPrintWriter(writer, " ");
synchronized (mBlobsLock) {
fout.println("mCurrentMaxSessionId: " + mCurrentMaxSessionId);
fout.println();
- for (int i = 0, userCount = mSessions.size(); i < userCount; ++i) {
- final int userId = mSessions.keyAt(i);
- final LongSparseArray<BlobStoreSession> userSessions = mSessions.valueAt(i);
- fout.println("List of sessions in user #"
- + userId + " (" + userSessions.size() + "):");
- fout.increaseIndent();
- for (int j = 0, sessionsCount = userSessions.size(); j < sessionsCount; ++j) {
- final long sessionId = userSessions.keyAt(j);
- final BlobStoreSession session = userSessions.valueAt(j);
- fout.println("Session #" + sessionId);
- fout.increaseIndent();
- session.dump(fout);
- fout.decreaseIndent();
- }
- fout.decreaseIndent();
+
+ if (dumpArgs.shouldDumpSessions()) {
+ dumpSessionsLocked(fout, dumpArgs);
+ fout.println();
}
-
- fout.print("\n\n");
-
- for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) {
- final int userId = mBlobsMap.keyAt(i);
- final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i);
- fout.println("List of blobs in user #"
- + userId + " (" + userBlobs.size() + "):");
- fout.increaseIndent();
- for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) {
- final BlobMetadata blobMetadata = userBlobs.valueAt(j);
- fout.println("Blob #" + blobMetadata.blobId);
- fout.increaseIndent();
- blobMetadata.dump(fout);
- fout.decreaseIndent();
- }
- fout.decreaseIndent();
+ if (dumpArgs.shouldDumpBlobs()) {
+ dumpBlobsLocked(fout, dumpArgs);
+ fout.println();
}
}
}
+
+ @Override
+ public int handleShellCommand(@NonNull ParcelFileDescriptor in,
+ @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,
+ @NonNull String[] args) {
+ return (new BlobStoreManagerShellCommand(BlobStoreManagerService.this)).exec(this,
+ in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(), args);
+ }
+ }
+
+ static final class DumpArgs {
+ private boolean mDumpFull;
+ private final ArrayList<String> mDumpPackages = new ArrayList<>();
+ private final ArrayList<Integer> mDumpUids = new ArrayList<>();
+ private final ArrayList<Integer> mDumpUserIds = new ArrayList<>();
+ private final ArrayList<Long> mDumpBlobIds = new ArrayList<>();
+ private boolean mDumpOnlySelectedSections;
+ private boolean mDumpSessions;
+ private boolean mDumpBlobs;
+
+ public boolean shouldDumpSession(String packageName, int uid, long blobId) {
+ if (!CollectionUtils.isEmpty(mDumpPackages)
+ && mDumpPackages.indexOf(packageName) < 0) {
+ return false;
+ }
+ if (!CollectionUtils.isEmpty(mDumpUids)
+ && mDumpUids.indexOf(uid) < 0) {
+ return false;
+ }
+ if (!CollectionUtils.isEmpty(mDumpBlobIds)
+ && mDumpBlobIds.indexOf(blobId) < 0) {
+ return false;
+ }
+ return true;
+ }
+
+ public boolean shouldDumpSessions() {
+ if (!mDumpOnlySelectedSections) {
+ return true;
+ }
+ return mDumpSessions;
+ }
+
+ public boolean shouldDumpBlobs() {
+ if (!mDumpOnlySelectedSections) {
+ return true;
+ }
+ return mDumpBlobs;
+ }
+
+ public boolean shouldDumpBlob(long blobId) {
+ return CollectionUtils.isEmpty(mDumpBlobIds)
+ || mDumpBlobIds.indexOf(blobId) >= 0;
+ }
+
+ public boolean shouldDumpFull() {
+ return mDumpFull;
+ }
+
+ public boolean shouldDumpUser(int userId) {
+ return CollectionUtils.isEmpty(mDumpUserIds)
+ || mDumpUserIds.indexOf(userId) >= 0;
+ }
+
+ private DumpArgs() {}
+
+ public static DumpArgs parse(String[] args) {
+ final DumpArgs dumpArgs = new DumpArgs();
+ if (args == null) {
+ return dumpArgs;
+ }
+
+ for (int i = 0; i < args.length; ++i) {
+ final String opt = args[i];
+ if ("--full".equals(opt) || "-f".equals(opt)) {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
+ dumpArgs.mDumpFull = true;
+ }
+ } else if ("--sessions".equals(opt)) {
+ dumpArgs.mDumpOnlySelectedSections = true;
+ dumpArgs.mDumpSessions = true;
+ } else if ("--blobs".equals(opt)) {
+ dumpArgs.mDumpOnlySelectedSections = true;
+ dumpArgs.mDumpBlobs = true;
+ } else if ("--package".equals(opt) || "-p".equals(opt)) {
+ dumpArgs.mDumpPackages.add(getStringArgRequired(args, ++i, "packageName"));
+ } else if ("--uid".equals(opt) || "-u".equals(opt)) {
+ dumpArgs.mDumpUids.add(getIntArgRequired(args, ++i, "uid"));
+ } else if ("--user".equals(opt)) {
+ dumpArgs.mDumpUserIds.add(getIntArgRequired(args, ++i, "userId"));
+ } else if ("--blob".equals(opt) || "-b".equals(opt)) {
+ dumpArgs.mDumpBlobIds.add(getLongArgRequired(args, ++i, "blobId"));
+ } else {
+ // Everything else is assumed to be blob ids.
+ dumpArgs.mDumpBlobIds.add(getLongArgRequired(args, i, "blobId"));
+ }
+ }
+ return dumpArgs;
+ }
+
+ private static String getStringArgRequired(String[] args, int index, String argName) {
+ if (index >= args.length) {
+ throw new IllegalArgumentException("Missing " + argName);
+ }
+ return args[index];
+ }
+
+ private static int getIntArgRequired(String[] args, int index, String argName) {
+ if (index >= args.length) {
+ throw new IllegalArgumentException("Missing " + argName);
+ }
+ final int value;
+ try {
+ value = Integer.parseInt(args[index]);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Invalid " + argName + ": " + args[index]);
+ }
+ return value;
+ }
+
+ private static long getLongArgRequired(String[] args, int index, String argName) {
+ if (index >= args.length) {
+ throw new IllegalArgumentException("Missing " + argName);
+ }
+ final long value;
+ try {
+ value = Long.parseLong(args[index]);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Invalid " + argName + ": " + args[index]);
+ }
+ return value;
+ }
}
@VisibleForTesting
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerShellCommand.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerShellCommand.java
new file mode 100644
index 0000000..3ac30f8
--- /dev/null
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerShellCommand.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.blob;
+
+import android.os.ShellCommand;
+import android.os.UserHandle;
+
+import java.io.PrintWriter;
+
+class BlobStoreManagerShellCommand extends ShellCommand {
+
+ private final BlobStoreManagerService mService;
+
+ BlobStoreManagerShellCommand(BlobStoreManagerService blobStoreManagerService) {
+ mService = blobStoreManagerService;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(null);
+ }
+ final PrintWriter pw = getOutPrintWriter();
+ switch (cmd) {
+ case "clear-all-sessions":
+ return runClearAllSessions(pw);
+ case "clear-all-blobs":
+ return runClearAllBlobs(pw);
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ }
+
+ private int runClearAllSessions(PrintWriter pw) {
+ final ParsedArgs args = new ParsedArgs();
+ args.userId = UserHandle.USER_ALL;
+
+ if (parseOptions(pw, args) < 0) {
+ return -1;
+ }
+
+ mService.runClearAllSessions(args.userId);
+ return 0;
+ }
+
+ private int runClearAllBlobs(PrintWriter pw) {
+ final ParsedArgs args = new ParsedArgs();
+ args.userId = UserHandle.USER_ALL;
+
+ if (parseOptions(pw, args) < 0) {
+ return -1;
+ }
+
+ mService.runClearAllBlobs(args.userId);
+ return 0;
+ }
+
+ @Override
+ public void onHelp() {
+ final PrintWriter pw = getOutPrintWriter();
+ pw.println("BlobStore service (blob_store) commands:");
+ pw.println("help");
+ pw.println(" Print this help text.");
+ pw.println();
+ pw.println("clear-all-sessions [-u | --user USER_ID]");
+ pw.println(" Remove all sessions.");
+ pw.println(" Options:");
+ pw.println(" -u or --user: specify which user's sessions to be removed;");
+ pw.println(" If not specified, sessions in all users are removed.");
+ pw.println();
+ pw.println("clear-all-blobs [-u | --user USER_ID]");
+ pw.println(" Remove all blobs.");
+ pw.println(" Options:");
+ pw.println(" -u or --user: specify which user's blobs to be removed;");
+ pw.println(" If not specified, blobs in all users are removed.");
+ pw.println();
+ }
+
+ private int parseOptions(PrintWriter pw, ParsedArgs args) {
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-u":
+ case "--user":
+ args.userId = Integer.parseInt(getNextArgRequired());
+ break;
+ default:
+ pw.println("Error: unknown option '" + opt + "'");
+ return -1;
+ }
+ }
+ return 0;
+ }
+
+ private static class ParsedArgs {
+ public int userId;
+ }
+}
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
index b457396..54a2997 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
@@ -51,6 +51,7 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
+import com.android.server.blob.BlobStoreManagerService.DumpArgs;
import com.android.server.blob.BlobStoreManagerService.SessionStateChangeListener;
import org.xmlpull.v1.XmlPullParser;
@@ -453,7 +454,7 @@
}
}
- void dump(IndentingPrintWriter fout) {
+ void dump(IndentingPrintWriter fout, DumpArgs dumpArgs) {
synchronized (mSessionLock) {
fout.println("state: " + stateToString(mState));
fout.println("ownerUid: " + mOwnerUid);
@@ -461,7 +462,7 @@
fout.println("blobHandle:");
fout.increaseIndent();
- mBlobHandle.dump(fout);
+ mBlobHandle.dump(fout, dumpArgs.shouldDumpFull());
fout.decreaseIndent();
fout.println("accessMode:");
diff --git a/api/current.txt b/api/current.txt
index 9f1b8c6..5ac58d0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2875,9 +2875,17 @@
method public boolean takeScreenshot(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.graphics.Bitmap>);
field public static final int GESTURE_2_FINGER_DOUBLE_TAP = 20; // 0x14
field public static final int GESTURE_2_FINGER_SINGLE_TAP = 19; // 0x13
+ field public static final int GESTURE_2_FINGER_SWIPE_DOWN = 26; // 0x1a
+ field public static final int GESTURE_2_FINGER_SWIPE_LEFT = 27; // 0x1b
+ field public static final int GESTURE_2_FINGER_SWIPE_RIGHT = 28; // 0x1c
+ field public static final int GESTURE_2_FINGER_SWIPE_UP = 25; // 0x19
field public static final int GESTURE_2_FINGER_TRIPLE_TAP = 21; // 0x15
field public static final int GESTURE_3_FINGER_DOUBLE_TAP = 23; // 0x17
field public static final int GESTURE_3_FINGER_SINGLE_TAP = 22; // 0x16
+ field public static final int GESTURE_3_FINGER_SWIPE_DOWN = 30; // 0x1e
+ field public static final int GESTURE_3_FINGER_SWIPE_LEFT = 31; // 0x1f
+ field public static final int GESTURE_3_FINGER_SWIPE_RIGHT = 32; // 0x20
+ field public static final int GESTURE_3_FINGER_SWIPE_UP = 29; // 0x1d
field public static final int GESTURE_3_FINGER_TRIPLE_TAP = 24; // 0x18
field public static final int GESTURE_DOUBLE_TAP = 17; // 0x11
field public static final int GESTURE_DOUBLE_TAP_AND_HOLD = 18; // 0x12
@@ -26729,6 +26737,7 @@
method public int getVolume();
method public int getVolumeHandling();
method public int getVolumeMax();
+ method public boolean isSystemRoute();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field public static final int CONNECTION_STATE_CONNECTED = 2; // 0x2
field public static final int CONNECTION_STATE_CONNECTING = 1; // 0x1
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 1a2cb74..d11801b 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -6,7 +6,7 @@
method public void addDebugInfo(@NonNull java.util.List<java.lang.String>);
method public int describeContents();
method @NonNull public java.util.List<java.lang.String> getDebugInfo();
- method public int getPhoneId();
+ method public int getSlotIndex();
method @Nullable public android.os.TimestampedValue<java.lang.Long> getUtcTime();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.timedetector.PhoneTimeSuggestion> CREATOR;
@@ -34,8 +34,8 @@
method public int describeContents();
method @NonNull public java.util.List<java.lang.String> getDebugInfo();
method public int getMatchType();
- method public int getPhoneId();
method public int getQuality();
+ method public int getSlotIndex();
method @Nullable public String getZoneId();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.timezonedetector.PhoneTimeZoneSuggestion> CREATOR;
diff --git a/api/system-current.txt b/api/system-current.txt
index daad703..ff2d308 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -9169,7 +9169,12 @@
method @WorkerThread public void allocateBytes(java.io.FileDescriptor, long, @RequiresPermission int) throws java.io.IOException;
method @WorkerThread public long getAllocatableBytes(@NonNull java.util.UUID, @RequiresPermission int) throws java.io.IOException;
method public static boolean hasIsolatedStorage();
+ method public void updateExternalStorageFileQuotaType(@NonNull java.io.File, int) throws java.io.IOException;
field @RequiresPermission(android.Manifest.permission.ALLOCATE_AGGRESSIVE) public static final int FLAG_ALLOCATE_AGGRESSIVE = 1; // 0x1
+ field public static final int QUOTA_TYPE_MEDIA_AUDIO = 2; // 0x2
+ field public static final int QUOTA_TYPE_MEDIA_IMAGE = 1; // 0x1
+ field public static final int QUOTA_TYPE_MEDIA_NONE = 0; // 0x0
+ field public static final int QUOTA_TYPE_MEDIA_VIDEO = 3; // 0x3
}
public final class StorageVolume implements android.os.Parcelable {
@@ -10107,6 +10112,7 @@
ctor public AugmentedAutofillService();
method protected final void dump(java.io.FileDescriptor, java.io.PrintWriter, String[]);
method protected void dump(@NonNull java.io.PrintWriter, @NonNull String[]);
+ method @Nullable public final android.service.autofill.FillEventHistory getFillEventHistory();
method public void onConnected();
method public void onDisconnected();
method public void onFillRequest(@NonNull android.service.autofill.augmented.FillRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.augmented.FillController, @NonNull android.service.autofill.augmented.FillCallback);
@@ -10136,6 +10142,7 @@
public static final class FillResponse.Builder {
ctor public FillResponse.Builder();
method @NonNull public android.service.autofill.augmented.FillResponse build();
+ method @NonNull public android.service.autofill.augmented.FillResponse.Builder setClientState(@Nullable android.os.Bundle);
method @NonNull public android.service.autofill.augmented.FillResponse.Builder setFillWindow(@Nullable android.service.autofill.augmented.FillWindow);
method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineSuggestions(@Nullable java.util.List<android.service.autofill.Dataset>);
}
diff --git a/api/test-current.txt b/api/test-current.txt
index e604806..fda3ab4 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3104,6 +3104,7 @@
ctor public AugmentedAutofillService();
method protected final void dump(java.io.FileDescriptor, java.io.PrintWriter, String[]);
method protected void dump(@NonNull java.io.PrintWriter, @NonNull String[]);
+ method @Nullable public final android.service.autofill.FillEventHistory getFillEventHistory();
method public void onConnected();
method public void onDisconnected();
method public void onFillRequest(@NonNull android.service.autofill.augmented.FillRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.augmented.FillController, @NonNull android.service.autofill.augmented.FillCallback);
@@ -3133,6 +3134,7 @@
public static final class FillResponse.Builder {
ctor public FillResponse.Builder();
method @NonNull public android.service.autofill.augmented.FillResponse build();
+ method @NonNull public android.service.autofill.augmented.FillResponse.Builder setClientState(@Nullable android.os.Bundle);
method @NonNull public android.service.autofill.augmented.FillResponse.Builder setFillWindow(@Nullable android.service.autofill.augmented.FillWindow);
method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineSuggestions(@Nullable java.util.List<android.service.autofill.Dataset>);
}
diff --git a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
index 47fc7e1..9cf1de9 100644
--- a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
+++ b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
@@ -19,9 +19,17 @@
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_DOWN;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_LEFT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_RIGHT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_UP;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_DOWN;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_LEFT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_RIGHT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_UP;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_TRIPLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_DOUBLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_DOUBLE_TAP_AND_HOLD;
@@ -89,7 +97,15 @@
GESTURE_SWIPE_RIGHT,
GESTURE_SWIPE_RIGHT_AND_UP,
GESTURE_SWIPE_RIGHT_AND_LEFT,
- GESTURE_SWIPE_RIGHT_AND_DOWN
+ GESTURE_SWIPE_RIGHT_AND_DOWN,
+ GESTURE_2_FINGER_SWIPE_DOWN,
+ GESTURE_2_FINGER_SWIPE_LEFT,
+ GESTURE_2_FINGER_SWIPE_RIGHT,
+ GESTURE_2_FINGER_SWIPE_UP,
+ GESTURE_3_FINGER_SWIPE_DOWN,
+ GESTURE_3_FINGER_SWIPE_LEFT,
+ GESTURE_3_FINGER_SWIPE_RIGHT,
+ GESTURE_3_FINGER_SWIPE_UP
})
@Retention(RetentionPolicy.SOURCE)
public @interface GestureId {}
@@ -167,6 +183,14 @@
case GESTURE_SWIPE_UP_AND_LEFT: return "GESTURE_SWIPE_UP_AND_LEFT";
case GESTURE_SWIPE_UP_AND_DOWN: return "GESTURE_SWIPE_UP_AND_DOWN";
case GESTURE_SWIPE_UP_AND_RIGHT: return "GESTURE_SWIPE_UP_AND_RIGHT";
+ case GESTURE_2_FINGER_SWIPE_DOWN: return "GESTURE_2_FINGER_SWIPE_DOWN";
+ case GESTURE_2_FINGER_SWIPE_LEFT: return "GESTURE_2_FINGER_SWIPE_LEFT";
+ case GESTURE_2_FINGER_SWIPE_RIGHT: return "GESTURE_2_FINGER_SWIPE_RIGHT";
+ case GESTURE_2_FINGER_SWIPE_UP: return "GESTURE_2_FINGER_SWIPE_UP";
+ case GESTURE_3_FINGER_SWIPE_DOWN: return "GESTURE_3_FINGER_SWIPE_DOWN";
+ case GESTURE_3_FINGER_SWIPE_LEFT: return "GESTURE_3_FINGER_SWIPE_LEFT";
+ case GESTURE_3_FINGER_SWIPE_RIGHT: return "GESTURE_3_FINGER_SWIPE_RIGHT";
+ case GESTURE_3_FINGER_SWIPE_UP: return "GESTURE_3_FINGER_SWIPE_UP";
default: return Integer.toHexString(eventType);
}
}
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 27cd285..2165fb3 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -349,6 +349,46 @@
public static final int GESTURE_3_FINGER_TRIPLE_TAP = 24;
/**
+ * The user has performed a two-finger swipe up gesture on the touch screen.
+ */
+ public static final int GESTURE_2_FINGER_SWIPE_UP = 25;
+
+ /**
+ * The user has performed a two-finger swipe down gesture on the touch screen.
+ */
+ public static final int GESTURE_2_FINGER_SWIPE_DOWN = 26;
+
+ /**
+ * The user has performed a two-finger swipe left gesture on the touch screen.
+ */
+ public static final int GESTURE_2_FINGER_SWIPE_LEFT = 27;
+
+ /**
+ * The user has performed a two-finger swipe right gesture on the touch screen.
+ */
+ public static final int GESTURE_2_FINGER_SWIPE_RIGHT = 28;
+
+ /**
+ * The user has performed a three-finger swipe up gesture on the touch screen.
+ */
+ public static final int GESTURE_3_FINGER_SWIPE_UP = 29;
+
+ /**
+ * The user has performed a three-finger swipe down gesture on the touch screen.
+ */
+ public static final int GESTURE_3_FINGER_SWIPE_DOWN = 30;
+
+ /**
+ * The user has performed a three-finger swipe left gesture on the touch screen.
+ */
+ public static final int GESTURE_3_FINGER_SWIPE_LEFT = 31;
+
+ /**
+ * The user has performed a three-finger swipe right gesture on the touch screen.
+ */
+ public static final int GESTURE_3_FINGER_SWIPE_RIGHT = 32;
+
+ /**
* The {@link Intent} that must be declared as handled by the service.
*/
public static final String SERVICE_INTERFACE =
diff --git a/core/java/android/app/AppCompatCallbacks.java b/core/java/android/app/AppCompatCallbacks.java
index 19d158d..28a21f7 100644
--- a/core/java/android/app/AppCompatCallbacks.java
+++ b/core/java/android/app/AppCompatCallbacks.java
@@ -18,7 +18,6 @@
import android.compat.Compatibility;
import android.os.Process;
-import android.util.StatsLog;
import com.android.internal.compat.ChangeReporter;
@@ -46,20 +45,20 @@
mDisabledChanges = Arrays.copyOf(disabledChanges, disabledChanges.length);
Arrays.sort(mDisabledChanges);
mChangeReporter = new ChangeReporter(
- StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__APP_PROCESS);
+ ChangeReporter.SOURCE_APP_PROCESS);
}
protected void reportChange(long changeId) {
- reportChange(changeId, StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED);
+ reportChange(changeId, ChangeReporter.STATE_LOGGED);
}
protected boolean isChangeEnabled(long changeId) {
if (Arrays.binarySearch(mDisabledChanges, changeId) < 0) {
// Not present in the disabled array
- reportChange(changeId, StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__ENABLED);
+ reportChange(changeId, ChangeReporter.STATE_ENABLED);
return true;
}
- reportChange(changeId, StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__DISABLED);
+ reportChange(changeId, ChangeReporter.STATE_DISABLED);
return false;
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index c09aa1f..71cb4a4 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -618,36 +618,22 @@
return hasSystemFeature(name, 0);
}
- private boolean hasSystemFeatureUncached(String name, int version) {
- try {
- return mPM.hasSystemFeature(name, version);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- // Make this cache relatively large. There are many system features and
- // none are ever invalidated. MPTS tests suggests that the cache should
- // hold at least 150 entries.
- private static final int SYS_FEATURE_CACHE_SIZE = 256;
- private static final String CACHE_KEY_SYS_FEATURE_PROPERTY = "cache_key.has_system_feature";
-
- private class SystemFeatureQuery {
+ private class HasSystemFeatureQuery {
public final String name;
public final int version;
- public SystemFeatureQuery(String n, int v) {
+ public HasSystemFeatureQuery(String n, int v) {
name = n;
version = v;
}
@Override
public String toString() {
- return String.format("SystemFeatureQuery(name=\"%s\", version=%d)",
+ return String.format("HasSystemFeatureQuery(name=\"%s\", version=%d)",
name, version);
}
@Override
public boolean equals(Object o) {
- if (o instanceof SystemFeatureQuery) {
- SystemFeatureQuery r = (SystemFeatureQuery) o;
+ if (o instanceof HasSystemFeatureQuery) {
+ HasSystemFeatureQuery r = (HasSystemFeatureQuery) o;
return Objects.equals(name, r.name) && version == r.version;
} else {
return false;
@@ -655,33 +641,41 @@
}
@Override
public int hashCode() {
- return Objects.hashCode(name) + version;
+ return Objects.hashCode(name) * 13 + version;
}
}
- private final PropertyInvalidatedCache<SystemFeatureQuery, Boolean> mSysFeatureCache =
- new PropertyInvalidatedCache<SystemFeatureQuery, Boolean>(
- SYS_FEATURE_CACHE_SIZE,
- CACHE_KEY_SYS_FEATURE_PROPERTY) {
+ // Make this cache relatively large. There are many system features and
+ // none are ever invalidated. MPTS tests suggests that the cache should
+ // hold at least 150 entries.
+ private final static PropertyInvalidatedCache<HasSystemFeatureQuery, Boolean>
+ mHasSystemFeatureCache =
+ new PropertyInvalidatedCache<HasSystemFeatureQuery, Boolean>(
+ 256, "cache_key.has_system_feature") {
@Override
- protected Boolean recompute(SystemFeatureQuery query) {
- return hasSystemFeatureUncached(query.name, query.version);
+ protected Boolean recompute(HasSystemFeatureQuery query) {
+ try {
+ return ActivityThread.currentActivityThread().getPackageManager().
+ hasSystemFeature(query.name, query.version);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
};
@Override
public boolean hasSystemFeature(String name, int version) {
- return mSysFeatureCache.query(new SystemFeatureQuery(name, version)).booleanValue();
+ return mHasSystemFeatureCache.query(new HasSystemFeatureQuery(name, version));
}
/** @hide */
- public void disableSysFeatureCache() {
- mSysFeatureCache.disableLocal();
+ public void disableHasSystemFeatureCache() {
+ mHasSystemFeatureCache.disableLocal();
}
/** @hide */
- public static void invalidateSysFeatureCache() {
- PropertyInvalidatedCache.invalidateCache(CACHE_KEY_SYS_FEATURE_PROPERTY);
+ public static void invalidateHasSystemFeatureCache() {
+ mHasSystemFeatureCache.invalidateCache();
}
@Override
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 04d3e39..57cd894 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -750,7 +750,7 @@
if (type != null) {
dirs = Environment.buildPaths(dirs, type);
}
- return ensureExternalDirsExistOrFilter(dirs);
+ return ensureExternalDirsExistOrFilter(dirs, true /* tryCreateInProcess */);
}
}
@@ -765,7 +765,7 @@
public File[] getObbDirs() {
synchronized (mSync) {
File[] dirs = Environment.buildExternalStorageAppObbDirs(getPackageName());
- return ensureExternalDirsExistOrFilter(dirs);
+ return ensureExternalDirsExistOrFilter(dirs, true /* tryCreateInProcess */);
}
}
@@ -809,7 +809,10 @@
public File[] getExternalCacheDirs() {
synchronized (mSync) {
File[] dirs = Environment.buildExternalStorageAppCacheDirs(getPackageName());
- return ensureExternalDirsExistOrFilter(dirs);
+ // We don't try to create cache directories in-process, because they need special
+ // setup for accurate quota tracking. This ensures the cache dirs are always
+ // created through StorageManagerService.
+ return ensureExternalDirsExistOrFilter(dirs, false /* tryCreateInProcess */);
}
}
@@ -817,7 +820,7 @@
public File[] getExternalMediaDirs() {
synchronized (mSync) {
File[] dirs = Environment.buildExternalStorageAppMediaDirs(getPackageName());
- return ensureExternalDirsExistOrFilter(dirs);
+ return ensureExternalDirsExistOrFilter(dirs, true /* tryCreateInProcess */);
}
}
@@ -2804,24 +2807,24 @@
* Ensure that given directories exist, trying to create them if missing. If
* unable to create, they are filtered by replacing with {@code null}.
*/
- private File[] ensureExternalDirsExistOrFilter(File[] dirs) {
+ private File[] ensureExternalDirsExistOrFilter(File[] dirs, boolean tryCreateInProcess) {
final StorageManager sm = getSystemService(StorageManager.class);
final File[] result = new File[dirs.length];
for (int i = 0; i < dirs.length; i++) {
File dir = dirs[i];
if (!dir.exists()) {
- if (!dir.mkdirs()) {
- // recheck existence in case of cross-process race
- if (!dir.exists()) {
- // Failing to mkdir() may be okay, since we might not have
- // enough permissions; ask vold to create on our behalf.
- try {
+ try {
+ if (!tryCreateInProcess || !dir.mkdirs()) {
+ // recheck existence in case of cross-process race
+ if (!dir.exists()) {
+ // Failing to mkdir() may be okay, since we might not have
+ // enough permissions; ask vold to create on our behalf.
sm.mkdirs(dir);
- } catch (Exception e) {
- Log.w(TAG, "Failed to ensure " + dir + ": " + e);
- dir = null;
}
}
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to ensure " + dir + ": " + e);
+ dir = null;
}
}
result[i] = dir;
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index f71d78b..3676a9b 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -328,7 +328,7 @@
* modified by the user and the only way of resetting the device is via factory reset.
*
* <p>From version {@link android.os.Build.VERSION_CODES#Q}, the admin app can choose
- * whether to set up a fully managed device or a work profile. For the admin app to support
+ * whether to set up a fully managed device or a managed profile. For the admin app to support
* this, it must have an activity with intent filter {@link #ACTION_GET_PROVISIONING_MODE} and
* another one with intent filter {@link #ACTION_ADMIN_POLICY_COMPLIANCE}. For example:
* <pre>
@@ -1781,12 +1781,13 @@
/**
* Grants access to selection of KeyChain certificates on behalf of requesting apps.
* Once granted the app will start receiving
- * DelegatedAdminReceiver.onChoosePrivateKeyAlias. The caller (PO/DO) will
+ * {@link DelegatedAdminReceiver#onChoosePrivateKeyAlias}. The caller (PO/DO) will
* no longer receive {@link DeviceAdminReceiver#onChoosePrivateKeyAlias}.
* There can be at most one app that has this delegation.
* If another app already had delegated certificate selection access,
* it will lose the delegation when a new app is delegated.
- *
+ * <p> The delegaetd app can also call {@link #grantKeyPairToApp} and
+ * {@link #revokeKeyPairFromApp} to directly grant KeyCain keys to other apps.
* <p> Can be granted by Device Owner or Profile Owner.
*/
public static final String DELEGATION_CERT_SELECTION = "delegation-cert-selection";
@@ -4313,7 +4314,7 @@
* additionally call this method on the parent instance.
* Calling this method on the parent {@link DevicePolicyManager} instance would wipe the
* entire device, while calling it on the current profile instance would relinquish the device
- * for personal use, removing the work profile and all policies set by the profile owner.
+ * for personal use, removing the managed profile and all policies set by the profile owner.
*
* @param flags Bit mask of additional options: currently supported flags are
* {@link #WIPE_EXTERNAL_STORAGE}, {@link #WIPE_RESET_PROTECTION_DATA},
@@ -4339,7 +4340,7 @@
* additionally call this method on the parent instance.
* Calling this method on the parent {@link DevicePolicyManager} instance would wipe the
* entire device, while calling it on the current profile instance would relinquish the device
- * for personal use, removing the work profile and all policies set by the profile owner.
+ * for personal use, removing the managed profile and all policies set by the profile owner.
*
* @param flags Bit mask of additional options: currently supported flags are
* {@link #WIPE_EXTERNAL_STORAGE}, {@link #WIPE_RESET_PROTECTION_DATA} and
@@ -11584,12 +11585,14 @@
* #setCrossProfilePackages(ComponentName, Set)}.</li>
* <li>The default package names set by the OEM that are allowed to request user consent for
* cross-profile communication without being explicitly enabled by the admin, via
- * {@link com.android.internal.R.array#cross_profile_apps}</li>
+ * {@link com.android.internal.R.array#cross_profile_apps} and
+ * {@link com.android.internal.R.array#vendor_cross_profile_apps}.</li>
* </ul>
*
* @return the combined set of whitelisted package names set via
- * {@link #setCrossProfilePackages(ComponentName, Set)} and
- * {@link com.android.internal.R.array#cross_profile_apps}
+ * {@link #setCrossProfilePackages(ComponentName, Set)},
+ * {@link com.android.internal.R.array#cross_profile_apps},
+ * and {@link com.android.internal.R.array#vendor_cross_profile_apps}.
*
* @hide
*/
@@ -11599,7 +11602,7 @@
permission.INTERACT_ACROSS_PROFILES
})
public @NonNull Set<String> getAllCrossProfilePackages() {
- throwIfParentInstance("getDefaultCrossProfilePackages");
+ throwIfParentInstance("getAllCrossProfilePackages");
if (mService != null) {
try {
return new ArraySet<>(mService.getAllCrossProfilePackages());
@@ -11611,6 +11614,26 @@
}
/**
+ * Returns the default package names set by the OEM that are allowed to request user consent for
+ * cross-profile communication without being explicitly enabled by the admin, via
+ * {@link com.android.internal.R.array#cross_profile_apps} and
+ * {@link com.android.internal.R.array#vendor_cross_profile_apps}.
+ *
+ * @hide
+ */
+ public @NonNull Set<String> getDefaultCrossProfilePackages() {
+ throwIfParentInstance("getDefaultCrossProfilePackages");
+ if (mService != null) {
+ try {
+ return new ArraySet<>(mService.getDefaultCrossProfilePackages());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return Collections.emptySet();
+ }
+
+ /**
* Returns whether the device is being used as a managed kiosk. These requirements are as
* follows:
* <ul>
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 7fd0ae4..d2672eb 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -457,6 +457,7 @@
List<String> getCrossProfilePackages(in ComponentName admin);
List<String> getAllCrossProfilePackages();
+ List<String> getDefaultCrossProfilePackages();
boolean isManagedKiosk();
boolean isUnattendedManagedKiosk();
diff --git a/core/java/android/app/timedetector/PhoneTimeSuggestion.java b/core/java/android/app/timedetector/PhoneTimeSuggestion.java
index bd649f8..eab8838 100644
--- a/core/java/android/app/timedetector/PhoneTimeSuggestion.java
+++ b/core/java/android/app/timedetector/PhoneTimeSuggestion.java
@@ -57,19 +57,19 @@
}
};
- private final int mPhoneId;
+ private final int mSlotIndex;
@Nullable private final TimestampedValue<Long> mUtcTime;
@Nullable private ArrayList<String> mDebugInfo;
private PhoneTimeSuggestion(Builder builder) {
- mPhoneId = builder.mPhoneId;
+ mSlotIndex = builder.mSlotIndex;
mUtcTime = builder.mUtcTime;
mDebugInfo = builder.mDebugInfo != null ? new ArrayList<>(builder.mDebugInfo) : null;
}
private static PhoneTimeSuggestion createFromParcel(Parcel in) {
- int phoneId = in.readInt();
- PhoneTimeSuggestion suggestion = new PhoneTimeSuggestion.Builder(phoneId)
+ int slotIndex = in.readInt();
+ PhoneTimeSuggestion suggestion = new PhoneTimeSuggestion.Builder(slotIndex)
.setUtcTime(in.readParcelable(null /* classLoader */))
.build();
@SuppressWarnings("unchecked")
@@ -87,17 +87,17 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(mPhoneId);
+ dest.writeInt(mSlotIndex);
dest.writeParcelable(mUtcTime, 0);
dest.writeList(mDebugInfo);
}
/**
- * Returns an identifier for the source of this suggestion. When a device has several "phones",
- * i.e. sim slots or equivalent, it is used to identify which one.
+ * Returns an identifier for the source of this suggestion. When a device has several sim slots
+ * or equivalent, it is used to identify which one the suggestion is from.
*/
- public int getPhoneId() {
- return mPhoneId;
+ public int getSlotIndex() {
+ return mSlotIndex;
}
/**
@@ -152,19 +152,19 @@
return false;
}
PhoneTimeSuggestion that = (PhoneTimeSuggestion) o;
- return mPhoneId == that.mPhoneId
+ return mSlotIndex == that.mSlotIndex
&& Objects.equals(mUtcTime, that.mUtcTime);
}
@Override
public int hashCode() {
- return Objects.hash(mPhoneId, mUtcTime);
+ return Objects.hash(mSlotIndex, mUtcTime);
}
@Override
public String toString() {
return "PhoneTimeSuggestion{"
- + "mPhoneId='" + mPhoneId + '\''
+ + "mSlotIndex='" + mSlotIndex + '\''
+ ", mUtcTime=" + mUtcTime
+ ", mDebugInfo=" + mDebugInfo
+ '}';
@@ -177,13 +177,13 @@
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final class Builder {
- private final int mPhoneId;
+ private final int mSlotIndex;
@Nullable private TimestampedValue<Long> mUtcTime;
@Nullable private List<String> mDebugInfo;
- /** Creates a builder with the specified {@code phoneId}. */
- public Builder(int phoneId) {
- mPhoneId = phoneId;
+ /** Creates a builder with the specified {@code slotIndex}. */
+ public Builder(int slotIndex) {
+ mSlotIndex = slotIndex;
}
/** Returns the builder for call chaining. */
diff --git a/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java b/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java
index d71ffcb..ebaf951 100644
--- a/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java
+++ b/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java
@@ -66,12 +66,12 @@
/**
* Creates an empty time zone suggestion, i.e. one that will cancel previous suggestions with
- * the same {@code phoneId}.
+ * the same {@code slotIndex}.
*/
@NonNull
public static PhoneTimeZoneSuggestion createEmptySuggestion(
- int phoneId, @NonNull String debugInfo) {
- return new Builder(phoneId).addDebugInfo(debugInfo).build();
+ int slotIndex, @NonNull String debugInfo) {
+ return new Builder(slotIndex).addDebugInfo(debugInfo).build();
}
/** @hide */
@@ -135,7 +135,7 @@
* The ID of the phone this suggestion is associated with. For multiple-sim devices this
* helps to establish source so filtering / stickiness can be implemented.
*/
- private final int mPhoneId;
+ private final int mSlotIndex;
/**
* The suggestion. {@code null} means there is no current suggestion and any previous suggestion
@@ -165,7 +165,7 @@
private List<String> mDebugInfo;
private PhoneTimeZoneSuggestion(Builder builder) {
- mPhoneId = builder.mPhoneId;
+ mSlotIndex = builder.mSlotIndex;
mZoneId = builder.mZoneId;
mMatchType = builder.mMatchType;
mQuality = builder.mQuality;
@@ -175,8 +175,8 @@
@SuppressWarnings("unchecked")
private static PhoneTimeZoneSuggestion createFromParcel(Parcel in) {
// Use the Builder so we get validation during build().
- int phoneId = in.readInt();
- PhoneTimeZoneSuggestion suggestion = new Builder(phoneId)
+ int slotIndex = in.readInt();
+ PhoneTimeZoneSuggestion suggestion = new Builder(slotIndex)
.setZoneId(in.readString())
.setMatchType(in.readInt())
.setQuality(in.readInt())
@@ -190,7 +190,7 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(mPhoneId);
+ dest.writeInt(mSlotIndex);
dest.writeString(mZoneId);
dest.writeInt(mMatchType);
dest.writeInt(mQuality);
@@ -203,11 +203,11 @@
}
/**
- * Returns an identifier for the source of this suggestion. When a device has several "phones",
- * i.e. sim slots or equivalent, it is used to identify which one.
+ * Returns an identifier for the source of this suggestion. When a device has several sim slots
+ * or equivalent, it is used to identify which one the suggestion is from.
*/
- public int getPhoneId() {
- return mPhoneId;
+ public int getSlotIndex() {
+ return mSlotIndex;
}
/**
@@ -282,7 +282,7 @@
return false;
}
PhoneTimeZoneSuggestion that = (PhoneTimeZoneSuggestion) o;
- return mPhoneId == that.mPhoneId
+ return mSlotIndex == that.mSlotIndex
&& mMatchType == that.mMatchType
&& mQuality == that.mQuality
&& Objects.equals(mZoneId, that.mZoneId);
@@ -290,13 +290,13 @@
@Override
public int hashCode() {
- return Objects.hash(mPhoneId, mZoneId, mMatchType, mQuality);
+ return Objects.hash(mSlotIndex, mZoneId, mMatchType, mQuality);
}
@Override
public String toString() {
return "PhoneTimeZoneSuggestion{"
- + "mPhoneId=" + mPhoneId
+ + "mSlotIndex=" + mSlotIndex
+ ", mZoneId='" + mZoneId + '\''
+ ", mMatchType=" + mMatchType
+ ", mQuality=" + mQuality
@@ -311,14 +311,14 @@
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final class Builder {
- private final int mPhoneId;
+ private final int mSlotIndex;
@Nullable private String mZoneId;
@MatchType private int mMatchType;
@Quality private int mQuality;
@Nullable private List<String> mDebugInfo;
- public Builder(int phoneId) {
- mPhoneId = phoneId;
+ public Builder(int slotIndex) {
+ mSlotIndex = slotIndex;
}
/**
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index a385771..7615b87c 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -91,7 +91,7 @@
private boolean mIdle = true;
/** map request IDs to callback/request data */
- private final SparseArray<CaptureCallbackHolder> mCaptureCallbackMap =
+ private SparseArray<CaptureCallbackHolder> mCaptureCallbackMap =
new SparseArray<CaptureCallbackHolder>();
private int mRepeatingRequestId = REQUEST_ID_NONE;
@@ -123,7 +123,7 @@
* An object tracking received frame numbers.
* Updated when receiving callbacks from ICameraDeviceCallbacks.
*/
- private final FrameNumberTracker mFrameNumberTracker = new FrameNumberTracker();
+ private FrameNumberTracker mFrameNumberTracker = new FrameNumberTracker();
private CameraCaptureSessionCore mCurrentSession;
private int mNextSessionId = 0;
@@ -892,6 +892,7 @@
HashSet<Integer> offlineStreamIds = new HashSet<Integer>();
SparseArray<OutputConfiguration> offlineConfiguredOutputs =
new SparseArray<OutputConfiguration>();
+ CameraOfflineSession ret;
synchronized(mInterfaceLock) {
if (mOfflineSessionImpl != null) {
@@ -919,15 +920,20 @@
offlineStreamIds.add(streamId);
}
+ stopRepeating();
mOfflineSessionImpl = new CameraOfflineSessionImpl(mCameraId,
mCharacteristics, executor, listener, offlineConfiguredOutputs,
- mConfiguredInput, mFrameNumberTracker, mCaptureCallbackMap,
+ mConfiguredInput, mConfiguredOutputs, mFrameNumberTracker, mCaptureCallbackMap,
mRequestLastFrameNumbersList);
+ ret = mOfflineSessionImpl;
mOfflineSwitchService = Executors.newSingleThreadExecutor();
mConfiguredOutputs.clear();
mConfiguredInput = new SimpleEntry<Integer, InputConfiguration>(REQUEST_ID_NONE, null);
+ mIdle = true;
+ mCaptureCallbackMap = new SparseArray<CaptureCallbackHolder>();
+ mFrameNumberTracker = new FrameNumberTracker();
mCurrentSession.closeWithoutDraining();
mCurrentSession = null;
@@ -949,11 +955,13 @@
mOfflineSessionImpl.setRemoteSession(remoteOfflineSession);
} catch (CameraAccessException e) {
mOfflineSessionImpl.notifyFailedSwitch();
+ } finally {
+ mOfflineSessionImpl = null;
}
}
});
- return mOfflineSessionImpl;
+ return ret;
}
public boolean supportsOfflineProcessing(Surface surface) {
diff --git a/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java
index 1db377a..1d9d644 100644
--- a/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraOfflineSessionImpl.java
@@ -63,6 +63,7 @@
private SimpleEntry<Integer, InputConfiguration> mOfflineInput =
new SimpleEntry<>(REQUEST_ID_NONE, null);
private SparseArray<OutputConfiguration> mOfflineOutputs = new SparseArray<>();
+ private SparseArray<OutputConfiguration> mConfiguredOutputs = new SparseArray<>();
final Object mInterfaceLock = new Object(); // access from this class and Session only!
@@ -96,6 +97,7 @@
Executor offlineExecutor, CameraOfflineSessionCallback offlineCallback,
SparseArray<OutputConfiguration> offlineOutputs,
SimpleEntry<Integer, InputConfiguration> offlineInput,
+ SparseArray<OutputConfiguration> configuredOutputs,
FrameNumberTracker frameNumberTracker, SparseArray<CaptureCallbackHolder> callbackMap,
List<RequestLastFrameNumbersHolder> frameNumberList) {
if ((cameraId == null) || (characteristics == null)) {
@@ -117,6 +119,7 @@
mOfflineRequestLastFrameNumbersList.addAll(frameNumberList);
mFrameNumberTracker = frameNumberTracker;
mCaptureCallbackMap = callbackMap;
+ mConfiguredOutputs = configuredOutputs;
mOfflineOutputs = offlineOutputs;
mOfflineInput = offlineInput;
mOfflineExecutor = checkNotNull(offlineExecutor, "offline executor must not be null");
@@ -137,9 +140,6 @@
@Override
public void onDeviceError(final int errorCode, CaptureResultExtras resultExtras) {
synchronized(mInterfaceLock) {
- if (mRemoteSession == null) {
- return; // Camera already closed
- }
switch (errorCode) {
case CameraDeviceCallbacks.ERROR_CAMERA_REQUEST:
@@ -177,6 +177,11 @@
@Override
public void onDeviceIdle() {
synchronized(mInterfaceLock) {
+ if (mRemoteSession == null) {
+ Log.v(TAG, "Ignoring idle state notifications during offline switches");
+ return;
+ }
+
Runnable idleDispatch = new Runnable() {
@Override
public void run() {
@@ -203,8 +208,6 @@
final CaptureCallbackHolder holder;
synchronized(mInterfaceLock) {
- if (mRemoteSession == null) return; // Camera already closed
-
// Get the callback for this frame ID, if there is one
holder = CameraOfflineSessionImpl.this.mCaptureCallbackMap.get(requestId);
@@ -269,8 +272,6 @@
long frameNumber = resultExtras.getFrameNumber();
synchronized(mInterfaceLock) {
- if (mRemoteSession == null) return; // Camera already closed
-
// TODO: Handle CameraCharacteristics access from CaptureResult correctly.
result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE,
mCharacteristics.get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE));
@@ -445,8 +446,12 @@
if (errorCode == ERROR_CAMERA_BUFFER) {
// Because 1 stream id could map to multiple surfaces, we need to specify both
// streamId and surfaceId.
- OutputConfiguration config = mOfflineOutputs.get(
- resultExtras.getErrorStreamId());
+ OutputConfiguration config;
+ if ((mRemoteSession == null) && !isClosed()) {
+ config = mConfiguredOutputs.get(resultExtras.getErrorStreamId());
+ } else {
+ config = mOfflineOutputs.get(resultExtras.getErrorStreamId());
+ }
if (config == null) {
Log.v(TAG, String.format(
"Stream %d has been removed. Skipping buffer lost callback",
@@ -538,11 +543,6 @@
final Executor executor;
final CameraCaptureSession.CaptureCallback callback;
synchronized(mInterfaceLock) {
- if (mRemoteSession == null) {
- Log.w(TAG, "Camera closed while checking sequences");
- return;
- }
-
int index = mCaptureCallbackMap.indexOfKey(requestId);
holder = (index >= 0) ?
mCaptureCallbackMap.valueAt(index) : null;
@@ -575,7 +575,7 @@
}
// Call onCaptureSequenceCompleted
- if ((sequenceCompleted) && (callback != null) && (executor == null)) {
+ if ((sequenceCompleted) && (callback != null) && (executor != null)) {
Runnable resultDispatch = new Runnable() {
@Override
public void run() {
@@ -592,7 +592,12 @@
} finally {
Binder.restoreCallingIdentity(ident);
}
+
+ if (mCaptureCallbackMap.size() == 0) {
+ getCallbacks().onDeviceIdle();
+ }
}
+
}
}
@@ -686,9 +691,7 @@
Runnable closeDispatch = new Runnable() {
@Override
public void run() {
- if (!isClosed()) {
- mOfflineCallback.onClosed(CameraOfflineSessionImpl.this);
- }
+ mOfflineCallback.onClosed(CameraOfflineSessionImpl.this);
}
};
diff --git a/core/java/android/hardware/display/AmbientDisplayConfiguration.java b/core/java/android/hardware/display/AmbientDisplayConfiguration.java
index 3e995b6..ece5c28 100644
--- a/core/java/android/hardware/display/AmbientDisplayConfiguration.java
+++ b/core/java/android/hardware/display/AmbientDisplayConfiguration.java
@@ -194,6 +194,11 @@
return !TextUtils.isEmpty(ambientDisplayComponent());
}
+ /** {@hide} */
+ public boolean dozeSuppressed(int user) {
+ return boolSettingDefaultOff(Settings.Secure.SUPPRESS_DOZE, user);
+ }
+
private boolean alwaysOnDisplayAvailable() {
return mContext.getResources().getBoolean(R.bool.config_dozeAlwaysOnDisplayAvailable);
}
diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java
index b13e4b7..140363c 100644
--- a/core/java/android/net/ConnectivityDiagnosticsManager.java
+++ b/core/java/android/net/ConnectivityDiagnosticsManager.java
@@ -25,13 +25,16 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
+import android.os.RemoteException;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Map;
import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
/**
@@ -57,6 +60,11 @@
* </ul>
*/
public class ConnectivityDiagnosticsManager {
+ /** @hide */
+ @VisibleForTesting
+ public static final Map<ConnectivityDiagnosticsCallback, ConnectivityDiagnosticsBinder>
+ sCallbacks = new ConcurrentHashMap<>();
+
private final Context mContext;
private final IConnectivityManager mService;
@@ -631,8 +639,9 @@
/**
* Registers a ConnectivityDiagnosticsCallback with the System.
*
- * <p>Only apps that offer network connectivity to the user are allowed to register callbacks.
- * This includes:
+ * <p>Only apps that offer network connectivity to the user should be registering callbacks.
+ * These are the only apps whose callbacks will be invoked by the system. Apps considered to
+ * meet these conditions include:
*
* <ul>
* <li>Carrier apps with active subscriptions
@@ -640,15 +649,14 @@
* <li>WiFi Suggesters
* </ul>
*
- * <p>Callbacks will be limited to receiving notifications for networks over which apps provide
- * connectivity.
+ * <p>Callbacks registered by apps not meeting the above criteria will not be invoked.
*
* <p>If a registering app loses its relevant permissions, any callbacks it registered will
* silently stop receiving callbacks.
*
- * <p>Each register() call <b>MUST</b> use a unique ConnectivityDiagnosticsCallback instance. If
- * a single instance is registered with multiple NetworkRequests, an IllegalArgumentException
- * will be thrown.
+ * <p>Each register() call <b>MUST</b> use a ConnectivityDiagnosticsCallback instance that is
+ * not currently registered. If a ConnectivityDiagnosticsCallback instance is registered with
+ * multiple NetworkRequests, an IllegalArgumentException will be thrown.
*
* @param request The NetworkRequest that will be used to match with Networks for which
* callbacks will be fired
@@ -657,15 +665,21 @@
* System
* @throws IllegalArgumentException if the same callback instance is registered with multiple
* NetworkRequests
- * @throws SecurityException if the caller does not have appropriate permissions to register a
- * callback
*/
public void registerConnectivityDiagnosticsCallback(
@NonNull NetworkRequest request,
@NonNull Executor e,
@NonNull ConnectivityDiagnosticsCallback callback) {
- // TODO(b/143187964): implement ConnectivityDiagnostics functionality
- throw new UnsupportedOperationException("registerCallback() not supported yet");
+ final ConnectivityDiagnosticsBinder binder = new ConnectivityDiagnosticsBinder(callback, e);
+ if (sCallbacks.putIfAbsent(callback, binder) != null) {
+ throw new IllegalArgumentException("Callback is currently registered");
+ }
+
+ try {
+ mService.registerConnectivityDiagnosticsCallback(binder, request);
+ } catch (RemoteException exception) {
+ exception.rethrowFromSystemServer();
+ }
}
/**
@@ -678,7 +692,15 @@
*/
public void unregisterConnectivityDiagnosticsCallback(
@NonNull ConnectivityDiagnosticsCallback callback) {
- // TODO(b/143187964): implement ConnectivityDiagnostics functionality
- throw new UnsupportedOperationException("registerCallback() not supported yet");
+ // unconditionally removing from sCallbacks prevents race conditions here, since remove() is
+ // atomic.
+ final ConnectivityDiagnosticsBinder binder = sCallbacks.remove(callback);
+ if (binder == null) return;
+
+ try {
+ mService.unregisterConnectivityDiagnosticsCallback(binder);
+ } catch (RemoteException exception) {
+ exception.rethrowFromSystemServer();
+ }
}
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 3ea64f1..bb1dafc 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -2309,6 +2309,121 @@
private static final String XATTR_CACHE_GROUP = "user.cache_group";
private static final String XATTR_CACHE_TOMBSTONE = "user.cache_tombstone";
+
+ // Matches AID_MEDIA_RW in android_filesystem_config.h
+ private static final int QUOTA_PROJECT_ID_MEDIA_NONE = 1023;
+
+ // Matches AID_MEDIA_IMAGE in android_filesystem_config.h
+ private static final int QUOTA_PROJECT_ID_MEDIA_IMAGE = 1057;
+
+ // Matches AID_MEDIA_AUDIO in android_filesystem_config.h
+ private static final int QUOTA_PROJECT_ID_MEDIA_AUDIO = 1055;
+
+ // Matches AID_MEDIA_VIDEO in android_filesystem_config.h
+ private static final int QUOTA_PROJECT_ID_MEDIA_VIDEO = 1056;
+
+ /**
+ * Constant for use with
+ * {@link #updateExternalStorageFileQuotaType(String, int)} (String, int)}, to indicate the file
+ * is not a media file.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int QUOTA_TYPE_MEDIA_NONE = 0;
+
+ /**
+ * Constant for use with
+ * {@link #updateExternalStorageFileQuotaType(String, int)} (String, int)}, to indicate the file
+ * is an image file.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int QUOTA_TYPE_MEDIA_IMAGE = 1;
+
+ /**
+ * Constant for use with
+ * {@link #updateExternalStorageFileQuotaType(String, int)} (String, int)}, to indicate the file
+ * is an audio file.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int QUOTA_TYPE_MEDIA_AUDIO = 2;
+
+ /**
+ * Constant for use with
+ * {@link #updateExternalStorageFileQuotaType(String, int)} (String, int)}, to indicate the file
+ * is a video file.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int QUOTA_TYPE_MEDIA_VIDEO = 3;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "QUOTA_TYPE_" }, value = {
+ QUOTA_TYPE_MEDIA_NONE,
+ QUOTA_TYPE_MEDIA_AUDIO,
+ QUOTA_TYPE_MEDIA_VIDEO,
+ QUOTA_TYPE_MEDIA_IMAGE,
+ })
+ public @interface QuotaType {}
+
+ private static native boolean setQuotaProjectId(String path, long projectId);
+
+ /**
+ * Let StorageManager know that the quota type for a file on external storage should
+ * be updated. Android tracks quotas for various media types. Consequently, this should be
+ * called on first creation of a new file on external storage, and whenever the
+ * media type of the file is updated later.
+ *
+ * This API doesn't require any special permissions, though typical implementations
+ * will require being called from an SELinux domain that allows setting file attributes
+ * related to quota (eg the GID or project ID).
+ *
+ * The default platform user of this API is the MediaProvider process, which is
+ * responsible for managing all of external storage.
+ *
+ * @param path the path to the file for which we should update the quota type
+ * @param quotaType the quota type of the file; this is based on the
+ * {@code QuotaType} constants, eg
+ * {@code StorageManager.QUOTA_TYPE_MEDIA_AUDIO}
+ *
+ * @throws IllegalArgumentException if {@code quotaType} does not correspond to a valid
+ * quota type.
+ * @throws IOException if the quota type could not be updated.
+ *
+ * @hide
+ */
+ @SystemApi
+ public void updateExternalStorageFileQuotaType(@NonNull File path,
+ @QuotaType int quotaType) throws IOException {
+ long projectId;
+ final String filePath = path.getCanonicalPath();
+ switch (quotaType) {
+ case QUOTA_TYPE_MEDIA_NONE:
+ projectId = QUOTA_PROJECT_ID_MEDIA_NONE;
+ break;
+ case QUOTA_TYPE_MEDIA_AUDIO:
+ projectId = QUOTA_PROJECT_ID_MEDIA_AUDIO;
+ break;
+ case QUOTA_TYPE_MEDIA_VIDEO:
+ projectId = QUOTA_PROJECT_ID_MEDIA_VIDEO;
+ break;
+ case QUOTA_TYPE_MEDIA_IMAGE:
+ projectId = QUOTA_PROJECT_ID_MEDIA_IMAGE;
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid quota type: " + quotaType);
+ }
+ if (!setQuotaProjectId(filePath, projectId)) {
+ throw new IOException("Failed to update quota type for " + filePath);
+ }
+ }
+
/** {@hide} */
private static void setCacheBehavior(File path, String name, boolean enabled)
throws IOException {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 605c585..b62142c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2417,6 +2417,10 @@
public static class NameValueTable implements BaseColumns {
public static final String NAME = "name";
public static final String VALUE = "value";
+ // A flag indicating whether the current value of a setting should be preserved during
+ // restore.
+ /** @hide */
+ public static final String IS_PRESERVED_IN_RESTORE = "is_preserved_in_restore";
protected static boolean putString(ContentResolver resolver, Uri uri,
String name, String value) {
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index 6334d9d..368c94c 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -31,6 +31,7 @@
import android.content.Intent;
import android.graphics.Rect;
import android.os.Build;
+import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
@@ -39,6 +40,7 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.service.autofill.Dataset;
+import android.service.autofill.FillEventHistory;
import android.service.autofill.augmented.PresentationParams.SystemPopupPresentationParams;
import android.util.Log;
import android.util.Pair;
@@ -319,6 +321,31 @@
pw.print(getClass().getName()); pw.println(": nothing to dump");
}
+ /**
+ * Gets the inline augmented autofill events that happened after the last
+ * {@link #onFillRequest(FillRequest, CancellationSignal, FillController, FillCallback)} call.
+ *
+ * <p>The history is not persisted over reboots, and it's cleared every time the service
+ * replies to a
+ * {@link #onFillRequest(FillRequest, CancellationSignal, FillController, FillCallback)}
+ * by calling {@link FillCallback#onSuccess(FillResponse)}. Hence, the service should call
+ * {@link #getFillEventHistory() before finishing the {@link FillCallback}.
+ *
+ * <p>Also note that the events from the dropdown suggestion UI is not stored in the history
+ * since the service owns the UI.
+ *
+ * @return The history or {@code null} if there are no events.
+ */
+ @Nullable public final FillEventHistory getFillEventHistory() {
+ final AutofillManager afm = getSystemService(AutofillManager.class);
+
+ if (afm == null) {
+ return null;
+ } else {
+ return afm.getFillEventHistory();
+ }
+ }
+
/** @hide */
static final class AutofillProxy {
@@ -487,9 +514,10 @@
}
}
- public void onInlineSuggestionsDataReady(@NonNull List<Dataset> inlineSuggestionsData) {
+ public void onInlineSuggestionsDataReady(@NonNull List<Dataset> inlineSuggestionsData,
+ @Nullable Bundle clientState) {
try {
- mCallback.onSuccess(inlineSuggestionsData.toArray(new Dataset[]{}));
+ mCallback.onSuccess(inlineSuggestionsData.toArray(new Dataset[]{}), clientState);
} catch (RemoteException e) {
Log.e(TAG, "Error calling back with the inline suggestions data: " + e);
}
@@ -511,7 +539,8 @@
}
}
try {
- mCallback.onSuccess(/* mInlineSuggestionsData= */null);
+ mCallback.onSuccess(/* inlineSuggestionsData= */null, /* clientState=*/
+ null);
} catch (RemoteException e) {
Log.e(TAG, "Error reporting success: " + e);
}
diff --git a/core/java/android/service/autofill/augmented/FillCallback.java b/core/java/android/service/autofill/augmented/FillCallback.java
index b767799..d0ffd7b 100644
--- a/core/java/android/service/autofill/augmented/FillCallback.java
+++ b/core/java/android/service/autofill/augmented/FillCallback.java
@@ -60,7 +60,7 @@
List<Dataset> inlineSuggestions = response.getInlineSuggestions();
if (inlineSuggestions != null && !inlineSuggestions.isEmpty()) {
- mProxy.onInlineSuggestionsDataReady(inlineSuggestions);
+ mProxy.onInlineSuggestionsDataReady(inlineSuggestions, response.getClientState());
return;
}
diff --git a/core/java/android/service/autofill/augmented/FillResponse.java b/core/java/android/service/autofill/augmented/FillResponse.java
index e8e6ff0..68ba63a 100644
--- a/core/java/android/service/autofill/augmented/FillResponse.java
+++ b/core/java/android/service/autofill/augmented/FillResponse.java
@@ -19,6 +19,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.os.Bundle;
import android.service.autofill.Dataset;
import com.android.internal.util.DataClass;
@@ -50,6 +51,13 @@
@DataClass.PluralOf("inlineSuggestion")
private @Nullable List<Dataset> mInlineSuggestions;
+ /**
+ * The client state that {@link AugmentedAutofillService} implementation can put anything in to
+ * identify the request and the response when calling
+ * {@link AugmentedAutofillService#getFillEventHistory()}.
+ */
+ private @Nullable Bundle mClientState;
+
private static FillWindow defaultFillWindow() {
return null;
}
@@ -58,6 +66,10 @@
return null;
}
+ private static Bundle defaultClientState() {
+ return null;
+ }
+
/** @hide */
abstract static class BaseBuilder {
@@ -82,9 +94,11 @@
@DataClass.Generated.Member
/* package-private */ FillResponse(
@Nullable FillWindow fillWindow,
- @Nullable List<Dataset> inlineSuggestions) {
+ @Nullable List<Dataset> inlineSuggestions,
+ @Nullable Bundle clientState) {
this.mFillWindow = fillWindow;
this.mInlineSuggestions = inlineSuggestions;
+ this.mClientState = clientState;
// onConstructed(); // You can define this method to get a callback
}
@@ -111,6 +125,18 @@
}
/**
+ * The client state that {@link AugmentedAutofillService} implementation can put anything in to
+ * identify the request and the response when calling
+ * {@link AugmentedAutofillService#getFillEventHistory()}.
+ *
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public @Nullable Bundle getClientState() {
+ return mClientState;
+ }
+
+ /**
* A builder for {@link FillResponse}
*/
@SuppressWarnings("WeakerAccess")
@@ -119,6 +145,7 @@
private @Nullable FillWindow mFillWindow;
private @Nullable List<Dataset> mInlineSuggestions;
+ private @Nullable Bundle mClientState;
private long mBuilderFieldsSet = 0L;
@@ -157,10 +184,23 @@
return this;
}
+ /**
+ * The client state that {@link AugmentedAutofillService} implementation can put anything in to
+ * identify the request and the response when calling
+ * {@link AugmentedAutofillService#getFillEventHistory()}.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setClientState(@Nullable Bundle value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4;
+ mClientState = value;
+ return this;
+ }
+
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull FillResponse build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x4; // Mark builder used
+ mBuilderFieldsSet |= 0x8; // Mark builder used
if ((mBuilderFieldsSet & 0x1) == 0) {
mFillWindow = defaultFillWindow();
@@ -168,14 +208,18 @@
if ((mBuilderFieldsSet & 0x2) == 0) {
mInlineSuggestions = defaultInlineSuggestions();
}
+ if ((mBuilderFieldsSet & 0x4) == 0) {
+ mClientState = defaultClientState();
+ }
FillResponse o = new FillResponse(
mFillWindow,
- mInlineSuggestions);
+ mInlineSuggestions,
+ mClientState);
return o;
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x4) != 0) {
+ if ((mBuilderFieldsSet & 0x8) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -183,10 +227,10 @@
}
@DataClass.Generated(
- time = 1577476012370L,
+ time = 1580335256422L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/service/autofill/augmented/FillResponse.java",
- inputSignatures = "private @android.annotation.Nullable android.service.autofill.augmented.FillWindow mFillWindow\nprivate @com.android.internal.util.DataClass.PluralOf(\"inlineSuggestion\") @android.annotation.Nullable java.util.List<android.service.autofill.Dataset> mInlineSuggestions\nprivate static android.service.autofill.augmented.FillWindow defaultFillWindow()\nprivate static java.util.List<android.service.autofill.Dataset> defaultInlineSuggestions()\nclass FillResponse extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true, genHiddenGetters=true)\nabstract android.service.autofill.augmented.FillResponse.Builder addInlineSuggestion(android.service.autofill.Dataset)\nclass BaseBuilder extends java.lang.Object implements []")
+ inputSignatures = "private @android.annotation.Nullable android.service.autofill.augmented.FillWindow mFillWindow\nprivate @com.android.internal.util.DataClass.PluralOf(\"inlineSuggestion\") @android.annotation.Nullable java.util.List<android.service.autofill.Dataset> mInlineSuggestions\nprivate @android.annotation.Nullable android.os.Bundle mClientState\nprivate static android.service.autofill.augmented.FillWindow defaultFillWindow()\nprivate static java.util.List<android.service.autofill.Dataset> defaultInlineSuggestions()\nprivate static android.os.Bundle defaultClientState()\nclass FillResponse extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true, genHiddenGetters=true)\nabstract android.service.autofill.augmented.FillResponse.Builder addInlineSuggestion(android.service.autofill.Dataset)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/service/autofill/augmented/IFillCallback.aidl b/core/java/android/service/autofill/augmented/IFillCallback.aidl
index 3ccb311..31e77f35 100644
--- a/core/java/android/service/autofill/augmented/IFillCallback.aidl
+++ b/core/java/android/service/autofill/augmented/IFillCallback.aidl
@@ -16,6 +16,7 @@
package android.service.autofill.augmented;
+import android.os.Bundle;
import android.os.ICancellationSignal;
import android.service.autofill.Dataset;
@@ -27,7 +28,7 @@
*/
interface IFillCallback {
void onCancellable(in ICancellationSignal cancellation);
- void onSuccess(in @nullable Dataset[] mInlineSuggestionsData);
+ void onSuccess(in @nullable Dataset[] inlineSuggestionsData, in @nullable Bundle clientState);
boolean isCompleted();
void cancel();
}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 06fccaf8..5fdac81 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -47,6 +47,9 @@
/** @hide */
public static final String BACKUP_NO_KV_DATA_CHANGE_CALLS =
"backup_enable_no_data_notification_calls";
+ /** @hide */
+ public static final String SETTINGS_DO_NOT_RESTORE_PRESERVED =
+ "settings_do_not_restore_preserved";
private static final Map<String, String> DEFAULT_FLAGS;
@@ -68,6 +71,11 @@
// Disabled until backup transports support it.
DEFAULT_FLAGS.put(BACKUP_NO_KV_DATA_CHANGE_CALLS, "false");
+ // Disabled by default until b/148278926 is resolved. This flags guards a feature
+ // introduced in R and will be removed in the next release (b/148367230).
+ DEFAULT_FLAGS.put(SETTINGS_DO_NOT_RESTORE_PRESERVED, "false");
+
+ DEFAULT_FLAGS.put("settings_tether_all_in_one", "false");
}
/**
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index dad7696..0653b06 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -16,8 +16,6 @@
package android.view;
-import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_HIDDEN;
-import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
import static android.view.InsetsState.ISIDE_BOTTOM;
import static android.view.InsetsState.ISIDE_FLOATING;
import static android.view.InsetsState.ISIDE_LEFT;
@@ -188,7 +186,7 @@
@Override
public void finish(boolean shown) {
- if (mCancelled) {
+ if (mCancelled || mFinished) {
return;
}
setInsetsAndAlpha(shown ? mShownInsets : mHiddenInsets, 1f /* alpha */, 1f /* fraction */);
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 2785d21..22d6f37 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -70,7 +70,7 @@
private static final int ANIMATION_DURATION_HIDE_MS = 340;
private static final int PENDING_CONTROL_TIMEOUT_MS = 2000;
- static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
+ public static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
/**
* Layout mode during insets animation: The views should be laid out as if the changing inset
@@ -78,7 +78,7 @@
* be called as if the changing insets types are shown, which will result in the views being
* laid out as if the insets are fully shown.
*/
- static final int LAYOUT_INSETS_DURING_ANIMATION_SHOWN = 0;
+ public static final int LAYOUT_INSETS_DURING_ANIMATION_SHOWN = 0;
/**
* Layout mode during insets animation: The views should be laid out as if the changing inset
@@ -86,7 +86,7 @@
* be called as if the changing insets types are hidden, which will result in the views being
* laid out as if the insets are fully hidden.
*/
- static final int LAYOUT_INSETS_DURING_ANIMATION_HIDDEN = 1;
+ public static final int LAYOUT_INSETS_DURING_ANIMATION_HIDDEN = 1;
/**
* Determines the behavior of how the views should be laid out during an insets animation that
@@ -149,7 +149,7 @@
@Override
public void set(WindowInsetsAnimationController controller, Insets value) {
controller.setInsetsAndAlpha(
- value, 1f /* alpha */, (((DefaultAnimationControlListener)
+ value, 1f /* alpha */, (((InternalAnimationControlListener)
((InsetsAnimationControlImpl) controller).getListener())
.getRawFraction()));
}
@@ -166,7 +166,7 @@
private ObjectAnimator mAnimator;
protected boolean mShow;
- InternalAnimationControlListener(boolean show) {
+ public InternalAnimationControlListener(boolean show) {
mShow = show;
}
@@ -214,7 +214,10 @@
return (float) mAnimator.getCurrentPlayTime() / mAnimator.getDuration();
}
- protected long getDurationMs() {
+ /**
+ * To get the animation duration in MS.
+ */
+ public long getDurationMs() {
if (mAnimator != null) {
return mAnimator.getDuration();
}
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index 77bc25b..d4961ea 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -193,6 +193,15 @@
dest.writeBoolean(mVisible);
}
+ @Override
+ public String toString() {
+ return "InsetsSource: {"
+ + "mType=" + InsetsState.typeToString(mType)
+ + ", mFrame=" + mFrame.toShortString()
+ + ", mVisible" + mVisible
+ + "}";
+ }
+
public static final @android.annotation.NonNull Creator<InsetsSource> CREATOR = new Creator<InsetsSource>() {
public InsetsSource createFromParcel(Parcel in) {
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index bd19799..8648682 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -21,7 +21,6 @@
import static android.view.ViewRootImpl.NEW_INSETS_MODE_IME;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
import static android.view.ViewRootImpl.sNewInsetsMode;
-import static android.view.WindowInsets.Type.IME;
import static android.view.WindowInsets.Type.MANDATORY_SYSTEM_GESTURES;
import static android.view.WindowInsets.Type.SIZE;
import static android.view.WindowInsets.Type.SYSTEM_GESTURES;
@@ -43,7 +42,6 @@
import android.util.SparseIntArray;
import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type.InsetsType;
-import android.view.WindowManager.LayoutParams;
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import java.io.PrintWriter;
@@ -366,7 +364,12 @@
return result;
}
- static @Type.InsetsType int toPublicType(@InternalInsetsType int type) {
+ /**
+ * Converting a internal type to the public type.
+ * @param type internal insets type, {@code InternalInsetsType}.
+ * @return public insets type, {@code Type.InsetsType}.
+ */
+ public static @Type.InsetsType int toPublicType(@InternalInsetsType int type) {
switch (type) {
case ITYPE_STATUS_BAR:
return Type.STATUS_BARS;
@@ -510,5 +513,13 @@
mSources.put(source.getType(), source);
}
}
+
+ @Override
+ public String toString() {
+ return "InsetsState: {"
+ + "mDisplayFrame=" + mDisplayFrame
+ + ", mSources=" + mSources
+ + "}";
+ }
}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 051534c..bbb7513 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -359,7 +359,7 @@
mMultiProfilePagerAdapter.getPersonalListAdapter());
mPersonalPackageMonitor.register(
this, getMainLooper(), getPersonalProfileUserHandle(), false);
- if (hasWorkProfile()) {
+ if (hasWorkProfile() && ENABLE_TABBED_VIEW) {
mWorkPackageMonitor = createPackageMonitor(
mMultiProfilePagerAdapter.getWorkListAdapter());
mWorkPackageMonitor.register(this, getMainLooper(), getWorkProfileUserHandle(), false);
@@ -725,7 +725,7 @@
if (!mRegistered) {
mPersonalPackageMonitor.register(this, getMainLooper(),
getPersonalProfileUserHandle(), false);
- if (hasWorkProfile()) {
+ if (hasWorkProfile() && ENABLE_TABBED_VIEW) {
if (mWorkPackageMonitor == null) {
mWorkPackageMonitor = createPackageMonitor(
mMultiProfilePagerAdapter.getWorkListAdapter());
@@ -1155,7 +1155,9 @@
}
private void safelyStartActivityInternal(TargetInfo cti) {
- mPersonalPackageMonitor.unregister();
+ if (mPersonalPackageMonitor != null) {
+ mPersonalPackageMonitor.unregister();
+ }
if (mWorkPackageMonitor != null) {
mWorkPackageMonitor.unregister();
}
diff --git a/core/java/com/android/internal/compat/ChangeReporter.java b/core/java/com/android/internal/compat/ChangeReporter.java
index e0eb9af..5e886a6 100644
--- a/core/java/com/android/internal/compat/ChangeReporter.java
+++ b/core/java/com/android/internal/compat/ChangeReporter.java
@@ -16,13 +16,16 @@
package com.android.internal.compat;
+import android.annotation.IntDef;
import android.util.Log;
import android.util.Slog;
-import android.util.StatsLog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -42,7 +45,7 @@
long mChangeId;
int mState;
- ChangeReport(long changeId, int state) {
+ ChangeReport(long changeId, @State int state) {
mChangeId = changeId;
mState = state;
}
@@ -69,7 +72,7 @@
// When true will of every time to debug (logcat).
private boolean mDebugLogAll;
- public ChangeReporter(int source) {
+ public ChangeReporter(@Source int source) {
mSource = source;
mReportedChanges = new HashMap<>();
mDebugLogAll = false;
@@ -85,8 +88,8 @@
*/
public void reportChange(int uid, long changeId, int state) {
if (shouldWriteToStatsLog(uid, changeId, state)) {
- StatsLog.write(StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED, uid, changeId,
- state, mSource);
+ FrameworkStatsLog.write(FrameworkStatsLog.APP_COMPATIBILITY_CHANGE_REPORTED, uid,
+ changeId, state, mSource);
}
if (shouldWriteToDebug(uid, changeId, state)) {
debugLog(uid, changeId, state);
@@ -110,7 +113,7 @@
/**
- * Returns whether the next report should be logged to statsLog.
+ * Returns whether the next report should be logged to FrameworkStatsLog.
*
* @param uid affected by the change
* @param changeId the reported change id
@@ -174,7 +177,7 @@
private void debugLog(int uid, long changeId, int state) {
String message = String.format("Compat change id reported: %d; UID %d; state: %s", changeId,
uid, stateToString(state));
- if (mSource == StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__SYSTEM_SERVER) {
+ if (mSource == SOURCE_SYSTEM_SERVER) {
Slog.d(TAG, message);
} else {
Log.d(TAG, message);
@@ -183,21 +186,56 @@
}
/**
- * Transforms StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE enum to a string.
+ * Transforms {@link #ChangeReporter.State} enum to a string.
*
* @param state to transform
* @return a string representing the state
*/
- private static String stateToString(int state) {
+ private static String stateToString(@State int state) {
switch (state) {
- case StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED:
+ case STATE_LOGGED:
return "LOGGED";
- case StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__ENABLED:
+ case STATE_ENABLED:
return "ENABLED";
- case StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__DISABLED:
+ case STATE_DISABLED:
return "DISABLED";
default:
return "UNKNOWN";
}
}
+
+ /** These values should be kept in sync with those in atoms.proto */
+ public static final int STATE_UNKNOWN_STATE =
+ FrameworkStatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__UNKNOWN_STATE;
+ public static final int STATE_ENABLED =
+ FrameworkStatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__ENABLED;
+ public static final int STATE_DISABLED =
+ FrameworkStatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__DISABLED;
+ public static final int STATE_LOGGED =
+ FrameworkStatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED;
+ public static final int SOURCE_UNKNOWN_SOURCE =
+ FrameworkStatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__UNKNOWN_SOURCE;
+ public static final int SOURCE_APP_PROCESS =
+ FrameworkStatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__APP_PROCESS;
+ public static final int SOURCE_SYSTEM_SERVER =
+ FrameworkStatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__SYSTEM_SERVER;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, prefix = { "STATE_" }, value = {
+ STATE_UNKNOWN_STATE,
+ STATE_ENABLED,
+ STATE_DISABLED,
+ STATE_LOGGED
+ })
+ public @interface State {
+ }
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, prefix = { "SOURCE_" }, value = {
+ SOURCE_UNKNOWN_SOURCE,
+ SOURCE_APP_PROCESS,
+ SOURCE_SYSTEM_SERVER
+ })
+ public @interface Source {
+ }
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index c04efa3..cec68df 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -137,6 +137,7 @@
"android_os_Parcel.cpp",
"android_os_SELinux.cpp",
"android_os_SharedMemory.cpp",
+ "android_os_storage_StorageManager.cpp",
"android_os_Trace.cpp",
"android_os_UEventObserver.cpp",
"android_os_VintfObject.cpp",
@@ -268,6 +269,7 @@
"libnativewindow",
"libdl",
"libdl_android",
+ "libstats_jni",
"libstatslog",
"server_configurable_flags",
],
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 924cd81..481be24 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -108,6 +108,7 @@
extern int register_android_app_admin_SecurityLog(JNIEnv* env);
extern int register_android_content_AssetManager(JNIEnv* env);
extern int register_android_util_EventLog(JNIEnv* env);
+extern int register_android_util_StatsLog(JNIEnv* env);
extern int register_android_util_StatsLogInternal(JNIEnv* env);
extern int register_android_util_Log(JNIEnv* env);
extern int register_android_util_MemoryIntArray(JNIEnv* env);
@@ -143,6 +144,7 @@
extern int register_android_os_SELinux(JNIEnv* env);
extern int register_android_os_VintfObject(JNIEnv *env);
extern int register_android_os_VintfRuntimeInfo(JNIEnv *env);
+extern int register_android_os_storage_StorageManager(JNIEnv* env);
extern int register_android_os_SystemProperties(JNIEnv *env);
extern int register_android_os_SystemClock(JNIEnv* env);
extern int register_android_os_Trace(JNIEnv* env);
@@ -1445,6 +1447,7 @@
REG_JNI(register_android_util_EventLog),
REG_JNI(register_android_util_Log),
REG_JNI(register_android_util_MemoryIntArray),
+ REG_JNI(register_android_util_StatsLog),
REG_JNI(register_android_util_StatsLogInternal),
REG_JNI(register_android_app_admin_SecurityLog),
REG_JNI(register_android_content_AssetManager),
@@ -1466,6 +1469,7 @@
REG_JNI(register_android_os_HwParcel),
REG_JNI(register_android_os_HwRemoteBinder),
REG_JNI(register_android_os_NativeHandle),
+ REG_JNI(register_android_os_storage_StorageManager),
REG_JNI(register_android_os_VintfObject),
REG_JNI(register_android_os_VintfRuntimeInfo),
REG_JNI(register_android_service_DataLoaderService),
diff --git a/core/jni/android_os_storage_StorageManager.cpp b/core/jni/android_os_storage_StorageManager.cpp
new file mode 100644
index 0000000..aee6733
--- /dev/null
+++ b/core/jni/android_os_storage_StorageManager.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "StorageManager"
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <fcntl.h>
+#include <linux/fs.h>
+
+#include <nativehelper/JNIHelp.h>
+#include "core_jni_helpers.h"
+
+namespace android {
+
+jboolean android_os_storage_StorageManager_setQuotaProjectId(JNIEnv* env, jobject self,
+ jstring path, jlong projectId) {
+ struct fsxattr fsx;
+ ScopedUtfChars utf_chars_path(env, path);
+
+ if (projectId > UINT32_MAX) {
+ LOG(ERROR) << "Invalid project id: " << projectId;
+ return JNI_FALSE;
+ }
+
+ android::base::unique_fd fd(
+ TEMP_FAILURE_RETRY(open(utf_chars_path.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (fd == -1) {
+ PLOG(ERROR) << "Failed to open " << utf_chars_path.c_str() << " to set project id.";
+ return JNI_FALSE;
+ }
+
+ int ret = ioctl(fd, FS_IOC_FSGETXATTR, &fsx);
+ if (ret == -1) {
+ PLOG(ERROR) << "Failed to get extended attributes for " << utf_chars_path.c_str()
+ << " to get project id.";
+ return JNI_FALSE;
+ }
+
+ fsx.fsx_projid = projectId;
+ ret = ioctl(fd, FS_IOC_FSSETXATTR, &fsx);
+ if (ret == -1) {
+ PLOG(ERROR) << "Failed to set extended attributes for " << utf_chars_path.c_str()
+ << " to set project id.";
+ return JNI_FALSE;
+ }
+
+ return JNI_TRUE;
+}
+
+// ----------------------------------------------------------------------------
+
+static const JNINativeMethod gStorageManagerMethods[] = {
+ {"setQuotaProjectId", "(Ljava/lang/String;J)Z",
+ (void*)android_os_storage_StorageManager_setQuotaProjectId},
+};
+
+const char* const kStorageManagerPathName = "android/os/storage/StorageManager";
+
+int register_android_os_storage_StorageManager(JNIEnv* env) {
+ return RegisterMethodsOrDie(env, kStorageManagerPathName, gStorageManagerMethods,
+ NELEM(gStorageManagerMethods));
+}
+
+}; // namespace android
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index c0743e5..7c8f62c 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -150,9 +150,9 @@
// Will be removed soon.
optional PinnedStackControllerProto pinned_stack_controller = 5 [deprecated=true];
/* non app windows */
- repeated WindowTokenProto above_app_windows = 6;
- repeated WindowTokenProto below_app_windows = 7;
- repeated WindowTokenProto ime_windows = 8;
+ repeated WindowTokenProto above_app_windows = 6 [deprecated=true];
+ repeated WindowTokenProto below_app_windows = 7 [deprecated=true];
+ repeated WindowTokenProto ime_windows = 8 [deprecated=true];
optional int32 dpi = 9;
optional .android.view.DisplayInfoProto display_info = 10;
optional int32 rotation = 11;
@@ -165,8 +165,33 @@
repeated IdentifierProto closing_apps = 18;
repeated IdentifierProto changing_apps = 19;
repeated WindowTokenProto overlay_windows = 20;
+ optional DisplayAreaProto root_display_area = 21;
}
+/* represents DisplayArea object */
+message DisplayAreaProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional WindowContainerProto window_container = 1;
+ optional string name = 2 [ (.android.privacy).dest = DEST_EXPLICIT ];
+ repeated DisplayAreaChildProto children = 3;
+}
+
+/* represents a generic child of a DisplayArea */
+message DisplayAreaChildProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ /* At most one of the following should be present: */
+
+ /* represents a DisplayArea child */
+ optional DisplayAreaProto display_area = 1;
+ /* represents a WindowToken child */
+ optional WindowTokenProto window = 2;
+ /* represents an unknown child - the class name is recorded */
+ repeated string unknown = 3;
+}
+
+
/* represents DisplayFrames */
message DisplayFramesProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index cfed805..059bc44 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1781,8 +1781,11 @@
<attr name="crossProfile" format="boolean" />
</declare-styleable>
- <!-- The <code>feature</code> tag declares a feature. A feature is a part of an app. E.g.
- photo sharing app might include a direct messaging component.
+ <!-- The <code>feature</code> tag declares a feature. A feature is a logical part of an app.
+ E.g. photo sharing app might include a direct messaging component. To tag certain code as
+ belonging to a feature, use a context created via
+ {@link android.content.Context#createFeatureContext(String)} for any interaction with the
+ system.
<p>This appears as a child tag of the root {@link #AndroidManifest manifest} tag.
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 21128e3..21d1d3c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1266,6 +1266,7 @@
<java-symbol type="array" name="vendor_disallowed_apps_managed_profile" />
<java-symbol type="array" name="vendor_disallowed_apps_managed_device" />
<java-symbol type="array" name="cross_profile_apps" />
+ <java-symbol type="array" name="vendor_cross_profile_apps" />
<java-symbol type="drawable" name="default_wallpaper" />
<java-symbol type="drawable" name="default_lock_wallpaper" />
diff --git a/core/res/res/values/vendor_cross_profile_apps.xml b/core/res/res/values/vendor_cross_profile_apps.xml
new file mode 100644
index 0000000..32839cd
--- /dev/null
+++ b/core/res/res/values/vendor_cross_profile_apps.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<resources>
+ <!--
+ A collection of apps that have been pre-approved for cross-profile communication.
+ These will not require admin consent, but will still require user consent during provisioning.
+ -->
+ <string-array translatable="false" name="vendor_cross_profile_apps">
+ </string-array>
+</resources>
diff --git a/core/tests/PlatformCompatFramework/src/com/android/internal/compat/ChangeReporterTest.java b/core/tests/PlatformCompatFramework/src/com/android/internal/compat/ChangeReporterTest.java
index 09bbe37..a052543 100644
--- a/core/tests/PlatformCompatFramework/src/com/android/internal/compat/ChangeReporterTest.java
+++ b/core/tests/PlatformCompatFramework/src/com/android/internal/compat/ChangeReporterTest.java
@@ -24,10 +24,10 @@
public class ChangeReporterTest {
@Test
public void testStatsLogOnce() {
- ChangeReporter reporter = new ChangeReporter(0);
+ ChangeReporter reporter = new ChangeReporter(ChangeReporter.SOURCE_UNKNOWN_SOURCE);
int myUid = 1022, otherUid = 1023;
long myChangeId = 500L, otherChangeId = 600L;
- int myState = 1, otherState = 2;
+ int myState = ChangeReporter.STATE_ENABLED, otherState = ChangeReporter.STATE_DISABLED;
assertTrue(reporter.shouldWriteToStatsLog(myUid, myChangeId, myState));
reporter.reportChange(myUid, myChangeId, myState);
@@ -42,10 +42,10 @@
@Test
public void testStatsLogAfterReset() {
- ChangeReporter reporter = new ChangeReporter(0);
+ ChangeReporter reporter = new ChangeReporter(ChangeReporter.SOURCE_UNKNOWN_SOURCE);
int myUid = 1022;
long myChangeId = 500L;
- int myState = 1;
+ int myState = ChangeReporter.STATE_ENABLED;
assertTrue(reporter.shouldWriteToStatsLog(myUid, myChangeId, myState));
reporter.reportChange(myUid, myChangeId, myState);
@@ -60,10 +60,10 @@
@Test
public void testDebugLogOnce() {
- ChangeReporter reporter = new ChangeReporter(0);
+ ChangeReporter reporter = new ChangeReporter(ChangeReporter.SOURCE_UNKNOWN_SOURCE);
int myUid = 1022, otherUid = 1023;
long myChangeId = 500L, otherChangeId = 600L;
- int myState = 1, otherState = 2;
+ int myState = ChangeReporter.STATE_ENABLED, otherState = ChangeReporter.STATE_DISABLED;
assertTrue(reporter.shouldWriteToDebug(myUid, myChangeId, myState));
reporter.reportChange(myUid, myChangeId, myState);
@@ -78,10 +78,10 @@
@Test
public void testDebugLogAfterReset() {
- ChangeReporter reporter = new ChangeReporter(0);
+ ChangeReporter reporter = new ChangeReporter(ChangeReporter.SOURCE_UNKNOWN_SOURCE);
int myUid = 1022;
long myChangeId = 500L;
- int myState = 1;
+ int myState = ChangeReporter.STATE_ENABLED;
assertTrue(reporter.shouldWriteToDebug(myUid, myChangeId, myState));
reporter.reportChange(myUid, myChangeId, myState);
@@ -96,10 +96,10 @@
@Test
public void testDebugLogWithLogAll() {
- ChangeReporter reporter = new ChangeReporter(0);
+ ChangeReporter reporter = new ChangeReporter(ChangeReporter.SOURCE_UNKNOWN_SOURCE);
int myUid = 1022;
long myChangeId = 500L;
- int myState = 1;
+ int myState = ChangeReporter.STATE_ENABLED;
assertTrue(reporter.shouldWriteToDebug(myUid, myChangeId, myState));
reporter.reportChange(myUid, myChangeId, myState);
diff --git a/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java b/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java
index ba29a97..d17b635 100644
--- a/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java
+++ b/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java
@@ -27,17 +27,17 @@
import org.junit.Test;
public class PhoneTimeSuggestionTest {
- private static final int PHONE_ID = 99999;
+ private static final int SLOT_INDEX = 99999;
@Test
public void testEquals() {
- PhoneTimeSuggestion.Builder builder1 = new PhoneTimeSuggestion.Builder(PHONE_ID);
+ PhoneTimeSuggestion.Builder builder1 = new PhoneTimeSuggestion.Builder(SLOT_INDEX);
{
PhoneTimeSuggestion one = builder1.build();
assertEquals(one, one);
}
- PhoneTimeSuggestion.Builder builder2 = new PhoneTimeSuggestion.Builder(PHONE_ID);
+ PhoneTimeSuggestion.Builder builder2 = new PhoneTimeSuggestion.Builder(SLOT_INDEX);
{
PhoneTimeSuggestion one = builder1.build();
PhoneTimeSuggestion two = builder2.build();
@@ -59,7 +59,7 @@
assertEquals(two, one);
}
- PhoneTimeSuggestion.Builder builder3 = new PhoneTimeSuggestion.Builder(PHONE_ID + 1);
+ PhoneTimeSuggestion.Builder builder3 = new PhoneTimeSuggestion.Builder(SLOT_INDEX + 1);
builder3.setUtcTime(new TimestampedValue<>(1111L, 2222L));
{
PhoneTimeSuggestion one = builder1.build();
@@ -80,7 +80,7 @@
@Test
public void testParcelable() {
- PhoneTimeSuggestion.Builder builder = new PhoneTimeSuggestion.Builder(PHONE_ID);
+ PhoneTimeSuggestion.Builder builder = new PhoneTimeSuggestion.Builder(SLOT_INDEX);
assertRoundTripParcelable(builder.build());
builder.setUtcTime(new TimestampedValue<>(1111L, 2222L));
diff --git a/core/tests/coretests/src/android/app/timezonedetector/PhoneTimeZoneSuggestionTest.java b/core/tests/coretests/src/android/app/timezonedetector/PhoneTimeZoneSuggestionTest.java
index 0108a0b..384dbf9 100644
--- a/core/tests/coretests/src/android/app/timezonedetector/PhoneTimeZoneSuggestionTest.java
+++ b/core/tests/coretests/src/android/app/timezonedetector/PhoneTimeZoneSuggestionTest.java
@@ -26,17 +26,17 @@
import org.junit.Test;
public class PhoneTimeZoneSuggestionTest {
- private static final int PHONE_ID = 99999;
+ private static final int SLOT_INDEX = 99999;
@Test
public void testEquals() {
- PhoneTimeZoneSuggestion.Builder builder1 = new PhoneTimeZoneSuggestion.Builder(PHONE_ID);
+ PhoneTimeZoneSuggestion.Builder builder1 = new PhoneTimeZoneSuggestion.Builder(SLOT_INDEX);
{
PhoneTimeZoneSuggestion one = builder1.build();
assertEquals(one, one);
}
- PhoneTimeZoneSuggestion.Builder builder2 = new PhoneTimeZoneSuggestion.Builder(PHONE_ID);
+ PhoneTimeZoneSuggestion.Builder builder2 = new PhoneTimeZoneSuggestion.Builder(SLOT_INDEX);
{
PhoneTimeZoneSuggestion one = builder1.build();
PhoneTimeZoneSuggestion two = builder2.build();
@@ -45,7 +45,7 @@
}
PhoneTimeZoneSuggestion.Builder builder3 =
- new PhoneTimeZoneSuggestion.Builder(PHONE_ID + 1);
+ new PhoneTimeZoneSuggestion.Builder(SLOT_INDEX + 1);
{
PhoneTimeZoneSuggestion one = builder1.build();
PhoneTimeZoneSuggestion three = builder3.build();
@@ -120,7 +120,7 @@
@Test(expected = RuntimeException.class)
public void testBuilderValidates_emptyZone_badMatchType() {
- PhoneTimeZoneSuggestion.Builder builder = new PhoneTimeZoneSuggestion.Builder(PHONE_ID);
+ PhoneTimeZoneSuggestion.Builder builder = new PhoneTimeZoneSuggestion.Builder(SLOT_INDEX);
// No zone ID, so match type should be left unset.
builder.setMatchType(PhoneTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET);
builder.build();
@@ -128,7 +128,7 @@
@Test(expected = RuntimeException.class)
public void testBuilderValidates_zoneSet_badMatchType() {
- PhoneTimeZoneSuggestion.Builder builder = new PhoneTimeZoneSuggestion.Builder(PHONE_ID);
+ PhoneTimeZoneSuggestion.Builder builder = new PhoneTimeZoneSuggestion.Builder(SLOT_INDEX);
builder.setZoneId("Europe/London");
builder.setQuality(PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE);
builder.build();
@@ -136,7 +136,7 @@
@Test
public void testParcelable() {
- PhoneTimeZoneSuggestion.Builder builder = new PhoneTimeZoneSuggestion.Builder(PHONE_ID);
+ PhoneTimeZoneSuggestion.Builder builder = new PhoneTimeZoneSuggestion.Builder(SLOT_INDEX);
assertRoundTripParcelable(builder.build());
builder.setZoneId("Europe/London");
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 91c5fbe..9da185b 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1183,12 +1183,6 @@
"group": "WM_DEBUG_APP_TRANSITIONS",
"at": "com\/android\/server\/wm\/AppTransitionController.java"
},
- "292555239": {
- "message": "ScreenRotation sill animating: mDisplayAnimator: %s\nmEnterBlackFrameAnimator: %s\nmRotateScreenAnimator: %s\nmScreenshotRotationAnimator: %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_ORIENTATION",
- "at": "com\/android\/server\/wm\/ScreenRotationAnimation.java"
- },
"292904800": {
"message": "Deferring rotation, animation in progress.",
"level": "VERBOSE",
@@ -1573,12 +1567,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "1004585481": {
- "message": "%s forcing orientation to %d for display id=%d",
- "level": "VERBOSE",
- "group": "WM_DEBUG_ORIENTATION",
- "at": "com\/android\/server\/wm\/DisplayContent.java"
- },
"1051545910": {
"message": "Exit animation finished in %s: remove=%b",
"level": "VERBOSE",
@@ -1699,6 +1687,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "1346895820": {
+ "message": "ScreenRotation still animating: type: %d\nmDisplayAnimator: %s\nmEnterBlackFrameAnimator: %s\nmRotateScreenAnimator: %s\nmScreenshotRotationAnimator: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/ScreenRotationAnimation.java"
+ },
"1358462645": {
"message": "Looking for focus: %s, flags=%d, canReceive=%b",
"level": "VERBOSE",
@@ -1717,6 +1711,12 @@
"group": "WM_DEBUG_IME",
"at": "com\/android\/server\/wm\/ImeInsetsSourceProvider.java"
},
+ "1389009035": {
+ "message": "NonAppWindowContainer cannot set orientation: %s",
+ "level": "WARN",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
"1401700824": {
"message": "Window drawn win=%s",
"level": "DEBUG",
@@ -1891,6 +1891,12 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "1674747211": {
+ "message": "%s forcing orientation to %d for display id=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DisplayArea.java"
+ },
"1677260366": {
"message": "Finish starting %s: first real window is shown, no animation",
"level": "VERBOSE",
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index dace091..0e88c75 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -198,6 +198,7 @@
final List<String> mFeatures;
@DeviceType
final int mDeviceType;
+ final boolean mIsSystem;
final Uri mIconUri;
final CharSequence mDescription;
@ConnectionState
@@ -214,6 +215,7 @@
mName = builder.mName;
mFeatures = builder.mFeatures;
mDeviceType = builder.mDeviceType;
+ mIsSystem = builder.mIsSystem;
mIconUri = builder.mIconUri;
mDescription = builder.mDescription;
mConnectionState = builder.mConnectionState;
@@ -230,6 +232,7 @@
mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mFeatures = in.createStringArrayList();
mDeviceType = in.readInt();
+ mIsSystem = in.readBoolean();
mIconUri = in.readParcelable(null);
mDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mConnectionState = in.readInt();
@@ -288,6 +291,17 @@
}
/**
+ * Returns whether the route is a system route or not.
+ * <p>
+ * System routes are media routes directly controlled by the system
+ * such as phone speaker, wired headset, and Bluetooth devices.
+ * </p>
+ */
+ public boolean isSystemRoute() {
+ return mIsSystem;
+ }
+
+ /**
* Gets the URI of the icon representing this route.
* <p>
* This icon will be used in picker UIs if available.
@@ -425,6 +439,7 @@
&& Objects.equals(mName, other.mName)
&& Objects.equals(mFeatures, other.mFeatures)
&& (mDeviceType == other.mDeviceType)
+ && (mIsSystem == other.mIsSystem)
&& Objects.equals(mIconUri, other.mIconUri)
&& Objects.equals(mDescription, other.mDescription)
&& (mConnectionState == other.mConnectionState)
@@ -438,7 +453,7 @@
@Override
public int hashCode() {
// Note: mExtras is not included.
- return Objects.hash(mId, mName, mFeatures, mDeviceType, mIconUri, mDescription,
+ return Objects.hash(mId, mName, mFeatures, mDeviceType, mIsSystem, mIconUri, mDescription,
mConnectionState, mClientPackageName, mVolumeHandling, mVolumeMax, mVolume,
mProviderId);
}
@@ -475,6 +490,7 @@
TextUtils.writeToParcel(mName, dest, flags);
dest.writeStringList(mFeatures);
dest.writeInt(mDeviceType);
+ dest.writeBoolean(mIsSystem);
dest.writeParcelable(mIconUri, flags);
TextUtils.writeToParcel(mDescription, dest, flags);
dest.writeInt(mConnectionState);
@@ -496,6 +512,7 @@
@DeviceType
int mDeviceType = DEVICE_TYPE_UNKNOWN;
+ boolean mIsSystem;
Uri mIconUri;
CharSequence mDescription;
@ConnectionState
@@ -542,6 +559,7 @@
mName = routeInfo.mName;
mFeatures = new ArrayList<>(routeInfo.mFeatures);
mDeviceType = routeInfo.mDeviceType;
+ mIsSystem = routeInfo.mIsSystem;
mIconUri = routeInfo.mIconUri;
mDescription = routeInfo.mDescription;
mConnectionState = routeInfo.mConnectionState;
@@ -610,6 +628,16 @@
}
/**
+ * Sets whether the route is a system route or not.
+ * @hide
+ */
+ @NonNull
+ public Builder setSystemRoute(boolean isSystem) {
+ mIsSystem = isSystem;
+ return this;
+ }
+
+ /**
* Sets the URI of the icon representing this route.
* <p>
* This icon will be used in picker UIs if available.
diff --git a/media/java/android/media/MediaRoute2ProviderInfo.java b/media/java/android/media/MediaRoute2ProviderInfo.java
index c9a2ec7..afe002e 100644
--- a/media/java/android/media/MediaRoute2ProviderInfo.java
+++ b/media/java/android/media/MediaRoute2ProviderInfo.java
@@ -180,6 +180,23 @@
}
/**
+ * Sets whether the provider provides system routes or not
+ */
+ @NonNull
+ public Builder setSystemRouteProvider(boolean isSystem) {
+ int count = mRoutes.size();
+ for (int i = 0; i < count; i++) {
+ MediaRoute2Info route = mRoutes.valueAt(i);
+ if (route.isSystemRoute() != isSystem) {
+ mRoutes.setValueAt(i, new MediaRoute2Info.Builder(route)
+ .setSystemRoute(isSystem)
+ .build());
+ }
+ }
+ return this;
+ }
+
+ /**
* Adds a route to the provider
*/
@NonNull
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 7b9a44f..f751a22 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -230,7 +230,11 @@
/**
* Gets the unmodifiable list of {@link MediaRoute2Info routes} currently
* known to the media router.
+ * <p>
+ * {@link MediaRoute2Info#isSystemRoute() System routes} such as phone speaker,
+ * Bluetooth devices are always included in the list.
* Please note that the list can be changed before callbacks are invoked.
+ * </p>
*
* @return the list of routes that contains at least one of the route features in discovery
* preferences registered by the application
@@ -243,7 +247,8 @@
List<MediaRoute2Info> filteredRoutes = new ArrayList<>();
for (MediaRoute2Info route : mRoutes.values()) {
- if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
+ if (route.isSystemRoute()
+ || route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
filteredRoutes.add(route);
}
}
@@ -307,12 +312,18 @@
* with the given route.
*
* @param route the route you want to create a controller with.
+ * @throws IllegalArgumentException if the given route is
+ * {@link MediaRoute2Info#isSystemRoute() system route}
*
* @see RoutingControllerCallback#onControllerCreated
* @see RoutingControllerCallback#onControllerCreationFailed
*/
public void requestCreateController(@NonNull MediaRoute2Info route) {
Objects.requireNonNull(route, "route must not be null");
+ if (route.isSystemRoute()) {
+ throw new IllegalArgumentException("Can't create a route controller with "
+ + "a system route. Use getSystemController().");
+ }
// TODO: Check the given route exists
final int requestId;
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 61e2f77..662eeb1 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -161,14 +161,15 @@
public List<MediaRoute2Info> getAvailableRoutes(@NonNull String packageName) {
Objects.requireNonNull(packageName, "packageName must not be null");
+ List<MediaRoute2Info> routes = new ArrayList<>();
+
List<String> preferredFeatures = mPreferredFeaturesMap.get(packageName);
if (preferredFeatures == null) {
- return Collections.emptyList();
+ preferredFeatures = Collections.emptyList();
}
- List<MediaRoute2Info> routes = new ArrayList<>();
synchronized (mRoutesLock) {
for (MediaRoute2Info route : mRoutes.values()) {
- if (route.hasAnyFeatures(preferredFeatures)) {
+ if (route.isSystemRoute() || route.hasAnyFeatures(preferredFeatures)) {
routes.add(route);
}
}
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
index 4a2044a..16259ab 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
@@ -196,7 +196,14 @@
public void testRouteFeatures() throws Exception {
Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_SPECIAL);
- assertEquals(1, routes.size());
+ int routeCount = 0;
+ for (MediaRoute2Info route : routes.values()) {
+ if (!route.isSystemRoute()) {
+ routeCount++;
+ }
+ }
+
+ assertEquals(1, routeCount);
assertNotNull(routes.get(ROUTE_ID_SPECIAL_FEATURE));
}
diff --git a/native/android/Android.bp b/native/android/Android.bp
index 34ab7a0..0c6f507 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -64,7 +64,6 @@
"libgui",
"libharfbuzz_ng", // Only for including hb.h via minikin
"libsensor",
- "libstats_jni",
"libandroid_runtime",
"libminikin",
"libnetd_client",
diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml
index e2297e4..d2f514c 100644
--- a/packages/CarSystemUI/res/values/config.xml
+++ b/packages/CarSystemUI/res/values/config.xml
@@ -29,6 +29,9 @@
<bool name="config_enableRightNavigationBar">false</bool>
<bool name="config_enableBottomNavigationBar">true</bool>
+ <!-- Disable normal notification rendering; we handle that ourselves -->
+ <bool name="config_renderNotifications">false</bool>
+
<!-- Whether heads-up notifications should be shown when shade is open. -->
<bool name="config_enableHeadsUpNotificationWhenNotificationShadeOpen">true</bool>
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
index cf4ee7d..585acfe 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -22,7 +22,6 @@
import android.content.Context;
import com.android.systemui.car.CarDeviceProvisionedControllerImpl;
-import com.android.systemui.car.CarNotificationEntryManager;
import com.android.systemui.car.CarNotificationInterruptionStateProvider;
import com.android.systemui.dagger.SystemUIRootComponent;
import com.android.systemui.dock.DockManager;
@@ -73,13 +72,6 @@
return false;
}
- /**
- * Use {@link CarNotificationEntryManager}, which does nothing when adding a notification.
- */
- @Binds
- abstract NotificationEntryManager bindNotificationEntryManager(
- CarNotificationEntryManager notificationEntryManager);
-
@Singleton
@Provides
static HeadsUpManagerPhone provideHeadsUpManagerPhone(Context context,
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
deleted file mode 100644
index cfe1c70..0000000
--- a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.car;
-
-import android.service.notification.NotificationListenerService;
-import android.service.notification.StatusBarNotification;
-
-import com.android.systemui.statusbar.FeatureFlags;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
-import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
-import com.android.systemui.statusbar.notification.logging.NotifLog;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.util.leak.LeakDetector;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-import dagger.Lazy;
-
-/**
- * Car specific notification entry manager that does nothing when adding a notification.
- *
- * <p> This is because system UI notifications are disabled and we have a different implementation.
- * Please see {@link com.android.car.notification}.
- */
-@Singleton
-public class CarNotificationEntryManager extends NotificationEntryManager {
-
- @Inject
- public CarNotificationEntryManager(
- NotifLog notifLog,
- NotificationGroupManager groupManager,
- NotificationRankingManager rankingManager,
- KeyguardEnvironment keyguardEnvironment,
- FeatureFlags featureFlags,
- Lazy<NotificationRowBinder> notificationRowBinderLazy,
- Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
- LeakDetector leakDetector) {
- super(notifLog, groupManager, rankingManager, keyguardEnvironment, featureFlags,
- notificationRowBinderLazy, notificationRemoteInputManagerLazy, leakDetector);
- }
-
- @Override
- public void addNotification(
- StatusBarNotification notification, NotificationListenerService.RankingMap ranking) {
- }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 76e9ec6..210dd32 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -87,10 +87,8 @@
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.NavigationBarController;
-import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -103,12 +101,10 @@
import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationAlertingManager;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
-import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer;
+import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.phone.AutoHideController;
@@ -124,7 +120,6 @@
import com.android.systemui.statusbar.phone.LightsOutNotifController;
import com.android.systemui.statusbar.phone.LockscreenLockIconController;
import com.android.systemui.statusbar.phone.LockscreenWallpaper;
-import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.ScrimController;
@@ -141,7 +136,6 @@
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
-import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.volume.VolumeComponent;
@@ -257,7 +251,7 @@
public CarStatusBar(
Context context,
- FeatureFlags featureFlags,
+ NotificationsController notificationsController,
LightBarController lightBarController,
AutoHideController autoHideController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -269,13 +263,11 @@
HeadsUpManagerPhone headsUpManagerPhone,
DynamicPrivacyController dynamicPrivacyController,
BypassHeadsUpNotifier bypassHeadsUpNotifier,
- Lazy<NotifPipelineInitializer> notifPipelineInitializer,
FalsingManager falsingManager,
BroadcastDispatcher broadcastDispatcher,
RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
- NotificationEntryManager notificationEntryManager,
NotificationInterruptionStateProvider notificationInterruptionStateProvider,
NotificationViewHierarchyManager notificationViewHierarchyManager,
KeyguardViewMediator keyguardViewMediator,
@@ -296,12 +288,10 @@
VibratorHelper vibratorHelper,
BubbleController bubbleController,
NotificationGroupManager groupManager,
- NotificationGroupAlertTransferHelper groupAlertTransferHelper,
VisualStabilityManager visualStabilityManager,
DeviceProvisionedController deviceProvisionedController,
NavigationBarController navigationBarController,
Lazy<AssistManager> assistManagerLazy,
- NotificationListener notificationListener,
ConfigurationController configurationController,
NotificationShadeWindowController notificationShadeWindowController,
LockscreenLockIconController lockscreenLockIconController,
@@ -318,7 +308,6 @@
Optional<Recents> recents,
Provider<StatusBarComponent.Builder> statusBarComponentBuilder,
PluginManager pluginManager,
- RemoteInputUriController remoteInputUriController,
Optional<Divider> dividerOptional,
SuperStatusBarViewFactory superStatusBarViewFactory,
LightsOutNotifController lightsOutNotifController,
@@ -334,7 +323,6 @@
KeyguardDismissUtil keyguardDismissUtil,
ExtensionController extensionController,
UserInfoControllerImpl userInfoControllerImpl,
- NotificationRowBinderImpl notificationRowBinder,
DismissCallbackRegistry dismissCallbackRegistry,
/* Car Settings injected components. */
CarServiceProvider carServiceProvider,
@@ -345,7 +333,7 @@
FlingAnimationUtils.Builder flingAnimationUtilsBuilder) {
super(
context,
- featureFlags,
+ notificationsController,
lightBarController,
autoHideController,
keyguardUpdateMonitor,
@@ -357,13 +345,11 @@
headsUpManagerPhone,
dynamicPrivacyController,
bypassHeadsUpNotifier,
- notifPipelineInitializer,
falsingManager,
broadcastDispatcher,
remoteInputQuickSettingsDisabler,
notificationGutsManager,
notificationLogger,
- notificationEntryManager,
notificationInterruptionStateProvider,
notificationViewHierarchyManager,
keyguardViewMediator,
@@ -384,12 +370,10 @@
vibratorHelper,
bubbleController,
groupManager,
- groupAlertTransferHelper,
visualStabilityManager,
deviceProvisionedController,
navigationBarController,
assistManagerLazy,
- notificationListener,
configurationController,
notificationShadeWindowController,
lockscreenLockIconController,
@@ -407,7 +391,6 @@
recents,
statusBarComponentBuilder,
pluginManager,
- remoteInputUriController,
dividerOptional,
lightsOutNotifController,
statusBarNotificationActivityStarterBuilder,
@@ -422,7 +405,6 @@
keyguardDismissUtil,
extensionController,
userInfoControllerImpl,
- notificationRowBinder,
dismissCallbackRegistry);
mUserSwitcherController = userSwitcherController;
mScrimController = scrimController;
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
index 45da822..498bd87 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
@@ -47,10 +47,8 @@
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.NavigationBarController;
-import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -63,12 +61,10 @@
import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationAlertingManager;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
-import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer;
+import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.phone.AutoHideController;
@@ -83,7 +79,6 @@
import com.android.systemui.statusbar.phone.LightsOutNotifController;
import com.android.systemui.statusbar.phone.LockscreenLockIconController;
import com.android.systemui.statusbar.phone.LockscreenWallpaper;
-import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.ScrimController;
@@ -99,7 +94,6 @@
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
-import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.volume.VolumeComponent;
@@ -127,7 +121,7 @@
@Singleton
static CarStatusBar provideStatusBar(
Context context,
- FeatureFlags featureFlags,
+ NotificationsController notificationsController,
LightBarController lightBarController,
AutoHideController autoHideController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -139,13 +133,11 @@
HeadsUpManagerPhone headsUpManagerPhone,
DynamicPrivacyController dynamicPrivacyController,
BypassHeadsUpNotifier bypassHeadsUpNotifier,
- Lazy<NotifPipelineInitializer> notifPipelineInitializer,
FalsingManager falsingManager,
BroadcastDispatcher broadcastDispatcher,
RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
- NotificationEntryManager notificationEntryManager,
NotificationInterruptionStateProvider notificationInterruptionStateProvider,
NotificationViewHierarchyManager notificationViewHierarchyManager,
KeyguardViewMediator keyguardViewMediator,
@@ -166,12 +158,10 @@
VibratorHelper vibratorHelper,
BubbleController bubbleController,
NotificationGroupManager groupManager,
- NotificationGroupAlertTransferHelper groupAlertTransferHelper,
VisualStabilityManager visualStabilityManager,
DeviceProvisionedController deviceProvisionedController,
NavigationBarController navigationBarController,
Lazy<AssistManager> assistManagerLazy,
- NotificationListener notificationListener,
ConfigurationController configurationController,
NotificationShadeWindowController notificationShadeWindowController,
LockscreenLockIconController lockscreenLockIconController,
@@ -188,7 +178,6 @@
Optional<Recents> recentsOptional,
Provider<StatusBarComponent.Builder> statusBarComponentBuilder,
PluginManager pluginManager,
- RemoteInputUriController remoteInputUriController,
Optional<Divider> dividerOptional,
SuperStatusBarViewFactory superStatusBarViewFactory,
LightsOutNotifController lightsOutNotifController,
@@ -204,7 +193,6 @@
KeyguardDismissUtil keyguardDismissUtil,
ExtensionController extensionController,
UserInfoControllerImpl userInfoControllerImpl,
- NotificationRowBinderImpl notificationRowBinder,
DismissCallbackRegistry dismissCallbackRegistry,
CarServiceProvider carServiceProvider,
Lazy<PowerManagerHelper> powerManagerHelperLazy,
@@ -214,7 +202,7 @@
FlingAnimationUtils.Builder flingAnimationUtilsBuilder) {
return new CarStatusBar(
context,
- featureFlags,
+ notificationsController,
lightBarController,
autoHideController,
keyguardUpdateMonitor,
@@ -226,13 +214,11 @@
headsUpManagerPhone,
dynamicPrivacyController,
bypassHeadsUpNotifier,
- notifPipelineInitializer,
falsingManager,
broadcastDispatcher,
remoteInputQuickSettingsDisabler,
notificationGutsManager,
notificationLogger,
- notificationEntryManager,
notificationInterruptionStateProvider,
notificationViewHierarchyManager,
keyguardViewMediator,
@@ -253,12 +239,10 @@
vibratorHelper,
bubbleController,
groupManager,
- groupAlertTransferHelper,
visualStabilityManager,
deviceProvisionedController,
navigationBarController,
assistManagerLazy,
- notificationListener,
configurationController,
notificationShadeWindowController,
lockscreenLockIconController,
@@ -275,7 +259,6 @@
recentsOptional,
statusBarComponentBuilder,
pluginManager,
- remoteInputUriController,
dividerOptional,
superStatusBarViewFactory,
lightsOutNotifController,
@@ -290,7 +273,6 @@
keyguardDismissUtil,
extensionController,
userInfoControllerImpl,
- notificationRowBinder,
dismissCallbackRegistry,
carServiceProvider,
powerManagerHelperLazy,
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 2b143e4..b31841d 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Gekoppel via <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Beskikbaar via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Tik om aan te meld"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Gekoppel, geen internet nie"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Daar kan nie by private DNS-bediener ingegaan word nie"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Beperkte verbinding"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Geen internet nie"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 2a93e017..4955ad8 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"በ <xliff:g id="NAME">%1$s</xliff:g> በኩል ተገናኝተዋል"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"በ%1$s በኩል የሚገኝ"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"ለመመዝገብ መታ ያድርጉ"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"ተገናኝቷል፣ ምንም በይነመረብ የለም"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"የግል ዲኤንኤስ አገልጋይ ሊደረስበት አይችልም"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"የተገደበ ግንኙነት"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"ምንም በይነመረብ የለም"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 5103345..7b26be4 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"تم الاتصال عبر <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"متوفرة عبر %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"انقر للاشتراك."</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"متصلة ولكن بلا إنترنت"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"لا يمكن الوصول إلى خادم أسماء نظام نطاقات خاص"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"اتصال محدود"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"لا يتوفر اتصال إنترنت."</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index fa26b9b..dcebe5b 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g>ৰ জৰিয়তে সংযুক্ত"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$sৰ মাধ্যমেৰে উপলব্ধ"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"ছাইন আপ কৰিবলৈ টিপক"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"সংযোজিত, ইণ্টাৰনেট নাই"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"ব্যক্তিগত DNS ছাৰ্ভাৰ এক্সেছ কৰিব নোৱাৰি"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"ইণ্টাৰনেট সংযোগ সীমিত"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"ইণ্টাৰনেট সংযোগ নাই"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 2e80fdc..7f3db37 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> ilə qoşulub"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s vasitəsilə əlçatandır"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Qeydiyyatdan keçmək üçün klikləyin"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Qoşuludur, internet yoxdur"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Özəl DNS serverinə giriş mümkün deyil"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Məhdud bağlantı"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"İnternet yoxdur"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index ae5c936..b789cb0 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Povezano preko: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Dostupna je preko pristupne tačke %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Dodirnite da biste se registrovali"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Veza je uspostavljena, nema interneta"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Pristup privatnom DNS serveru nije uspeo"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Ograničena veza"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Nema interneta"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index ad201d0..ce191eb 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Падключана праз праграму \"<xliff:g id="NAME">%1$s</xliff:g>\""</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Даступна праз %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Націсніце, каб зарэгістравацца"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Падключана, без доступу да інтэрнэту"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Не ўдалося атрымаць доступ да прыватнага DNS-сервера"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Абмежаваныя магчымасці падключэння"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Не падключана да інтэрнэту"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 94f78ad..3e6f77d 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Установена е връзка през <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Мрежата е достъпна през „%1$s“"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Докоснете, за да се регистрирате"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Установена е връзка – няма достъп до интернет"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Не може да се осъществи достъп до частния DNS сървър"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Ограничена връзка"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Няма връзка с интернет"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 51719160..7f6938a 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g>-এর মাধ্যমে কানেক্ট করা আছে"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s এর মাধ্যমে উপলব্ধ"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"সাইন-আপ করতে ট্যাপ করুন"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"কানেক্ট, ইন্টারনেট নেই"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"ব্যক্তিগত ডিএনএস সার্ভার অ্যাক্সেস করা যাবে না"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"সীমিত কানেকশন"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"ইন্টারনেট কানেকশন নেই"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index f74bcee..0563abd 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Povezano preko <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Dostupan preko %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Dodirnite za prijavu"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Povezano, nema interneta"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Nije moguće pristupiti privatnom DNS serveru"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Ograničena veza"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Nema internetske veze"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 1b23ecf..71b2c5d 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Connectat mitjançant <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Disponible mitjançant %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Toca per registrar-te"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Connectada, sense Internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"No es pot accedir al servidor DNS privat"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Connexió limitada"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Sense connexió a Internet"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 22603cc..a381772 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Připojeno přes <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Dostupné prostřednictvím %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Klepnutím se zaregistrujete"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Připojeno, není k dispozici internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Nelze získat přístup k soukromému serveru DNS"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Omezené připojení"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Nejste připojeni k internetu"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index bef1855..f6e8576 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Forbundet via <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Tilgængelig via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Tryk for at registrere dig"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Tilsluttet – intet internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Der er ikke adgang til den private DNS-server"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Begrænset forbindelse"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Intet internet"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index c8c97bd..99a0910 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Verbunden über <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Verfügbar über %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Zum Anmelden tippen"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Verbunden, kein Internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Auf den privaten DNS-Server kann nicht zugegriffen werden"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Eingeschränkte Verbindung"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Kein Internet"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 371075c..d37ea44 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Συνδέθηκε μέσω <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Διαθέσιμο μέσω %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Πατήστε για εγγραφή"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Συνδέθηκε, χωρίς σύνδεση στο διαδίκτυο"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Δεν είναι δυνατή η πρόσβαση στον ιδιωτικό διακομιστή DNS."</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Περιορισμένη σύνδεση"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Δεν υπάρχει σύνδεση στο διαδίκτυο"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index b314d17..429cd3e 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Connected via <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Available via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Tap to sign up"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Connected, no Internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Private DNS server cannot be accessed"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Limited connection"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"No Internet"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index b314d17..429cd3e 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Connected via <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Available via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Tap to sign up"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Connected, no Internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Private DNS server cannot be accessed"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Limited connection"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"No Internet"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index b314d17..429cd3e 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Connected via <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Available via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Tap to sign up"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Connected, no Internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Private DNS server cannot be accessed"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Limited connection"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"No Internet"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index b314d17..429cd3e 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Connected via <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Available via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Tap to sign up"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Connected, no Internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Private DNS server cannot be accessed"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Limited connection"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"No Internet"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 95944dc..1aa6cdb 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Connected via <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Available via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Tap to sign up"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Connected, no internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Private DNS server cannot be accessed"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Limited connection"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"No internet"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index c6dfdd3..7d28a32 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Conexión a través de <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Disponible a través de %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Presiona para registrarte"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Conectado pero sin conexión a Internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"No se puede acceder al servidor DNS privado"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Conexión limitada"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Sin Internet"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index ba4a9ff..b35696f5 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Conectado a través de <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Disponible a través de %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Toca para registrarte"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Conexión sin Internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"No se ha podido acceder al servidor DNS privado"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Conexión limitada"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Sin Internet"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index df3b792..b6c112b 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Ühendatud võrgu <xliff:g id="NAME">%1$s</xliff:g> kaudu"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Saadaval üksuse %1$s kaudu"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Puudutage registreerumiseks"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Ühendatud, Interneti-ühendus puudub"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Privaatsele DNS-serverile ei pääse juurde"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Piiratud ühendus"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Interneti-ühendus puudub"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index fcb320f..4fd9add 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> bidez konektatuta"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s bidez erabilgarri"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Sakatu erregistratzeko"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Konektatuta; ezin da atzitu Internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Ezin da atzitu DNS zerbitzari pribatua"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Konexio mugatua"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Ez dago Interneteko konexiorik"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 261a438..32a98be 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"متصل شده ازطریق <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"در دسترس از طریق %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"برای ثبتنام ضربه بزنید"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"متصل، بدون اینترنت"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"سرور DNS خصوصی قابل دسترسی نیست"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"اتصال محدود"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"عدم دسترسی به اینترنت"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 4ccf430..84c5372 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Yhdistetty (<xliff:g id="NAME">%1$s</xliff:g>)"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Käytettävissä seuraavan kautta: %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Rekisteröidy napauttamalla"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Yhdistetty, ei internetyhteyttä"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Ei pääsyä yksityiselle DNS-palvelimelle"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Rajallinen yhteys"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Ei internetyhteyttä"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 84a9797..995eab6 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Connecté sur le réseau <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Accessible par %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Toucher pour vous connecter"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Connecté, aucun accès à Internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Impossible d\'accéder au serveur DNS privé"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Connexion limitée"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Aucune connexion Internet"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 030a7f9..2afacab 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Connecté via <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Disponible via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Appuyez ici pour vous connecter"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Connecté, aucun accès à Internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Impossible d\'accéder au serveur DNS privé"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Connexion limitée"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Aucun accès à Internet"</string>
@@ -57,7 +58,7 @@
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Inscription terminée. Connexion…"</string>
<string name="speed_label_very_slow" msgid="8526005255731597666">"Très lente"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Lente"</string>
- <string name="speed_label_okay" msgid="1253594383880810424">"Correct"</string>
+ <string name="speed_label_okay" msgid="1253594383880810424">"Correcte"</string>
<string name="speed_label_medium" msgid="9078405312828606976">"Moyenne"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Élevée"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Très rapide"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index af033cf..a106e60 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Wifi conectada a través de <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Dispoñible a través de %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Toca para rexistrarte"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Conexión sen Internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Non se puido acceder ao servidor DNS privado"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Pouca conexión"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Non hai conexión a Internet"</string>
@@ -316,7 +317,7 @@
<string name="usb_audio_disable_routing" msgid="3367656923544254975">"Desactivar encamiñamento audio USB"</string>
<string name="usb_audio_disable_routing_summary" msgid="8768242894849534699">"Desactiva o encamiñamento automático a periféricos de audio USB"</string>
<string name="debug_layout" msgid="1659216803043339741">"Mostrar límites de deseño"</string>
- <string name="debug_layout_summary" msgid="8825829038287321978">"Mostra os límites dos clips, as marxes, etc."</string>
+ <string name="debug_layout_summary" msgid="8825829038287321978">"Mostra os límites dos clips, as marxes etc."</string>
<string name="force_rtl_layout_all_locales" msgid="8690762598501599796">"Forzar dirección do deseño RTL"</string>
<string name="force_rtl_layout_all_locales_summary" msgid="6663016859517239880">"Forza a dirección de pantalla a RTL (dereita a esquerda) para todas as configuración rexionais"</string>
<string name="force_msaa" msgid="4081288296137775550">"Forzar MSAA 4x"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 87fd876..5f2d5cd 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> દ્વારા કનેક્ટ થયેલ"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s દ્વારા ઉપલબ્ધ"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"સાઇન અપ કરવા માટે ટૅપ કરો"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"કનેક્ટ કર્યું, કોઈ ઇન્ટરનેટ નથી"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"ખાનગી DNS સર્વર ઍક્સેસ કરી શકાતા નથી"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"મર્યાદિત કનેક્શન"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"ઇન્ટરનેટ ઍક્સેસ નથી"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 02b5c96..d60eead 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> के ज़रिए कनेक्ट किया गया"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s के द्वारा उपलब्ध"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"साइन अप करने के लिए टैप करें"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"कनेक्ट हो गया है, लेकिन इंटरनेट नहीं है"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"निजी डीएनएस सर्वर को ऐक्सेस नहीं किया जा सकता"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"सीमित कनेक्शन"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"इंटरनेट कनेक्शन नहीं है"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 598cfe2..28e3460 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Povezan putem mreže <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Dostupno putem %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Dodirnite da biste se registrirali"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Povezano, bez interneta"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Nije moguće pristupiti privatnom DNS poslužitelju"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Ograničena veza"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Nema interneta"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index d970c73..22e03a7 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Kapcsolódva a következőn keresztül: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Elérhető a következőn keresztül: %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Koppintson a regisztrációhoz"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Csatlakozva, nincs internet-hozzáférés"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"A privát DNS-kiszolgálóhoz nem lehet hozzáférni"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Korlátozott kapcsolat"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Nincs internetkapcsolat"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 9086934..ecb615a 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Միացված է <xliff:g id="NAME">%1$s</xliff:g>-ի միջոցով"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Հասանելի է %1$s-ի միջոցով"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Հպեք՝ գրանցվելու համար"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Միացված է, սակայն ինտերնետ կապ չկա"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Մասնավոր DNS սերվերն անհասանելի է"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Սահմանափակ կապ"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Ինտերնետ կապ չկա"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 3c1504c..cfbda04 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Tersambung melalui <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Tersedia melalui %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Ketuk untuk mendaftar"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Tersambung, tidak ada internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Server DNS pribadi tidak dapat diakses"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Koneksi terbatas"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Tidak ada internet"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 438e900..ba4c009 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Tenging í gegnum <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Í boði í gegnum %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Ýttu til að skrá þig"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Tengt, enginn netaðgangur"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Ekki næst í DNS-einkaþjón"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Takmörkuð tenging"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Engin nettenging"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 114b33b..50e5777 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Connesso tramite <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Disponibile tramite %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Tocca per registrarti"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Connesso, senza Internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Non è possibile accedere al server DNS privato"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Connessione limitata"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Nessuna connessione a Internet"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 62085a8..9f1e457 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"מחוברת באמצעות <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"זמינה דרך %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"יש להקיש כדי להירשם"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"מחובר. אין אינטרנט"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"לא ניתן לגשת לשרת DNS הפרטי"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"חיבור מוגבל"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"אין אינטרנט"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 28b98ee..47020ed 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> で接続しました"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s経由で使用可能"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"タップして登録してください"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"接続済み、インターネット接続なし"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"プライベート DNS サーバーにアクセスできません"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"接続が制限されています"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"インターネット未接続"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 6f5d0b3..116488d 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"დაკავშირებულია <xliff:g id="NAME">%1$s</xliff:g>-ით"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"ხელმისაწვდომია %1$s-ით"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"შეეხეთ რეგისტრაციისთვის"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"დაკავშირებულია, ინტერნეტის გარეშე"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"პირად DNS სერვერზე წვდომა შეუძლებელია"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"შეზღუდული კავშირი"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"ინტერნეტ-კავშირი არ არის"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 3fe426e..3c4fdb7 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> арқылы жалғанған"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s арқылы қолжетімді"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Тіркелу үшін түртіңіз."</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Қосылған, интернет жоқ"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Жеке DNS серверіне кіру мүмкін емес."</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Шектеулі байланыс"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Интернетпен байланыс жоқ"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 24bfa35..87a4f19 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"ភ្ជាប់តាម <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"មានតាមរយៈ %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"ចុចដើម្បីចុះឈ្មោះ"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"បានភ្ជាប់ ប៉ុន្តែគ្មានអ៊ីនធឺណិតទេ"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"មិនអាចចូលប្រើម៉ាស៊ីនមេ DNS ឯកជនបានទេ"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"ការតភ្ជាប់មានកម្រិត"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"គ្មានអ៊ីនធឺណិតទេ"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 0699bbc..144dddb 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> ಆ್ಯಪ್ ಮೂಲಕ ಸಂಪರ್ಕಿಸಲಾಗಿದೆ"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s ಮೂಲಕ ಲಭ್ಯವಿದೆ"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"ಸೈನ್ ಅಪ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"ಸಂಪರ್ಕಪಡಿಸಲಾಗಿದೆ, ಇಂಟರ್ನೆಟ್ ಇಲ್ಲ"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"ಖಾಸಗಿ DNS ಸರ್ವರ್ ಅನ್ನು ಪ್ರವೇಶಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"ಸೀಮಿತ ಸಂಪರ್ಕ"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"ಇಂಟರ್ನೆಟ್ ಇಲ್ಲ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 25e9cfe..df9f21d 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g>을(를) 통해 연결됨"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s을(를) 통해 사용 가능"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"탭하여 가입"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"연결됨, 인터넷 사용 불가"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"비공개 DNS 서버에 액세스할 수 없습니다."</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"제한된 연결"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"인터넷 연결 없음"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 3dfce1e..13c4144 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> аркылуу туташты"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s аркылуу жеткиликтүү"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Катталуу үчүн таптап коюңуз"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Туташып турат, Интернет жок"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Жеке DNS сервери жеткиликсиз"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Байланыш чектелген"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Интернет жок"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 48e5093..48224f4 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"ເຊື່ອມຕໍ່ຜ່ານ <xliff:g id="NAME">%1$s</xliff:g> ແລ້ວ"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"ມີໃຫ້ຜ່ານ %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"ແຕະເພື່ອສະໝັກ"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"ເຊື່ອມຕໍ່ແລ້ວ, ບໍ່ມີອິນເຕີເນັດ"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"ບໍ່ສາມາດເຂົ້າເຖິງເຊີບເວີ DNS ສ່ວນຕົວໄດ້"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"ການເຊື່ອມຕໍ່ຈຳກັດ"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"ບໍ່ມີອິນເຕີເນັດ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 8b3fbad..d080334 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Prisijungta naudojant programą „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Pasiekiama naudojant „%1$s“"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Palieskite, kad prisiregistruotumėte"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Prisijungta, nėra interneto"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Privataus DNS serverio negalima pasiekti"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Ribotas ryšys"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Nėra interneto ryšio"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 4fc5b22..e13a50d 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Savienojums ar <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Pieejams, izmantojot %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Pieskarieties, lai reģistrētos"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Savienojums izveidots, nav piekļuves internetam"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Nevar piekļūt privātam DNS serverim."</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Ierobežots savienojums"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Nav piekļuves internetam"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 288e526..d027ffe 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Поврзано преку <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Достапно преку %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Допрете за да се регистрирате"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Поврзана, нема интернет"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Не може да се пристапи до приватниот DNS-сервер"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Ограничена врска"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Нема интернет"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index e775297..e2a3d9f 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> മുഖേന കണക്റ്റ് ചെയ്തു"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s വഴി ലഭ്യം"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"സൈൻ അപ്പ് ചെയ്യാൻ ടാപ്പ് ചെയ്യുക"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"കണക്റ്റ് ചെയ്തു, ഇന്റർനെറ്റ് ഇല്ല"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"സ്വകാര്യ DNS സെർവർ ആക്സസ് ചെയ്യാനാവില്ല"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"പരിമിത കണക്ഷൻ"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"ഇന്റർനെറ്റ് ഇല്ല"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index ba69f9b..dd08c9e 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g>-р холбогдсон"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s-р боломжтой"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Бүртгүүлэхийн тулд товшино уу"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Холбогдсон хэдий ч интернет алга"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Хувийн DNS серверт хандах боломжгүй байна"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Хязгаарлагдмал холболт"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Интернэт алга"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 1930cdf..fb7cd1f 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> द्वारे कनेक्ट केले"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s द्वारे उपलब्ध"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"साइन अप करण्यासाठी टॅप करा"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"कनेक्ट केले, इंटरनेट नाही"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"खाजगी DNS सर्व्हर ॲक्सेस करू शकत नाही"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"मर्यादित कनेक्शन"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"इंटरनेट नाही"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index e493188..72b15ff 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Disambungkan melalui <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Tersedia melalui %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Ketik untuk daftar"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Disambungkan, tiada Internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Pelayan DNS peribadi tidak boleh diakses"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Sambungan terhad"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Tiada Internet"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 2c4b32c..24017ec 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> မှတစ်ဆင့် ချိတ်ဆက်ထားသည်"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s မှတစ်ဆင့်ရနိုင်သည်"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"အကောင့်ဖွင့်ရန် တို့ပါ"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"ချိတ်ဆက်ထားသည်၊ အင်တာနက်မရှိ"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"သီးသန့် ဒီအန်အက်စ် (DNS) ဆာဗာကို သုံး၍မရပါ။"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"ချိတ်ဆက်မှု ကန့်သတ်ထားသည်"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"အင်တာနက် မရှိပါ"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 093c06f..aca13a7 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Tilkoblet via <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Tilgjengelig via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Trykk for å registrere deg"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Tilkoblet – ingen Internett-tilgang"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Den private DNS-tjeneren kan ikke nås"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Begrenset tilkobling"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Ingen internettilkobling"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index fb8b737..b2c7518 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> मार्फत जडान गरिएको"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s मार्फत उपलब्ध"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"साइन अप गर्न ट्याप गर्नुहोस्"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"जडान गरियो तर इन्टरनेट छैन"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"निजी DNS सर्भरमाथि पहुँच प्राप्त गर्न सकिँदैन"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"सीमित जडान"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"इन्टरनेटमाथिको पहुँच छैन"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 267dab4..c74683b 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Verbonden via <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Beschikbaar via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Tik om aan te melden"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Verbonden, geen internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Geen toegang tot privé-DNS-server"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Beperkte verbinding"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Geen internet"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index d8ae3bf..704fc42 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> ଦ୍ବାରା ସଂଯୋଗ କରାଯାଇଛି"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s ମାଧ୍ୟମରେ ଉପଲବ୍ଧ"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"ସାଇନ୍ ଅପ୍ ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"ସଂଯୁକ୍ତ, ଇଣ୍ଟର୍ନେଟ୍ ନାହିଁ"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"ବ୍ୟକ୍ତିଗତ DNS ସର୍ଭର୍ ଆକ୍ସେସ୍ କରିହେବ ନାହିଁ"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"ସୀମିତ ସଂଯୋଗ"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"କୌଣସି ଇଣ୍ଟରନେଟ୍ ନାହିଁ"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 6a78486..bf5a32c 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> ਰਾਹੀਂ ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s ਰਾਹੀਂ ਉਪਲਬਧ"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"ਸਾਈਨ-ਅੱਪ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"ਕਨੈਕਟ ਕੀਤਾ, ਕੋਈ ਇੰਟਰਨੈੱਟ ਨਹੀਂ"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"ਨਿੱਜੀ ਡੋਮੇਨ ਨਾਮ ਪ੍ਰਣਾਲੀ (DNS) ਸਰਵਰ \'ਤੇ ਪਹੁੰਚ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕੀ"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"ਸੀਮਤ ਕਨੈਕਸ਼ਨ"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"ਇੰਟਰਨੈੱਟ ਨਹੀਂ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 8c5547c..d907760 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Połączenie przez: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Dostępne przez %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Kliknij, by się zarejestrować"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Połączono, brak internetu"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Brak dostępu do prywatnego serwera DNS"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Ograniczone połączenie"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Brak internetu"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 8c03616..e61c84e 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Conectado via <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Disponível via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Toque para se inscrever"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Conectada, sem Internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Não é possível acessar o servidor DNS privado"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Conexão limitada"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Sem Internet"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 6aeff1c..8ceeb63 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Ligado via <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Disponível através de %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Toque para se inscrever"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Ligado, sem Internet."</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Não é possível aceder ao servidor DNS."</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Ligação limitada"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Sem Internet"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 8c03616..e61c84e 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Conectado via <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Disponível via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Toque para se inscrever"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Conectada, sem Internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Não é possível acessar o servidor DNS privado"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Conexão limitada"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Sem Internet"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 387441f..5ad8dfd 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Conectat prin <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Disponibilă prin %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Atingeți pentru a vă înscrie"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Conectată, fără internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Serverul DNS privat nu poate fi accesat"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Conexiune limitată"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Fără conexiune la internet"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 361e29f..104efdc 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Подключено через приложение \"<xliff:g id="NAME">%1$s</xliff:g>\"."</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Доступно через %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Нажмите, чтобы зарегистрироваться"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Подключено, без доступа к Интернету"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Доступа к частному DNS-серверу нет."</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Подключение к сети ограничено."</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Нет подключения к Интернету"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index faa848f..f5dad87 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> හරහා සම්බන්ධයි"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s හරහා ලබා ගැනීමට හැකිය"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"ලියාපදිංචි වීමට තට්ටු කරන්න"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"සම්බන්ධයි, අන්තර්ජාලය නැත"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"පුද්ගලික DNS සේවාදායකයට ප්රවේශ වීමට නොහැකිය"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"සීමිත සම්බන්ධතාව"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"අන්තර්ජාලය නැත"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 2035d88c..527dafb 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Pripojené prostredníctvom siete <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"K dispozícii prostredníctvom %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Prihláste sa klepnutím"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Pripojené, žiadny internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"K súkromnému serveru DNS sa nepodarilo získať prístup"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Obmedzené pripojenie"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Žiadny internet"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 2889619..4816950 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Povezava vzpostavljena prek omrežja <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Na voljo prek: %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Dotaknite se, če se želite registrirati"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Vzpostavljena povezava, brez interneta"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Do zasebnega strežnika DNS ni mogoče dostopati"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Omejena povezava"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Brez internetne povezave"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index ccd4e306..12511dd3 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Lidhur përmes <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"E mundshme përmes %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Trokit për t\'u regjistruar"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"U lidh, por nuk ka internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Serveri privat DNS nuk mund të qaset"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Lidhje e kufizuar"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Nuk ka internet"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 08e2bc8..4724be5 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Повезано преко: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Доступна је преко приступне тачке %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Додирните да бисте се регистровали"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Веза је успостављена, нема интернета"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Приступ приватном DNS серверу није успео"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Ограничена веза"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Нема интернета"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index c0cdbc9..9d1bc42 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Anslutet via <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Tillgängligt via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Tryck för att logga in"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Ansluten, inget internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Det går inte att komma åt den privata DNS-servern."</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Begränsad anslutning"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Inget internet"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index f00dea3..1e2489f 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Imeunganishwa kupitia <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Inapatikana kupitia %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Gusa ili ujisajili"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Imeunganishwa, hakuna intaneti"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Seva ya faragha ya DNS haiwezi kufikiwa"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Muunganisho hafifu"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Hakuna intaneti"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 52e0363..fcb801b 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> மூலம் இணைக்கப்பட்டது"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s வழியாகக் கிடைக்கிறது"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"பதிவு செய்யத் தட்டவும்"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"இணைக்கப்பட்டுள்ளது, ஆனால் இண்டர்நெட் இல்லை"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"தனிப்பட்ட DNS சேவையகத்தை அணுக இயலாது"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"வரம்பிற்கு உட்பட்ட இணைப்பு"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"இணைய இணைப்பு இல்லை"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index a39c4e1..7a80451 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> ద్వారా కనెక్ట్ చేయబడింది"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s ద్వారా అందుబాటులో ఉంది"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"సైన్ అప్ చేయడానికి నొక్కండి"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"కనెక్ట్ చేయబడింది, ఇంటర్నెట్ లేదు"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"ప్రైవేట్ DNS సర్వర్ను యాక్సెస్ చేయడం సాధ్యపడదు"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"పరిమిత కనెక్షన్"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"ఇంటర్నెట్ లేదు"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 635d77a..130e1c4 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"เชื่อมต่อแล้วผ่าน <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"พร้อมใช้งานผ่านทาง %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"แตะเพื่อลงชื่อสมัครใช้"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"เชื่อมต่อแล้ว ไม่พบอินเทอร์เน็ต"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"เข้าถึงเซิร์ฟเวอร์ DNS ไม่ได้"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"การเชื่อมต่อที่จำกัด"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"ไม่มีอินเทอร์เน็ต"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 3f7f0ff..bc1a405 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Nakakonekta sa pamamagitan ng <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Available sa pamamagitan ng %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"I-tap para mag-sign up"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Nakakonekta, walang internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Hindi ma-access ang pribadong DNS server"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Limitadong koneksyon"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Walang internet"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 7ad6fcd..4e263ef 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> ile bağlandı"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s üzerinden kullanılabilir"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Kaydolmak için dokunun"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Bağlı, internet yok"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Gizli DNS sunucusuna erişilemiyor"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Sınırlı bağlantı"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"İnternet yok"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index b5dd618..ad9f600 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Підключено через додаток <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Доступ через %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Торкніться, щоб увійти"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Під’єднано, але немає доступу до Інтернету"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Немає доступу до приватного DNS-сервера"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Обмежене з’єднання"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Немає Інтернету"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index ef9b2a1..b995da1 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> کے ذریعے منسلک"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"دستیاب بذریعہ %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"سائن اپ کے لیے تھپتھپائیں"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"منسلک، انٹرنیٹ نہیں ہے"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"نجی DNS سرور تک رسائی حاصل نہیں کی جا سکی"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"محدود کنکشن"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"انٹرنیٹ نہیں ہے"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index b202d64..f02b639 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> orqali ulandi"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s orqali ishlaydi"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Yozilish uchun bosing"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Ulangan, lekin internet aloqasi yo‘q"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Xususiy DNS server ishlamayapti"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Cheklangan aloqa"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Internet yo‘q"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 5bcff5a..8fcf1f2 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Đã kết nối qua <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Có sẵn qua %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Nhấn để đăng ký"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Đã kết nối, không có Internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Không thể truy cập máy chủ DNS riêng tư"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Kết nối giới hạn"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Không có Internet"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 418370b..755cbee 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"已通过<xliff:g id="NAME">%1$s</xliff:g>连接到网络"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"可通过%1$s连接"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"点按即可注册"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"已连接,但无法访问互联网"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"无法访问私人 DNS 服务器"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"网络连接受限"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"无法访问互联网"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 79b5579..e50acf5 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"已透過「<xliff:g id="NAME">%1$s</xliff:g>」連線"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"可透過 %1$s 連線"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"輕按即可登入"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"已連線,但沒有互聯網"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"無法存取私人 DNS 伺服器"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"連線受限"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"沒有互聯網連線"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 47ab764..7df6fed 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"透過「<xliff:g id="NAME">%1$s</xliff:g>」連線"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"可透過 %1$s 使用"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"輕觸即可註冊"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"已連線,沒有網際網路"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"無法存取私人 DNS 伺服器"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"連線能力受限"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"沒有網際網路連線"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 87f45de..46dffbe 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Ixhumeke nge-<xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Iyatholakala nge-%1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Thepha ukuze ubhalisele"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Kuxhunyiwe, ayikho i-inthanethi"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Iseva eyimfihlo ye-DNS ayikwazi ukufinyelelwa"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Iqoqo elikhawulelwe"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Ayikho i-inthanethi"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index e008cd03..a4be46c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -40,15 +40,16 @@
final Executor mExecutor = Executors.newSingleThreadExecutor();
@VisibleForTesting
MediaRouter2Manager mRouterManager;
+ @VisibleForTesting
+ String mPackageName;
- private String mPackageName;
private MediaDevice mCurrentConnectedDevice;
public InfoMediaManager(Context context, String packageName, Notification notification) {
super(context, notification);
mRouterManager = MediaRouter2Manager.getInstance(context);
- if (packageName != null) {
+ if (!TextUtils.isEmpty(packageName)) {
mPackageName = packageName;
}
}
@@ -57,6 +58,7 @@
public void startScan() {
mMediaDevices.clear();
mRouterManager.registerCallback(mExecutor, mMediaRouterCallback);
+ refreshDevices();
}
@VisibleForTesting
@@ -79,21 +81,37 @@
return mCurrentConnectedDevice;
}
- class RouterManagerCallback extends MediaRouter2Manager.Callback {
-
- private void refreshDevices() {
- mMediaDevices.clear();
- mCurrentConnectedDevice = null;
- for (MediaRoute2Info route : mRouterManager.getAvailableRoutes(mPackageName)) {
- final MediaDevice device = new InfoMediaDevice(mContext, mRouterManager, route,
- mPackageName);
- if (TextUtils.equals(route.getClientPackageName(), mPackageName)) {
- mCurrentConnectedDevice = device;
- }
- mMediaDevices.add(device);
- }
- dispatchDeviceListAdded();
+ private void refreshDevices() {
+ mMediaDevices.clear();
+ mCurrentConnectedDevice = null;
+ if (TextUtils.isEmpty(mPackageName)) {
+ buildAllRoutes();
+ } else {
+ buildAvailableRoutes();
}
+ dispatchDeviceListAdded();
+ }
+
+ private void buildAllRoutes() {
+ for (MediaRoute2Info route : mRouterManager.getAllRoutes()) {
+ final MediaDevice device = new InfoMediaDevice(mContext, mRouterManager, route,
+ mPackageName);
+ mMediaDevices.add(device);
+ }
+ }
+
+ private void buildAvailableRoutes() {
+ for (MediaRoute2Info route : mRouterManager.getAvailableRoutes(mPackageName)) {
+ final MediaDevice device = new InfoMediaDevice(mContext, mRouterManager, route,
+ mPackageName);
+ if (TextUtils.equals(route.getClientPackageName(), mPackageName)) {
+ mCurrentConnectedDevice = device;
+ }
+ mMediaDevices.add(device);
+ }
+ }
+
+ class RouterManagerCallback extends MediaRouter2Manager.Callback {
@Override
public void onRoutesAdded(List<MediaRoute2Info> routes) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index e85a102..50196d2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -18,7 +18,6 @@
import android.app.Notification;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
-import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.IntDef;
@@ -162,10 +161,8 @@
mMediaDevices.clear();
mBluetoothMediaManager.registerCallback(mMediaDeviceCallback);
mBluetoothMediaManager.startScan();
- if (!TextUtils.isEmpty(mPackageName)) {
- mInfoMediaManager.registerCallback(mMediaDeviceCallback);
- mInfoMediaManager.startScan();
- }
+ mInfoMediaManager.registerCallback(mMediaDeviceCallback);
+ mInfoMediaManager.startScan();
}
private void addPhoneDeviceIfNecessary() {
@@ -208,10 +205,8 @@
public void stopScan() {
mBluetoothMediaManager.unregisterCallback(mMediaDeviceCallback);
mBluetoothMediaManager.stopScan();
- if (!TextUtils.isEmpty(mPackageName)) {
- mInfoMediaManager.unregisterCallback(mMediaDeviceCallback);
- mInfoMediaManager.stopScan();
- }
+ mInfoMediaManager.unregisterCallback(mMediaDeviceCallback);
+ mInfoMediaManager.stopScan();
}
/**
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index 67f6dd90..3726fb2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -74,7 +74,7 @@
}
@Test
- public void onRouteAdded_shouldAddMediaDevice() {
+ public void onRouteAdded_getAvailableRoutes_shouldAddMediaDevice() {
final MediaRoute2Info info = mock(MediaRoute2Info.class);
when(info.getId()).thenReturn(TEST_ID);
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
@@ -95,6 +95,27 @@
}
@Test
+ public void onRouteAdded_buildAllRoutes_shouldAddMediaDevice() {
+ final MediaRoute2Info info = mock(MediaRoute2Info.class);
+ when(info.getId()).thenReturn(TEST_ID);
+ when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+
+ final List<MediaRoute2Info> routes = new ArrayList<>();
+ routes.add(info);
+ when(mRouterManager.getAllRoutes()).thenReturn(routes);
+
+ final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
+ assertThat(mediaDevice).isNull();
+
+ mInfoMediaManager.mPackageName = "";
+ mInfoMediaManager.mMediaRouterCallback.onRoutesAdded(routes);
+
+ final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
+ assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
+ assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
+ }
+
+ @Test
public void onControlCategoriesChanged_samePackageName_shouldAddMediaDevice() {
final MediaRoute2Info info = mock(MediaRoute2Info.class);
when(info.getId()).thenReturn(TEST_ID);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index cdf9728..3c52f54 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -45,6 +45,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.BackupUtils;
+import android.util.FeatureFlagUtils;
import android.util.Log;
import android.util.Slog;
import android.view.Display;
@@ -280,6 +281,16 @@
Settings.Secure.getMovedToGlobalSettings(movedToGlobal);
Set<String> movedToSecure = getMovedToSecureSettings();
+ Set<String> preservedGlobalSettings = getSettingsToPreserveInRestore(
+ Settings.Global.CONTENT_URI);
+ Set<String> preservedSecureSettings = getSettingsToPreserveInRestore(
+ Settings.Secure.CONTENT_URI);
+ Set<String> preservedSystemSettings = getSettingsToPreserveInRestore(
+ Settings.System.CONTENT_URI);
+ Set<String> preservedSettings = new HashSet<>(preservedGlobalSettings);
+ preservedSettings.addAll(preservedSecureSettings);
+ preservedSettings.addAll(preservedSystemSettings);
+
byte[] restoredWifiSupplicantData = null;
byte[] restoredWifiIpConfigData = null;
@@ -300,7 +311,8 @@
case KEY_SYSTEM :
restoreSettings(data, Settings.System.CONTENT_URI, movedToGlobal,
movedToSecure, R.array.restore_blocked_system_settings,
- dynamicBlockList);
+ dynamicBlockList,
+ preservedSystemSettings);
mSettingsHelper.applyAudioSettings();
break;
@@ -311,7 +323,8 @@
movedToGlobal,
null,
R.array.restore_blocked_secure_settings,
- dynamicBlockList);
+ dynamicBlockList,
+ preservedSecureSettings);
break;
case KEY_GLOBAL :
@@ -321,7 +334,8 @@
null,
movedToSecure,
R.array.restore_blocked_global_settings,
- dynamicBlockList);
+ dynamicBlockList,
+ preservedGlobalSettings);
break;
case KEY_WIFI_SUPPLICANT :
@@ -368,7 +382,8 @@
restoreDeviceSpecificConfig(
restoredDeviceSpecificConfig,
R.array.restore_blocked_device_specific_settings,
- dynamicBlockList);
+ dynamicBlockList,
+ preservedSettings);
break;
default :
@@ -418,7 +433,7 @@
in.readFully(buffer, 0, nBytes);
restoreSettings(buffer, nBytes, Settings.System.CONTENT_URI, movedToGlobal,
movedToSecure, R.array.restore_blocked_system_settings,
- Collections.emptySet());
+ Collections.emptySet(), Collections.emptySet());
// secure settings
nBytes = in.readInt();
@@ -432,7 +447,7 @@
movedToGlobal,
null,
R.array.restore_blocked_secure_settings,
- Collections.emptySet());
+ Collections.emptySet(), Collections.emptySet());
// Global only if sufficiently new
if (version >= FULL_BACKUP_ADDED_GLOBAL) {
@@ -443,7 +458,7 @@
movedToGlobal.clear(); // no redirection; this *is* the global namespace
restoreSettings(buffer, nBytes, Settings.Global.CONTENT_URI, movedToGlobal,
movedToSecure, R.array.restore_blocked_global_settings,
- Collections.emptySet());
+ Collections.emptySet(), Collections.emptySet());
}
// locale
@@ -608,6 +623,40 @@
}
/**
+ * Get names of the settings for which the current value should be preserved during restore.
+ */
+ private Set<String> getSettingsToPreserveInRestore(Uri settingsUri) {
+ if (!FeatureFlagUtils.isEnabled(getApplicationContext(),
+ FeatureFlagUtils.SETTINGS_DO_NOT_RESTORE_PRESERVED)) {
+ return Collections.emptySet();
+ }
+
+ Cursor cursor = getContentResolver().query(settingsUri, new String[] {
+ Settings.NameValueTable.NAME, Settings.NameValueTable.IS_PRESERVED_IN_RESTORE },
+ /* selection */ null, /* selectionArgs */ null, /* sortOrder */ null);
+
+ if (!cursor.moveToFirst()) {
+ Slog.i(TAG, "No settings to be preserved in restore");
+ return Collections.emptySet();
+ }
+
+ int nameIndex = cursor.getColumnIndex(Settings.NameValueTable.NAME);
+ int isPreservedIndex = cursor.getColumnIndex(
+ Settings.NameValueTable.IS_PRESERVED_IN_RESTORE);
+
+ Set<String> preservedSettings = new HashSet<>();
+ while (!cursor.isAfterLast()) {
+ if (Boolean.parseBoolean(cursor.getString(isPreservedIndex))) {
+ preservedSettings.add(getQualifiedKeyForSetting(cursor.getString(nameIndex),
+ settingsUri));
+ }
+ cursor.moveToNext();
+ }
+
+ return preservedSettings;
+ }
+
+ /**
* Serialize the owner info and other lock settings
*/
private byte[] getLockSettings(@UserIdInt int userId) {
@@ -650,7 +699,8 @@
HashSet<String> movedToGlobal,
Set<String> movedToSecure,
int blockedSettingsArrayId,
- Set<String> dynamicBlockList) {
+ Set<String> dynamicBlockList,
+ Set<String> settingsToPreserve) {
byte[] settings = new byte[data.getDataSize()];
try {
data.readEntityData(settings, 0, settings.length);
@@ -665,7 +715,8 @@
movedToGlobal,
movedToSecure,
blockedSettingsArrayId,
- dynamicBlockList);
+ dynamicBlockList,
+ settingsToPreserve);
}
private void restoreSettings(
@@ -675,7 +726,8 @@
HashSet<String> movedToGlobal,
Set<String> movedToSecure,
int blockedSettingsArrayId,
- Set<String> dynamicBlockList) {
+ Set<String> dynamicBlockList,
+ Set<String> settingsToPreserve) {
restoreSettings(
settings,
0,
@@ -684,10 +736,12 @@
movedToGlobal,
movedToSecure,
blockedSettingsArrayId,
- dynamicBlockList);
+ dynamicBlockList,
+ settingsToPreserve);
}
- private void restoreSettings(
+ @VisibleForTesting
+ void restoreSettings(
byte[] settings,
int pos,
int bytes,
@@ -695,31 +749,13 @@
HashSet<String> movedToGlobal,
Set<String> movedToSecure,
int blockedSettingsArrayId,
- Set<String> dynamicBlockList) {
+ Set<String> dynamicBlockList,
+ Set<String> settingsToPreserve) {
if (DEBUG) {
Log.i(TAG, "restoreSettings: " + contentUri);
}
- // Figure out the white list and redirects to the global table. We restore anything
- // in either the backup whitelist or the legacy-restore whitelist for this table.
- final String[] whitelist;
- Map<String, Validator> validators = null;
- if (contentUri.equals(Settings.Secure.CONTENT_URI)) {
- whitelist = ArrayUtils.concatElements(String.class, SecureSettings.SETTINGS_TO_BACKUP,
- Settings.Secure.LEGACY_RESTORE_SETTINGS,
- DeviceSpecificSettings.DEVICE_SPECIFIC_SETTINGS_TO_BACKUP);
- validators = SecureSettingsValidators.VALIDATORS;
- } else if (contentUri.equals(Settings.System.CONTENT_URI)) {
- whitelist = ArrayUtils.concatElements(String.class, SystemSettings.SETTINGS_TO_BACKUP,
- Settings.System.LEGACY_RESTORE_SETTINGS);
- validators = SystemSettingsValidators.VALIDATORS;
- } else if (contentUri.equals(Settings.Global.CONTENT_URI)) {
- whitelist = ArrayUtils.concatElements(String.class, GlobalSettings.SETTINGS_TO_BACKUP,
- Settings.Global.LEGACY_RESTORE_SETTINGS);
- validators = GlobalSettingsValidators.VALIDATORS;
- } else {
- throw new IllegalArgumentException("Unknown URI: " + contentUri);
- }
+ SettingsBackupWhitelist whitelist = getBackupWhitelist(contentUri);
// Restore only the white list data.
final ArrayMap<String, String> cachedEntries = new ArrayMap<>();
@@ -729,7 +765,7 @@
Set<String> blockedSettings = getBlockedSettings(blockedSettingsArrayId);
- for (String key : whitelist) {
+ for (String key : whitelist.mSettingsWhitelist) {
boolean isBlockedBySystem = blockedSettings != null && blockedSettings.contains(key);
if (isBlockedBySystem || isBlockedByDynamicList(dynamicBlockList, contentUri, key)) {
Log.i(
@@ -742,6 +778,12 @@
continue;
}
+ if (settingsToPreserve.contains(getQualifiedKeyForSetting(key, contentUri))) {
+ Log.i(TAG, "Skipping restore for setting " + key + " as it is marked as "
+ + "preserved");
+ continue;
+ }
+
String value = null;
boolean hasValueToRestore = false;
if (cachedEntries.indexOfKey(key) >= 0) {
@@ -775,7 +817,7 @@
}
// only restore the settings that have valid values
- if (!isValidSettingValue(key, value, validators)) {
+ if (!isValidSettingValue(key, value, whitelist.mSettingsValidators)) {
Log.w(TAG, "Attempted restore of " + key + " setting, but its value didn't pass"
+ " validation, value: " + value);
continue;
@@ -798,11 +840,42 @@
}
}
+ @VisibleForTesting
+ SettingsBackupWhitelist getBackupWhitelist(Uri contentUri) {
+ // Figure out the white list and redirects to the global table. We restore anything
+ // in either the backup whitelist or the legacy-restore whitelist for this table.
+ String[] whitelist;
+ Map<String, Validator> validators = null;
+ if (contentUri.equals(Settings.Secure.CONTENT_URI)) {
+ whitelist = ArrayUtils.concatElements(String.class, SecureSettings.SETTINGS_TO_BACKUP,
+ Settings.Secure.LEGACY_RESTORE_SETTINGS,
+ DeviceSpecificSettings.DEVICE_SPECIFIC_SETTINGS_TO_BACKUP);
+ validators = SecureSettingsValidators.VALIDATORS;
+ } else if (contentUri.equals(Settings.System.CONTENT_URI)) {
+ whitelist = ArrayUtils.concatElements(String.class, SystemSettings.SETTINGS_TO_BACKUP,
+ Settings.System.LEGACY_RESTORE_SETTINGS);
+ validators = SystemSettingsValidators.VALIDATORS;
+ } else if (contentUri.equals(Settings.Global.CONTENT_URI)) {
+ whitelist = ArrayUtils.concatElements(String.class, GlobalSettings.SETTINGS_TO_BACKUP,
+ Settings.Global.LEGACY_RESTORE_SETTINGS);
+ validators = GlobalSettingsValidators.VALIDATORS;
+ } else {
+ throw new IllegalArgumentException("Unknown URI: " + contentUri);
+ }
+
+ return new SettingsBackupWhitelist(whitelist, validators);
+ }
+
private boolean isBlockedByDynamicList(Set<String> dynamicBlockList, Uri areaUri, String key) {
String contentKey = Uri.withAppendedPath(areaUri, key).toString();
return dynamicBlockList.contains(contentKey);
}
+ @VisibleForTesting
+ static String getQualifiedKeyForSetting(String settingName, Uri settingUri) {
+ return Uri.withAppendedPath(settingUri, settingName).toString();
+ }
+
// There may be other sources of blocked settings, so I'm separating out this
// code to make it easy to modify in the future.
@VisibleForTesting
@@ -1089,7 +1162,7 @@
*/
@VisibleForTesting
boolean restoreDeviceSpecificConfig(byte[] data, int blockedSettingsArrayId,
- Set<String> dynamicBlocklist) {
+ Set<String> dynamicBlocklist, Set<String> preservedSettings) {
// We're using an AtomicInteger to wrap the position int and allow called methods to
// modify it.
AtomicInteger pos = new AtomicInteger(0);
@@ -1108,7 +1181,8 @@
null,
null,
blockedSettingsArrayId,
- dynamicBlocklist);
+ dynamicBlocklist,
+ preservedSettings);
updateWindowManagerIfNeeded(originalDensity);
@@ -1240,4 +1314,20 @@
| ((in[pos + 3] & 0xFF) << 0);
return result;
}
+
+ /**
+ * Store the whitelist of settings to be backed up and validators for them.
+ */
+ @VisibleForTesting
+ static class SettingsBackupWhitelist {
+ final String[] mSettingsWhitelist;
+ final Map<String, Validator> mSettingsValidators;
+
+
+ SettingsBackupWhitelist(String[] settingsWhitelist,
+ Map<String, Validator> settingsValidators) {
+ mSettingsWhitelist = settingsWhitelist;
+ mSettingsValidators = settingsValidators;
+ }
+ }
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 874e299..c969bfd 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -180,10 +180,17 @@
private static final int MUTATION_OPERATION_UPDATE = 3;
private static final int MUTATION_OPERATION_RESET = 4;
+ private static final String[] LEGACY_SQL_COLUMNS = new String[] {
+ Settings.NameValueTable._ID,
+ Settings.NameValueTable.NAME,
+ Settings.NameValueTable.VALUE,
+ };
+
private static final String[] ALL_COLUMNS = new String[] {
Settings.NameValueTable._ID,
Settings.NameValueTable.NAME,
- Settings.NameValueTable.VALUE
+ Settings.NameValueTable.VALUE,
+ Settings.NameValueTable.IS_PRESERVED_IN_RESTORE,
};
public static final int SETTINGS_TYPE_GLOBAL = SettingsState.SETTINGS_TYPE_GLOBAL;
@@ -2353,6 +2360,10 @@
case Settings.NameValueTable.VALUE: {
values[i] = setting.getValue();
} break;
+
+ case Settings.NameValueTable.IS_PRESERVED_IN_RESTORE: {
+ values[i] = String.valueOf(setting.isValuePreservedInRestore());
+ } break;
}
}
@@ -3097,7 +3108,7 @@
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
queryBuilder.setTables(table);
- Cursor cursor = queryBuilder.query(database, ALL_COLUMNS,
+ Cursor cursor = queryBuilder.query(database, LEGACY_SQL_COLUMNS,
null, null, null, null, null);
if (cursor == null) {
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
index e650882..f5334fb 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
@@ -32,6 +32,8 @@
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
+import android.provider.settings.validators.SettingsValidators;
+import android.provider.settings.validators.Validator;
import android.test.mock.MockContentProvider;
import android.test.mock.MockContentResolver;
@@ -43,6 +45,7 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
@@ -55,12 +58,24 @@
/** Tests for the SettingsHelperTest */
@RunWith(AndroidJUnit4.class)
public class SettingsBackupAgentTest extends BaseSettingsProviderTest {
-
+ private static final Uri TEST_URI = Uri.EMPTY;
private static final String TEST_DISPLAY_DENSITY_FORCED = "123";
+ private static final String OVERRIDDEN_TEST_SETTING = "overridden_setting";
+ private static final String PRESERVED_TEST_SETTING = "preserved_setting";
+ private static final Map<String, String> DEVICE_SPECIFIC_TEST_VALUES = new HashMap<>();
private static final Map<String, String> TEST_VALUES = new HashMap<>();
+ private static final Map<String, Validator> TEST_VALUES_VALIDATORS = new HashMap<>();
static {
- TEST_VALUES.put(Settings.Secure.DISPLAY_DENSITY_FORCED, TEST_DISPLAY_DENSITY_FORCED);
+ DEVICE_SPECIFIC_TEST_VALUES.put(Settings.Secure.DISPLAY_DENSITY_FORCED,
+ TEST_DISPLAY_DENSITY_FORCED);
+
+ TEST_VALUES.put(OVERRIDDEN_TEST_SETTING, "123");
+ TEST_VALUES.put(PRESERVED_TEST_SETTING, "124");
+
+ TEST_VALUES_VALIDATORS.put(OVERRIDDEN_TEST_SETTING,
+ SettingsValidators.ANY_STRING_VALIDATOR);
+ TEST_VALUES_VALIDATORS.put(PRESERVED_TEST_SETTING, SettingsValidators.ANY_STRING_VALIDATOR);
}
private TestFriendlySettingsBackupAgent mAgentUnderTest;
@@ -83,14 +98,15 @@
byte[] settingsBackup = mAgentUnderTest.getDeviceSpecificConfiguration();
- assertEquals("Not all values backed up.", TEST_VALUES.keySet(), helper.mReadEntries);
+ assertEquals("Not all values backed up.", DEVICE_SPECIFIC_TEST_VALUES.keySet(), helper.mReadEntries);
mAgentUnderTest.restoreDeviceSpecificConfig(
settingsBackup,
R.array.restore_blocked_device_specific_settings,
+ Collections.emptySet(),
Collections.emptySet());
- assertEquals("Not all values were restored.", TEST_VALUES, helper.mWrittenValues);
+ assertEquals("Not all values were restored.", DEVICE_SPECIFIC_TEST_VALUES, helper.mWrittenValues);
}
@Test
@@ -100,12 +116,13 @@
byte[] settingsBackup = mAgentUnderTest.getDeviceSpecificConfiguration();
- assertEquals("Not all values backed up.", TEST_VALUES.keySet(), helper.mReadEntries);
- mAgentUnderTest.setBlockedSettings(TEST_VALUES.keySet().toArray(new String[0]));
+ assertEquals("Not all values backed up.", DEVICE_SPECIFIC_TEST_VALUES.keySet(), helper.mReadEntries);
+ mAgentUnderTest.setBlockedSettings(DEVICE_SPECIFIC_TEST_VALUES.keySet().toArray(new String[0]));
mAgentUnderTest.restoreDeviceSpecificConfig(
settingsBackup,
R.array.restore_blocked_device_specific_settings,
+ Collections.emptySet(),
Collections.emptySet());
assertTrue("Not all values were blocked.", helper.mWrittenValues.isEmpty());
@@ -172,9 +189,50 @@
mAgentUnderTest.restoreDeviceSpecificConfig(
data,
R.array.restore_blocked_device_specific_settings,
+ Collections.emptySet(),
Collections.emptySet()));
}
+ @Test
+ public void testOnRestore_preservedSettingsAreNotRestored() {
+ SettingsBackupAgent.SettingsBackupWhitelist whitelist =
+ new SettingsBackupAgent.SettingsBackupWhitelist(
+ new String[] { OVERRIDDEN_TEST_SETTING, PRESERVED_TEST_SETTING },
+ TEST_VALUES_VALIDATORS);
+ mAgentUnderTest.setSettingsWhitelist(whitelist);
+ mAgentUnderTest.setBlockedSettings();
+ TestSettingsHelper settingsHelper = new TestSettingsHelper(mContext);
+ mAgentUnderTest.mSettingsHelper = settingsHelper;
+
+ byte[] backupData = generateBackupData(TEST_VALUES);
+ mAgentUnderTest.restoreSettings(backupData, /* pos */ 0, backupData.length, TEST_URI, new HashSet<>(),
+ Collections.emptySet(), /* blockedSettingsArrayId */ 0, Collections.emptySet(),
+ new HashSet<>(Collections.singletonList(SettingsBackupAgent.getQualifiedKeyForSetting(PRESERVED_TEST_SETTING, TEST_URI))));
+
+ assertTrue(settingsHelper.mWrittenValues.containsKey(OVERRIDDEN_TEST_SETTING));
+ assertFalse(settingsHelper.mWrittenValues.containsKey(PRESERVED_TEST_SETTING));
+ }
+
+ private byte[] generateBackupData(Map<String, String> keyValueData) {
+ int totalBytes = 0;
+ for (String key : keyValueData.keySet()) {
+ totalBytes += 2 * Integer.BYTES + key.getBytes().length
+ + keyValueData.get(key).getBytes().length;
+ }
+
+ ByteBuffer buffer = ByteBuffer.allocate(totalBytes);
+ for (String key : keyValueData.keySet()) {
+ byte[] keyBytes = key.getBytes();
+ byte[] valueBytes = keyValueData.get(key).getBytes();
+ buffer.putInt(keyBytes.length);
+ buffer.put(keyBytes);
+ buffer.putInt(valueBytes.length);
+ buffer.put(valueBytes);
+ }
+
+ return buffer.array();
+ }
+
private byte[] generateUncorruptedHeader() throws IOException {
try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
mAgentUnderTest.writeHeader(os);
@@ -219,6 +277,7 @@
private static class TestFriendlySettingsBackupAgent extends SettingsBackupAgent {
private Boolean mForcedDeviceInfoRestoreAcceptability = null;
private String[] mBlockedSettings = null;
+ private SettingsBackupWhitelist mSettingsWhitelist = null;
void setForcedDeviceInfoRestoreAcceptability(boolean value) {
mForcedDeviceInfoRestoreAcceptability = value;
@@ -228,6 +287,10 @@
mBlockedSettings = blockedSettings;
}
+ void setSettingsWhitelist(SettingsBackupWhitelist settingsWhitelist) {
+ mSettingsWhitelist = settingsWhitelist;
+ }
+
@Override
protected Set<String> getBlockedSettings(int blockedSettingsArrayId) {
return mBlockedSettings == null
@@ -241,6 +304,15 @@
? super.isSourceAcceptable(data, pos)
: mForcedDeviceInfoRestoreAcceptability;
}
+
+ @Override
+ SettingsBackupWhitelist getBackupWhitelist(Uri contentUri) {
+ if (mSettingsWhitelist == null) {
+ return super.getBackupWhitelist(contentUri);
+ }
+
+ return mSettingsWhitelist;
+ }
}
/** The TestSettingsHelper tracks which values have been backed up and/or restored. */
@@ -257,7 +329,7 @@
@Override
public String onBackupValue(String key, String value) {
mReadEntries.add(key);
- String readValue = TEST_VALUES.get(key);
+ String readValue = DEVICE_SPECIFIC_TEST_VALUES.get(key);
assert readValue != null;
return readValue;
}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 26fa1cf..149eaf4 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -646,6 +646,7 @@
android:label="Controls Providers"
android:theme="@style/Theme.SystemUI"
android:exported="true"
+ android:showForAllUsers="true"
android:excludeFromRecents="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
android:visibleToInstantApps="true">
@@ -655,6 +656,7 @@
android:parentActivityName=".controls.management.ControlsProviderSelectorActivity"
android:theme="@style/Theme.SystemUI"
android:excludeFromRecents="true"
+ android:showForAllUsers="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
android:visibleToInstantApps="true">
</activity>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 2aa6d95..d72ce5e 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -328,6 +328,10 @@
<item type="id" name="action_toggle_overview"/>
+ <!-- Whether or not to show notifications to the user. If disabled, SystemUI will still be
+ registered as a notification listener, but will ignore all notification events. -->
+ <bool name="config_renderNotifications">true</bool>
+
<!-- Whether or not the gear icon on notifications should be shown. The gear is shown when the
the notification is not swiped enough to dismiss it. -->
<bool name="config_showNotificationGear">true</bool>
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ControlStatus.kt b/packages/SystemUI/src/com/android/systemui/controls/ControlStatus.kt
index e6cdf50..53841e2 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ControlStatus.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ControlStatus.kt
@@ -18,4 +18,8 @@
import android.service.controls.Control
-data class ControlStatus(val control: Control, val favorite: Boolean, val removed: Boolean = false)
\ No newline at end of file
+data class ControlStatus(
+ val control: Control,
+ val favorite: Boolean,
+ val removed: Boolean = false
+)
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt b/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt
index 265ddd8..588ef5c 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt
@@ -22,7 +22,7 @@
class ControlsServiceInfo(
context: Context,
- serviceInfo: ServiceInfo
+ val serviceInfo: ServiceInfo
) : DefaultAppInfo(
context,
context.packageManager,
diff --git a/packages/SystemUI/src/com/android/systemui/controls/UserAwareController.kt b/packages/SystemUI/src/com/android/systemui/controls/UserAwareController.kt
new file mode 100644
index 0000000..4f39f22
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/UserAwareController.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls
+
+import android.os.UserHandle
+
+interface UserAwareController {
+
+ fun changeUser(newUser: UserHandle) {}
+ val currentUserId: Int
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
index 6b7fc4b..12c3ce9 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
@@ -19,8 +19,9 @@
import android.content.ComponentName
import android.service.controls.Control
import android.service.controls.actions.ControlAction
+import com.android.systemui.controls.UserAwareController
-interface ControlsBindingController {
+interface ControlsBindingController : UserAwareController {
fun bindAndLoad(component: ComponentName, callback: (List<Control>) -> Unit)
fun bindServices(components: List<ComponentName>)
fun subscribe(controls: List<ControlInfo>)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
index 2db2cf1..48d2fa6 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
@@ -19,6 +19,7 @@
import android.content.ComponentName
import android.content.Context
import android.os.IBinder
+import android.os.UserHandle
import android.service.controls.Control
import android.service.controls.IControlsActionCallback
import android.service.controls.IControlsLoadCallback
@@ -50,12 +51,17 @@
private val refreshing = AtomicBoolean(false)
+ private var currentUser = context.user
+
+ override val currentUserId: Int
+ get() = currentUser.identifier
+
@GuardedBy("componentMap")
private val tokenMap: MutableMap<IBinder, ControlsProviderLifecycleManager> =
ArrayMap<IBinder, ControlsProviderLifecycleManager>()
@GuardedBy("componentMap")
- private val componentMap: MutableMap<ComponentName, ControlsProviderLifecycleManager> =
- ArrayMap<ComponentName, ControlsProviderLifecycleManager>()
+ private val componentMap: MutableMap<Key, ControlsProviderLifecycleManager> =
+ ArrayMap<Key, ControlsProviderLifecycleManager>()
private val loadCallbackService = object : IControlsLoadCallback.Stub() {
override fun accept(token: IBinder, controls: MutableList<Control>) {
@@ -103,6 +109,7 @@
loadCallbackService,
actionCallbackService,
subscriberService,
+ currentUser,
component
)
}
@@ -110,7 +117,7 @@
private fun retrieveLifecycleManager(component: ComponentName):
ControlsProviderLifecycleManager {
synchronized(componentMap) {
- val provider = componentMap.getOrPut(component) {
+ val provider = componentMap.getOrPut(Key(component, currentUser)) {
createProviderManager(component)
}
tokenMap.putIfAbsent(provider.token, provider)
@@ -137,7 +144,7 @@
val providersWithFavorites = controlsByComponentName.keys
synchronized(componentMap) {
componentMap.forEach {
- if (it.key !in providersWithFavorites) {
+ if (it.key.component !in providersWithFavorites) {
backgroundExecutor.execute { it.value.unbindService() }
}
}
@@ -167,6 +174,36 @@
}
}
+ override fun changeUser(newUser: UserHandle) {
+ if (newUser == currentUser) return
+ synchronized(componentMap) {
+ unbindAllProvidersLocked() // unbind all providers from the old user
+ }
+ refreshing.set(false)
+ currentUser = newUser
+ }
+
+ private fun unbindAllProvidersLocked() {
+ componentMap.values.forEach {
+ if (it.user == currentUser) {
+ it.unbindService()
+ }
+ }
+ }
+
+ override fun toString(): String {
+ return StringBuilder(" ControlsBindingController:\n").apply {
+ append(" refreshing=${refreshing.get()}\n")
+ append(" currentUser=$currentUser\n")
+ append(" Providers:\n")
+ synchronized(componentMap) {
+ componentMap.values.forEach {
+ append(" $it\n")
+ }
+ }
+ }.toString()
+ }
+
private abstract inner class CallbackRunnable(val token: IBinder) : Runnable {
protected val provider: ControlsProviderLifecycleManager? =
synchronized(componentMap) {
@@ -183,6 +220,10 @@
Log.e(TAG, "No provider found for token:$token")
return
}
+ if (provider.user != currentUser) {
+ Log.e(TAG, "User ${provider.user} is not current user")
+ return
+ }
synchronized(componentMap) {
if (token !in tokenMap.keys) {
Log.e(TAG, "Provider for token:$token does not exist anymore")
@@ -204,6 +245,10 @@
if (!refreshing.get()) {
Log.d(TAG, "onRefresh outside of window from:${provider?.componentName}")
}
+ if (provider?.user != currentUser) {
+ Log.e(TAG, "User ${provider?.user} is not current user")
+ return
+ }
provider?.let {
lazyController.get().refreshStatus(it.componentName, control)
}
@@ -229,7 +274,7 @@
) : CallbackRunnable(token) {
override fun run() {
provider?.let {
- Log.i(TAG, "onComplete receive from '${provider?.componentName}'")
+ Log.i(TAG, "onComplete receive from '${provider.componentName}'")
}
}
}
@@ -240,7 +285,7 @@
) : CallbackRunnable(token) {
override fun run() {
provider?.let {
- Log.e(TAG, "onError receive from '${provider?.componentName}': $error")
+ Log.e(TAG, "onError receive from '${provider.componentName}': $error")
}
}
}
@@ -251,9 +296,15 @@
@ControlAction.ResponseResult val response: Int
) : CallbackRunnable(token) {
override fun run() {
+ if (provider?.user != currentUser) {
+ Log.e(TAG, "User ${provider?.user} is not current user")
+ return
+ }
provider?.let {
lazyController.get().onActionResponse(it.componentName, controlId, response)
}
}
}
}
+
+private data class Key(val component: ComponentName, val user: UserHandle)
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
index e098faa..b02de45 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
@@ -20,8 +20,9 @@
import android.service.controls.Control
import android.service.controls.actions.ControlAction
import com.android.systemui.controls.ControlStatus
+import com.android.systemui.controls.UserAwareController
-interface ControlsController {
+interface ControlsController : UserAwareController {
val available: Boolean
fun getFavoriteControls(): List<ControlInfo>
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index d5b5b5f..6ff1cf8 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -17,10 +17,13 @@
package com.android.systemui.controls.controller
import android.app.PendingIntent
+import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.Intent
+import android.content.IntentFilter
import android.os.Environment
+import android.os.UserHandle
import android.provider.Settings
import android.service.controls.Control
import android.service.controls.actions.ControlAction
@@ -29,14 +32,17 @@
import com.android.internal.annotations.GuardedBy
import com.android.systemui.DumpController
import com.android.systemui.Dumpable
+import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.controls.ControlStatus
import com.android.systemui.controls.management.ControlsFavoritingActivity
+import com.android.systemui.controls.management.ControlsListingController
import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.util.concurrency.DelayableExecutor
import java.io.FileDescriptor
import java.io.PrintWriter
import java.util.Optional
+import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.inject.Singleton
@@ -46,35 +52,101 @@
@Background private val executor: DelayableExecutor,
private val uiController: ControlsUiController,
private val bindingController: ControlsBindingController,
- private val optionalWrapper: Optional<ControlsFavoritePersistenceWrapper>,
+ private val listingController: ControlsListingController,
+ broadcastDispatcher: BroadcastDispatcher,
+ optionalWrapper: Optional<ControlsFavoritePersistenceWrapper>,
dumpController: DumpController
) : Dumpable, ControlsController {
companion object {
private const val TAG = "ControlsControllerImpl"
const val CONTROLS_AVAILABLE = "systemui.controls_available"
+ const val USER_CHANGE_RETRY_DELAY = 500L // ms
}
- override val available = Settings.Secure.getInt(
+ // Map of map: ComponentName -> (String -> ControlInfo).
+ // Only for current user
+ @GuardedBy("currentFavorites")
+ private val currentFavorites = ArrayMap<ComponentName, MutableMap<String, ControlInfo>>()
+
+ private var userChanging = true
+ override var available = Settings.Secure.getInt(
context.contentResolver, CONTROLS_AVAILABLE, 0) != 0
- val persistenceWrapper = optionalWrapper.orElseGet {
+ private set
+
+ private var currentUser = context.user
+ override val currentUserId
+ get() = currentUser.identifier
+
+ private val persistenceWrapper = optionalWrapper.orElseGet {
ControlsFavoritePersistenceWrapper(
Environment.buildPath(
- context.filesDir,
- ControlsFavoritePersistenceWrapper.FILE_NAME),
+ context.filesDir,
+ ControlsFavoritePersistenceWrapper.FILE_NAME
+ ),
executor
)
}
- // Map of map: ComponentName -> (String -> ControlInfo)
- @GuardedBy("currentFavorites")
- private val currentFavorites = ArrayMap<ComponentName, MutableMap<String, ControlInfo>>()
-
- init {
+ private fun setValuesForUser(newUser: UserHandle) {
+ Log.d(TAG, "Changing to user: $newUser")
+ currentUser = newUser
+ val userContext = context.createContextAsUser(currentUser, 0)
+ val fileName = Environment.buildPath(
+ userContext.filesDir, ControlsFavoritePersistenceWrapper.FILE_NAME)
+ persistenceWrapper.changeFile(fileName)
+ available = Settings.Secure.getIntForUser(
+ context.contentResolver, CONTROLS_AVAILABLE, 0) != 0
+ synchronized(currentFavorites) {
+ currentFavorites.clear()
+ }
if (available) {
- dumpController.registerDumpable(this)
loadFavorites()
}
+ bindingController.changeUser(newUser)
+ listingController.changeUser(newUser)
+ userChanging = false
+ }
+
+ private val userSwitchReceiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ if (intent.action == Intent.ACTION_USER_SWITCHED) {
+ userChanging = true
+ val newUser =
+ UserHandle.of(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, sendingUserId))
+ if (currentUser == newUser) {
+ userChanging = false
+ return
+ }
+ setValuesForUser(newUser)
+ }
+ }
+ }
+
+ init {
+ dumpController.registerDumpable(this)
+ if (available) {
+ loadFavorites()
+ }
+ userChanging = false
+ broadcastDispatcher.registerReceiver(
+ userSwitchReceiver,
+ IntentFilter(Intent.ACTION_USER_SWITCHED),
+ executor,
+ UserHandle.ALL
+ )
+ }
+
+ private fun confirmAvailability(): Boolean {
+ if (userChanging) {
+ Log.w(TAG, "Controls not available while user is changing")
+ return false
+ }
+ if (!available) {
+ Log.d(TAG, "Controls not available")
+ return false
+ }
+ return true
}
private fun loadFavorites() {
@@ -91,8 +163,18 @@
componentName: ComponentName,
callback: (List<ControlStatus>) -> Unit
) {
- if (!available) {
- Log.d(TAG, "Controls not available")
+ if (!confirmAvailability()) {
+ if (userChanging) {
+ // Try again later, userChanging should not last forever. If so, we have bigger
+ // problems
+ executor.executeDelayed(
+ { loadForComponent(componentName, callback) },
+ USER_CHANGE_RETRY_DELAY,
+ TimeUnit.MILLISECONDS
+ )
+ } else {
+ callback(emptyList())
+ }
return
}
bindingController.bindAndLoad(componentName) {
@@ -158,10 +240,7 @@
}
override fun subscribeToFavorites() {
- if (!available) {
- Log.d(TAG, "Controls not available")
- return
- }
+ if (!confirmAvailability()) return
// Make a copy of the favorites list
val favorites = synchronized(currentFavorites) {
currentFavorites.flatMap { it.value.values.toList() }
@@ -170,18 +249,12 @@
}
override fun unsubscribe() {
- if (!available) {
- Log.d(TAG, "Controls not available")
- return
- }
+ if (!confirmAvailability()) return
bindingController.unsubscribe()
}
override fun changeFavoriteStatus(controlInfo: ControlInfo, state: Boolean) {
- if (!available) {
- Log.d(TAG, "Controls not available")
- return
- }
+ if (!confirmAvailability()) return
var changed = false
val listOfControls = synchronized(currentFavorites) {
if (state) {
@@ -211,7 +284,7 @@
}
override fun refreshStatus(componentName: ComponentName, control: Control) {
- if (!available) {
+ if (!confirmAvailability()) {
Log.d(TAG, "Controls not available")
return
}
@@ -227,28 +300,24 @@
}
override fun onActionResponse(componentName: ComponentName, controlId: String, response: Int) {
- if (!available) {
- Log.d(TAG, "Controls not available")
- return
- }
+ if (!confirmAvailability()) return
uiController.onActionResponse(componentName, controlId, response)
}
override fun getFavoriteControls(): List<ControlInfo> {
- if (!available) {
- Log.d(TAG, "Controls not available")
- return emptyList()
- }
+ if (!confirmAvailability()) return emptyList()
synchronized(currentFavorites) {
return favoritesAsListLocked()
}
}
override fun action(controlInfo: ControlInfo, action: ControlAction) {
+ if (!confirmAvailability()) return
bindingController.action(controlInfo, action)
}
override fun clearFavorites() {
+ if (!confirmAvailability()) return
val changed = synchronized(currentFavorites) {
currentFavorites.isNotEmpty().also {
currentFavorites.clear()
@@ -261,6 +330,9 @@
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
pw.println("ControlsController state:")
+ pw.println(" Available: $available")
+ pw.println(" Changing users: $userChanging")
+ pw.println(" Current user: ${currentUser.identifier}")
pw.println(" Favorites:")
synchronized(currentFavorites) {
currentFavorites.forEach {
@@ -269,5 +341,6 @@
}
}
}
+ pw.println(bindingController.toString())
}
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapper.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapper.kt
index 6f2d71f..7d1df14 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapper.kt
@@ -16,7 +16,6 @@
package com.android.systemui.controls.controller
-import android.app.ActivityManager
import android.content.ComponentName
import android.util.AtomicFile
import android.util.Log
@@ -32,8 +31,8 @@
import java.io.IOException
class ControlsFavoritePersistenceWrapper(
- val file: File,
- val executor: DelayableExecutor
+ private var file: File,
+ private var executor: DelayableExecutor
) {
companion object {
@@ -47,11 +46,13 @@
private const val TAG_TYPE = "type"
}
- val currentUser: Int
- get() = ActivityManager.getCurrentUser()
+ fun changeFile(fileName: File) {
+ file = fileName
+ }
fun storeFavorites(list: List<ControlInfo>) {
executor.execute {
+ Log.d(TAG, "Saving data to file: $file")
val atomicFile = AtomicFile(file)
val writer = try {
atomicFile.startWrite()
@@ -98,6 +99,7 @@
return emptyList()
}
try {
+ Log.d(TAG, "Reading data from file: $file")
val parser = Xml.newPullParser()
parser.setInput(reader, null)
return parseXml(parser)
@@ -111,7 +113,7 @@
}
private fun parseXml(parser: XmlPullParser): List<ControlInfo> {
- var type: Int = 0
+ var type = 0
val infos = mutableListOf<ControlInfo>()
while (parser.next().also { type = it } != XmlPullParser.END_DOCUMENT) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
@@ -123,9 +125,9 @@
parser.getAttributeValue(null, TAG_COMPONENT))
val id = parser.getAttributeValue(null, TAG_ID)
val title = parser.getAttributeValue(null, TAG_TITLE)
- val type = parser.getAttributeValue(null, TAG_TYPE)?.toInt()
- if (component != null && id != null && title != null && type != null) {
- infos.add(ControlInfo(component, id, title, type))
+ val deviceType = parser.getAttributeValue(null, TAG_TYPE)?.toInt()
+ if (component != null && id != null && title != null && deviceType != null) {
+ infos.add(ControlInfo(component, id, title, deviceType))
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
index 99aa360..739ca7e 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
@@ -24,6 +24,7 @@
import android.os.Bundle
import android.os.IBinder
import android.os.RemoteException
+import android.os.UserHandle
import android.service.controls.Control
import android.service.controls.ControlsProviderService.CALLBACK_BUNDLE
import android.service.controls.ControlsProviderService.CALLBACK_TOKEN
@@ -46,6 +47,7 @@
private val loadCallbackService: IControlsLoadCallback.Stub,
private val actionCallbackService: IControlsActionCallback.Stub,
private val subscriberService: IControlsSubscriber.Stub,
+ val user: UserHandle,
val componentName: ComponentName
) : IBinder.DeathRecipient {
@@ -96,7 +98,7 @@
}
bindTryCount++
try {
- isBound = context.bindService(intent, serviceConnection, BIND_FLAGS)
+ isBound = context.bindServiceAsUser(intent, serviceConnection, BIND_FLAGS, user)
} catch (e: SecurityException) {
Log.e(TAG, "Failed to bind to service", e)
isBound = false
@@ -152,7 +154,9 @@
load()
}
queue.filter { it is Message.Subscribe }.flatMap { (it as Message.Subscribe).list }.run {
- subscribe(this)
+ if (this.isNotEmpty()) {
+ subscribe(this)
+ }
}
queue.filter { it is Message.Action }.forEach {
val msg = it as Message.Action
@@ -286,6 +290,15 @@
maybeUnbindAndRemoveCallback()
}
+ override fun toString(): String {
+ return StringBuilder("ControlsProviderLifecycleManager(").apply {
+ append("component=$componentName")
+ append(", user=$user")
+ append(", bound=$isBound")
+ append(")")
+ }.toString()
+ }
+
sealed class Message {
abstract val type: Int
object Load : Message() {
@@ -301,4 +314,4 @@
override val type = MSG_ACTION
}
}
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
index d62bb4d..22c6908 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
@@ -23,6 +23,7 @@
import android.widget.ImageView
import android.widget.TextView
import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.RecyclerView
import com.android.settingslib.widget.CandidateInfo
import com.android.systemui.R
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
index 01c4fef..7ee4fd5 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
@@ -22,15 +22,18 @@
import android.view.LayoutInflater
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
+import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.controls.controller.ControlInfo
import com.android.systemui.controls.controller.ControlsControllerImpl
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.settings.CurrentUserTracker
import java.util.concurrent.Executor
import javax.inject.Inject
class ControlsFavoritingActivity @Inject constructor(
@Main private val executor: Executor,
- private val controller: ControlsControllerImpl
+ private val controller: ControlsControllerImpl,
+ broadcastDispatcher: BroadcastDispatcher
) : Activity() {
companion object {
@@ -42,11 +45,24 @@
private lateinit var recyclerView: RecyclerView
private lateinit var adapter: ControlAdapter
+ private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) {
+ private val startingUser = controller.currentUserId
+
+ override fun onUserSwitched(newUserId: Int) {
+ if (newUserId != startingUser) {
+ stopTracking()
+ finish()
+ }
+ }
+ }
+
+ private var component: ComponentName? = null
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val app = intent.getCharSequenceExtra(EXTRA_APP)
- val component = intent.getParcelableExtra<ComponentName>(EXTRA_COMPONENT)
+ component = intent.getParcelableExtra<ComponentName>(EXTRA_COMPONENT)
// If we have no component name, there's not much we can do.
val callback = component?.let {
@@ -68,6 +84,11 @@
}
setContentView(recyclerView)
+ currentUserTracker.startTracking()
+ }
+
+ override fun onResume() {
+ super.onResume()
component?.let {
controller.loadForComponent(it) {
executor.execute {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt
index 09e0ce9..34db684 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt
@@ -18,14 +18,17 @@
import android.content.ComponentName
import com.android.settingslib.widget.CandidateInfo
+import com.android.systemui.controls.UserAwareController
import com.android.systemui.statusbar.policy.CallbackController
interface ControlsListingController :
- CallbackController<ControlsListingController.ControlsListingCallback> {
+ CallbackController<ControlsListingController.ControlsListingCallback>,
+ UserAwareController {
fun getCurrentServices(): List<CandidateInfo>
fun getAppLabel(name: ComponentName): CharSequence? = ""
+ @FunctionalInterface
interface ControlsListingCallback {
fun onServicesUpdated(list: List<CandidateInfo>)
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
index 3949c592..882382c 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
@@ -19,6 +19,7 @@
import android.content.ComponentName
import android.content.Context
import android.content.pm.ServiceInfo
+import android.os.UserHandle
import android.service.controls.ControlsProviderService
import android.util.Log
import com.android.internal.annotations.VisibleForTesting
@@ -31,6 +32,16 @@
import javax.inject.Inject
import javax.inject.Singleton
+private fun createServiceListing(context: Context): ServiceListing {
+ return ServiceListing.Builder(context).apply {
+ setIntentAction(ControlsProviderService.SERVICE_CONTROLS)
+ setPermission("android.permission.BIND_CONTROLS")
+ setNoun("Controls Provider")
+ setSetting("controls_providers")
+ setTag("controls_providers")
+ }.build()
+}
+
/**
* Provides a listing of components to be used as ControlsServiceProvider.
*
@@ -43,41 +54,55 @@
class ControlsListingControllerImpl @VisibleForTesting constructor(
private val context: Context,
@Background private val backgroundExecutor: Executor,
- private val serviceListing: ServiceListing
+ private val serviceListingBuilder: (Context) -> ServiceListing
) : ControlsListingController {
@Inject
constructor(context: Context, executor: Executor): this(
context,
executor,
- ServiceListing.Builder(context)
- .setIntentAction(ControlsProviderService.SERVICE_CONTROLS)
- .setPermission("android.permission.BIND_CONTROLS")
- .setNoun("Controls Provider")
- .setSetting("controls_providers")
- .setTag("controls_providers")
- .build()
+ ::createServiceListing
)
+ private var serviceListing = serviceListingBuilder(context)
+
companion object {
private const val TAG = "ControlsListingControllerImpl"
}
private var availableServices = emptyList<ServiceInfo>()
- init {
- serviceListing.addCallback {
- Log.d(TAG, "ServiceConfig reloaded")
- availableServices = it.toList()
+ override var currentUserId = context.userId
+ private set
- backgroundExecutor.execute {
- callbacks.forEach {
- it.onServicesUpdated(getCurrentServices())
- }
+ private val serviceListingCallback = ServiceListing.Callback {
+ Log.d(TAG, "ServiceConfig reloaded")
+ availableServices = it.toList()
+
+ backgroundExecutor.execute {
+ callbacks.forEach {
+ it.onServicesUpdated(getCurrentServices())
}
}
}
+ init {
+ serviceListing.addCallback(serviceListingCallback)
+ }
+
+ override fun changeUser(newUser: UserHandle) {
+ backgroundExecutor.execute {
+ callbacks.clear()
+ availableServices = emptyList()
+ serviceListing.setListening(false)
+ serviceListing.removeCallback(serviceListingCallback)
+ currentUserId = newUser.identifier
+ val contextForUser = context.createContextAsUser(newUser, 0)
+ serviceListing = serviceListingBuilder(contextForUser)
+ serviceListing.addCallback(serviceListingCallback)
+ }
+ }
+
// All operations in background thread
private val callbacks = mutableSetOf<ControlsListingController.ControlsListingCallback>()
@@ -91,6 +116,7 @@
*/
override fun addCallback(listener: ControlsListingController.ControlsListingCallback) {
backgroundExecutor.execute {
+ Log.d(TAG, "Subscribing callback")
callbacks.add(listener)
if (callbacks.size == 1) {
serviceListing.setListening(true)
@@ -108,6 +134,7 @@
*/
override fun removeCallback(listener: ControlsListingController.ControlsListingCallback) {
backgroundExecutor.execute {
+ Log.d(TAG, "Unsubscribing callback")
callbacks.remove(listener)
if (callbacks.size == 0) {
serviceListing.setListening(false)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
index 69af516..5ff949c 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
@@ -22,7 +22,10 @@
import android.view.LayoutInflater
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.settings.CurrentUserTracker
import com.android.systemui.util.LifecycleActivity
import java.util.concurrent.Executor
import javax.inject.Inject
@@ -32,7 +35,9 @@
*/
class ControlsProviderSelectorActivity @Inject constructor(
@Main private val executor: Executor,
- private val listingController: ControlsListingController
+ @Background private val backExecutor: Executor,
+ private val listingController: ControlsListingController,
+ broadcastDispatcher: BroadcastDispatcher
) : LifecycleActivity() {
companion object {
@@ -40,6 +45,16 @@
}
private lateinit var recyclerView: RecyclerView
+ private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) {
+ private val startingUser = listingController.currentUserId
+
+ override fun onUserSwitched(newUserId: Int) {
+ if (newUserId != startingUser) {
+ stopTracking()
+ finish()
+ }
+ }
+ }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -50,6 +65,7 @@
recyclerView.layoutManager = LinearLayoutManager(applicationContext)
setContentView(recyclerView)
+ currentUserTracker.startTracking()
}
/**
@@ -57,13 +73,17 @@
* @param component a component name for a [ControlsProviderService]
*/
fun launchFavoritingActivity(component: ComponentName?) {
- component?.let {
- val intent = Intent(applicationContext, ControlsFavoritingActivity::class.java).apply {
- putExtra(ControlsFavoritingActivity.EXTRA_APP, listingController.getAppLabel(it))
- putExtra(ControlsFavoritingActivity.EXTRA_COMPONENT, it)
- flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP
+ backExecutor.execute {
+ component?.let {
+ val intent = Intent(applicationContext, ControlsFavoritingActivity::class.java)
+ .apply {
+ putExtra(ControlsFavoritingActivity.EXTRA_APP,
+ listingController.getAppLabel(it))
+ putExtra(ControlsFavoritingActivity.EXTRA_COMPONENT, it)
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP
+ }
+ startActivity(intent)
}
- startActivity(intent)
}
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index f793b3d..7b54199 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -38,6 +38,7 @@
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
+import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.notification.people.PeopleHubModule;
import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
import com.android.systemui.statusbar.phone.KeyguardLiftController;
@@ -64,6 +65,7 @@
AssistModule.class,
ConcurrencyModule.class,
LogModule.class,
+ NotificationsModule.class,
PeopleHubModule.class,
},
subcomponents = {StatusBarComponent.class, NotificationRowComponent.class})
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index 43db85b..6f655bb 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -118,6 +118,7 @@
new DozeWallpaperState(mWallpaperManager, mBiometricUnlockController,
mDozeParameters),
new DozeDockHandler(config, machine, mDockManager),
+ new DozeSuppressedHandler(dozeService, config, machine),
new DozeAuthRemover(dozeService)
});
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 8afdf1a..96ae6c9 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -266,6 +266,14 @@
mLogger.logSensorTriggered(reason);
}
+ /**
+ * Appends doze suppressed event to the logs
+ * @param suppressedState The {@link DozeMachine.State} that was suppressed
+ */
+ public void traceDozeSuppressed(DozeMachine.State suppressedState) {
+ mLogger.logDozeSuppressed(suppressedState);
+ }
+
private class SummaryStats {
private int mCount;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
index 42decd5..732745a 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
@@ -194,6 +194,14 @@
"Sensor triggered, type=${reasonToString(int1)}"
})
}
+
+ fun logDozeSuppressed(state: DozeMachine.State) {
+ buffer.log(TAG, INFO, {
+ str1 = state.name
+ }, {
+ "Doze state suppressed, state=$str1"
+ })
+ }
}
private const val TAG = "DozeLog"
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index 03c25ee..6e81d3a 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -102,6 +102,10 @@
}
}
+ boolean isAlwaysOn() {
+ return this == DOZE_AOD || this == DOZE_AOD_DOCKED;
+ }
+
int screenState(DozeParameters parameters) {
switch (this) {
case UNINITIALIZED:
@@ -324,6 +328,11 @@
if (mState == State.FINISH) {
return State.FINISH;
}
+ if (mConfig.dozeSuppressed(UserHandle.USER_CURRENT) && requestedState.isAlwaysOn()) {
+ Log.i(TAG, "Doze is suppressed. Suppressing state: " + requestedState);
+ mDozeLog.traceDozeSuppressed(requestedState);
+ return State.DOZE;
+ }
if ((mState == State.DOZE_AOD_PAUSED || mState == State.DOZE_AOD_PAUSING
|| mState == State.DOZE_AOD || mState == State.DOZE)
&& requestedState == State.DOZE_PULSE_DONE) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
index 3abeea9..e50f1fb 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
@@ -18,7 +18,6 @@
import static com.android.systemui.doze.DozeMachine.State.DOZE;
import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD;
-import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_DOCKED;
import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED;
import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSING;
import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSE_DONE;
@@ -90,10 +89,10 @@
}
final boolean messagePending = mHandler.hasCallbacks(mApplyPendingScreenState);
- final boolean pulseEnding = oldState == DOZE_PULSE_DONE && isAlwaysOnState(newState);
+ final boolean pulseEnding = oldState == DOZE_PULSE_DONE && newState.isAlwaysOn();
final boolean turningOn = (oldState == DOZE_AOD_PAUSED || oldState == DOZE)
- && isAlwaysOnState(newState);
- final boolean turningOff = (isAlwaysOnState(oldState) && newState == DOZE)
+ && newState.isAlwaysOn();
+ final boolean turningOff = (newState.isAlwaysOn() && newState == DOZE)
|| (oldState == DOZE_AOD_PAUSING && newState == DOZE_AOD_PAUSED);
final boolean justInitialized = oldState == DozeMachine.State.INITIALIZED;
if (messagePending || justInitialized || pulseEnding || turningOn) {
@@ -132,10 +131,6 @@
}
}
- private boolean isAlwaysOnState(DozeMachine.State state) {
- return state == DOZE_AOD || state == DOZE_AOD_DOCKED;
- }
-
private void applyPendingScreenState() {
applyScreenState(mPendingScreenState);
mPendingScreenState = Display.STATE_UNKNOWN;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSuppressedHandler.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSuppressedHandler.java
new file mode 100644
index 0000000..3a5c1a0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSuppressedHandler.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.doze;
+
+import static java.util.Objects.requireNonNull;
+
+import android.app.ActivityManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.hardware.display.AmbientDisplayConfiguration;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/** Handles updating the doze state when doze is suppressed. */
+public final class DozeSuppressedHandler implements DozeMachine.Part {
+
+ private static final String TAG = DozeSuppressedHandler.class.getSimpleName();
+ private static final boolean DEBUG = DozeService.DEBUG;
+
+ private final ContentResolver mResolver;
+ private final AmbientDisplayConfiguration mConfig;
+ private final DozeMachine mMachine;
+ private final DozeSuppressedSettingObserver mSettingObserver;
+ private final Handler mHandler = new Handler();
+
+ public DozeSuppressedHandler(Context context, AmbientDisplayConfiguration config,
+ DozeMachine machine) {
+ this(context, config, machine, null);
+ }
+
+ @VisibleForTesting
+ DozeSuppressedHandler(Context context, AmbientDisplayConfiguration config, DozeMachine machine,
+ DozeSuppressedSettingObserver observer) {
+ mResolver = context.getContentResolver();
+ mConfig = requireNonNull(config);
+ mMachine = requireNonNull(machine);
+ if (observer == null) {
+ mSettingObserver = new DozeSuppressedSettingObserver(mHandler);
+ } else {
+ mSettingObserver = observer;
+ }
+ }
+
+ @Override
+ public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
+ switch (newState) {
+ case INITIALIZED:
+ mSettingObserver.register();
+ break;
+ case FINISH:
+ mSettingObserver.unregister();
+ break;
+ default:
+ // no-op
+ }
+ }
+
+ /**
+ * Listens to changes to the DOZE_SUPPRESSED secure setting and updates the doze state
+ * accordingly.
+ */
+ final class DozeSuppressedSettingObserver extends ContentObserver {
+ private boolean mRegistered;
+
+ private DozeSuppressedSettingObserver(Handler handler) {
+ super(handler);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri, int userId) {
+ if (userId != ActivityManager.getCurrentUser()) {
+ return;
+ }
+ final DozeMachine.State nextState;
+ if (mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT)
+ && !mConfig.dozeSuppressed(UserHandle.USER_CURRENT)) {
+ nextState = DozeMachine.State.DOZE_AOD;
+ } else {
+ nextState = DozeMachine.State.DOZE;
+ }
+ mMachine.requestState(nextState);
+ }
+
+ void register() {
+ if (mRegistered) {
+ return;
+ }
+ mResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.SUPPRESS_DOZE),
+ false, this, UserHandle.USER_CURRENT);
+ Log.d(TAG, "Register");
+ mRegistered = true;
+ }
+
+ void unregister() {
+ if (!mRegistered) {
+ return;
+ }
+ mResolver.unregisterContentObserver(this);
+ Log.d(TAG, "Unregister");
+ mRegistered = false;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index b9c056d..305a4c8 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -127,6 +127,11 @@
mDozeLog.tracePulseDropped("pulseOnNotificationsDisabled");
return;
}
+ if (mConfig.dozeSuppressed(UserHandle.USER_CURRENT)) {
+ runIfNotNull(onPulseSuppressedListener);
+ mDozeLog.tracePulseDropped("dozeSuppressed");
+ return;
+ }
requestPulse(DozeLog.PULSE_REASON_NOTIFICATION, false /* performedProxCheck */,
onPulseSuppressedListener);
mDozeLog.traceNotificationPulse();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index 3fcd1c1..476af20 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -116,6 +116,7 @@
@Override
public void onClick(View v) {
+ if (!hasFooter()) return;
mHandler.sendEmptyMessage(H.CLICK);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt
index 015c323..269a7a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt
@@ -43,10 +43,10 @@
private val headsUpManager: HeadsUpManagerPhone,
private val notificationLockscreenUserManager: NotificationLockscreenUserManager,
private val mediaManager: NotificationMediaManager,
+ private val entryManager: NotificationEntryManager,
tunerService: TunerService
) : StatusBarStateController.StateListener, NotificationMediaManager.MediaListener {
- private lateinit var entryManager: NotificationEntryManager
private var currentMediaEntry: NotificationEntry? = null
private var enabled = true
@@ -70,8 +70,7 @@
}, Settings.Secure.SHOW_MEDIA_WHEN_BYPASSING)
}
- fun setUp(entryManager: NotificationEntryManager) {
- this.entryManager = entryManager
+ fun setUp() {
mediaManager.addCallback(this)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 8ac4d30..cb8cef8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -92,8 +92,8 @@
* {@link #addNotificationLifetimeExtender(NotifLifetimeExtender)}).
*
* Interested parties can register listeners
- * ({@link #addCollectionListener(NotifCollectionListener)}) to be informed when notifications are
- * added, updated, or removed.
+ * ({@link #addCollectionListener(NotifCollectionListener)}) to be informed when notifications
+ * events occur.
*/
@MainThread
@Singleton
@@ -230,6 +230,8 @@
entry = new NotificationEntry(sbn, ranking);
mNotificationSet.put(sbn.getKey(), entry);
+ dispatchOnEntryInit(entry);
+
if (rankingMap != null) {
applyRanking(rankingMap);
}
@@ -297,6 +299,7 @@
}
dispatchOnEntryRemoved(entry, reason, dismissedByUserStats != null /* removedByUser */);
+ dispatchOnEntryCleanUp(entry);
}
rebuildList();
@@ -378,6 +381,14 @@
return ranking;
}
+ private void dispatchOnEntryInit(NotificationEntry entry) {
+ mAmDispatchingToOtherCode = true;
+ for (NotifCollectionListener listener : mNotifCollectionListeners) {
+ listener.onEntryInit(entry);
+ }
+ mAmDispatchingToOtherCode = false;
+ }
+
private void dispatchOnEntryAdded(NotificationEntry entry) {
mAmDispatchingToOtherCode = true;
for (NotifCollectionListener listener : mNotifCollectionListeners) {
@@ -413,6 +424,14 @@
mAmDispatchingToOtherCode = false;
}
+ private void dispatchOnEntryCleanUp(NotificationEntry entry) {
+ mAmDispatchingToOtherCode = true;
+ for (NotifCollectionListener listener : mNotifCollectionListeners) {
+ listener.onEntryCleanUp(entry);
+ }
+ mAmDispatchingToOtherCode = false;
+ }
+
private final BatchableNotificationHandler mNotifHandler = new BatchableNotificationHandler() {
@Override
public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java
index 0377f57..9142388 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java
@@ -90,7 +90,8 @@
}
/**
- * Registers a listener to be informed when notifications are added, removed or updated.
+ * Registers a listener to be informed when there is a notification entry event such as an add,
+ * update, or remove.
*/
public void addCollectionListener(NotifCollectionListener listener) {
mNotifCollection.addCollectionListener(listener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java
index 6adcabd..ff6da12 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java
@@ -18,15 +18,25 @@
import android.service.notification.NotificationListenerService;
-import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
/**
- * Listener interface for {@link NotifCollection}.
+ * Listener interface for {@link NotificationEntry} events.
*/
public interface NotifCollectionListener {
/**
+ * Called whenever a new {@link NotificationEntry} is initialized. This should be used for
+ * initializing any decorated state tied to the notification.
+ *
+ * Do not reference other registered {@link NotifCollectionListener} implementations here as
+ * there is no guarantee of order and they may not have had a chance to initialize yet. Instead,
+ * use {@link #onEntryAdded} which is called after all initialization.
+ */
+ default void onEntryInit(NotificationEntry entry) {
+ }
+
+ /**
* Called whenever a notification with a new key is posted.
*/
default void onEntryAdded(NotificationEntry entry) {
@@ -51,6 +61,18 @@
}
/**
+ * Called whenever a {@link NotificationEntry} is considered deleted. This should be used for
+ * cleaning up any state tied to the notification.
+ *
+ * This is the deletion parallel of {@link #onEntryInit} and similarly means that you cannot
+ * expect other {@link NotifCollectionListener} implementations to still have valid state for
+ * the entry during this call. Instead, use {@link #onEntryRemoved} which will be called before
+ * deletion.
+ */
+ default void onEntryCleanUp(NotificationEntry entry) {
+ }
+
+ /**
* Called whenever the RankingMap is updated by system server. By the time this listener is
* called, the Rankings of all entries will have been updated.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
new file mode 100644
index 0000000..c7666e4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.dagger;
+
+import android.content.Context;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.init.NotificationsController;
+import com.android.systemui.statusbar.notification.init.NotificationsControllerImpl;
+import com.android.systemui.statusbar.notification.init.NotificationsControllerStub;
+
+import javax.inject.Singleton;
+
+import dagger.Lazy;
+import dagger.Module;
+import dagger.Provides;
+
+/** Module for classes related to the notifications data pipeline */
+@Module
+public class NotificationsModule {
+ /** Initializes the notification data pipeline (can be disabled via config). */
+ @Singleton
+ @Provides
+ static NotificationsController provideNotificationsController(
+ Context context,
+ Lazy<NotificationsControllerImpl> realController,
+ Lazy<NotificationsControllerStub> stubController) {
+ if (context.getResources().getBoolean(R.bool.config_renderNotifications)) {
+ return realController.get();
+ } else {
+ return stubController.get();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt
new file mode 100644
index 0000000..9da8b8a3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.init
+
+import android.service.notification.StatusBarNotification
+import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption
+import com.android.systemui.statusbar.NotificationPresenter
+import com.android.systemui.statusbar.notification.NotificationActivityStarter
+import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer
+import com.android.systemui.statusbar.phone.StatusBar
+import java.io.FileDescriptor
+import java.io.PrintWriter
+
+/**
+ * The master controller for all notifications-related work
+ *
+ * Split into two implementations: [NotificationsControllerImpl] (most cases) and
+ * [NotificationsControllerStub] (for builds that disable notification rendering).
+ */
+interface NotificationsController {
+ fun initialize(
+ statusBar: StatusBar,
+ presenter: NotificationPresenter,
+ listContainer: NotificationListContainer,
+ notificationActivityStarter: NotificationActivityStarter,
+ bindRowCallback: NotificationRowBinderImpl.BindRowCallback
+ )
+
+ fun requestNotificationUpdate(reason: String)
+ fun resetUserExpandedStates()
+ fun setNotificationSnoozed(sbn: StatusBarNotification, snoozeOption: SnoozeOption)
+ fun getActiveNotificationsCount(): Int
+ fun setNotificationSnoozed(sbn: StatusBarNotification, hoursToSnooze: Int)
+ fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>, dumpTruck: Boolean)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
new file mode 100644
index 0000000..61e3192
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.init
+
+import android.service.notification.StatusBarNotification
+import com.android.systemui.bubbles.BubbleController
+import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption
+import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.statusbar.NotificationListener
+import com.android.systemui.statusbar.NotificationPresenter
+import com.android.systemui.statusbar.notification.NotificationActivityStarter
+import com.android.systemui.statusbar.notification.NotificationClicker
+import com.android.systemui.statusbar.notification.NotificationEntryManager
+import com.android.systemui.statusbar.notification.NotificationListController
+import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl
+import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer
+import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper
+import com.android.systemui.statusbar.phone.NotificationGroupManager
+import com.android.systemui.statusbar.phone.StatusBar
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.statusbar.policy.HeadsUpManager
+import com.android.systemui.statusbar.policy.RemoteInputUriController
+import dagger.Lazy
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import java.util.Optional
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/**
+ * Master controller for all notifications-related work
+ *
+ * At the moment exposes a number of event-handler-esque methods; these are for historical reasons.
+ * Once we migrate away from the need for such things, this class becomes primarily a place to do
+ * any initialization work that notifications require.
+ */
+@Singleton
+class NotificationsControllerImpl @Inject constructor(
+ private val featureFlags: FeatureFlags,
+ private val notificationListener: NotificationListener,
+ private val entryManager: NotificationEntryManager,
+ private val newNotifPipeline: Lazy<NotifPipelineInitializer>,
+ private val deviceProvisionedController: DeviceProvisionedController,
+ private val notificationRowBinder: NotificationRowBinderImpl,
+ private val remoteInputUriController: RemoteInputUriController,
+ private val bubbleController: BubbleController,
+ private val groupManager: NotificationGroupManager,
+ private val groupAlertTransferHelper: NotificationGroupAlertTransferHelper,
+ private val headsUpManager: HeadsUpManager
+) : NotificationsController {
+
+ override fun initialize(
+ statusBar: StatusBar,
+ presenter: NotificationPresenter,
+ listContainer: NotificationListContainer,
+ notificationActivityStarter: NotificationActivityStarter,
+ bindRowCallback: NotificationRowBinderImpl.BindRowCallback
+ ) {
+ notificationListener.registerAsSystemService()
+
+ val listController =
+ NotificationListController(
+ entryManager,
+ listContainer,
+ deviceProvisionedController)
+ listController.bind()
+
+ notificationRowBinder.setNotificationClicker(
+ NotificationClicker(
+ Optional.of(statusBar),
+ bubbleController,
+ notificationActivityStarter))
+ notificationRowBinder.setUpWithPresenter(
+ presenter,
+ listContainer,
+ headsUpManager,
+ bindRowCallback)
+
+ if (featureFlags.isNewNotifPipelineEnabled) {
+ newNotifPipeline.get().initialize(notificationListener, notificationRowBinder)
+ }
+
+ if (featureFlags.isNewNotifPipelineRenderingEnabled) {
+ // TODO
+ } else {
+ notificationRowBinder.setInflationCallback(entryManager)
+
+ remoteInputUriController.attach(entryManager)
+ groupAlertTransferHelper.bind(entryManager, groupManager)
+ headsUpManager.addListener(groupManager)
+ headsUpManager.addListener(groupAlertTransferHelper)
+ groupManager.setHeadsUpManager(headsUpManager)
+ groupAlertTransferHelper.setHeadsUpManager(headsUpManager)
+
+ entryManager.attach(notificationListener)
+ }
+ }
+
+ override fun dump(
+ fd: FileDescriptor,
+ pw: PrintWriter,
+ args: Array<String>,
+ dumpTruck: Boolean
+ ) {
+ if (dumpTruck) {
+ entryManager.dump(pw, " ")
+ }
+ groupManager.dump(fd, pw, args)
+ }
+
+ // TODO: Convert all functions below this line into listeners instead of public methods
+
+ override fun requestNotificationUpdate(reason: String) {
+ entryManager.updateNotifications(reason)
+ }
+
+ override fun resetUserExpandedStates() {
+ for (entry in entryManager.visibleNotifications) {
+ entry.resetUserExpansion()
+ }
+ }
+
+ override fun setNotificationSnoozed(sbn: StatusBarNotification, snoozeOption: SnoozeOption) {
+ if (snoozeOption.snoozeCriterion != null) {
+ notificationListener.snoozeNotification(sbn.key, snoozeOption.snoozeCriterion.id)
+ } else {
+ notificationListener.snoozeNotification(
+ sbn.key,
+ snoozeOption.minutesToSnoozeFor * 60 * 1000.toLong())
+ }
+ }
+
+ override fun getActiveNotificationsCount(): Int {
+ return entryManager.activeNotificationsCount
+ }
+
+ override fun setNotificationSnoozed(sbn: StatusBarNotification, hoursToSnooze: Int) {
+ notificationListener.snoozeNotification(
+ sbn.key,
+ hoursToSnooze * 60 * 60 * 1000.toLong())
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt
new file mode 100644
index 0000000..ded855d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.init
+
+import android.service.notification.StatusBarNotification
+import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption
+import com.android.systemui.statusbar.NotificationListener
+import com.android.systemui.statusbar.NotificationPresenter
+import com.android.systemui.statusbar.notification.NotificationActivityStarter
+import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer
+import com.android.systemui.statusbar.phone.StatusBar
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import javax.inject.Inject
+
+/**
+ * Implementation of [NotificationsController] that's used when notifications rendering is disabled.
+ */
+class NotificationsControllerStub @Inject constructor(
+ private val notificationListener: NotificationListener
+) : NotificationsController {
+
+ override fun initialize(
+ statusBar: StatusBar,
+ presenter: NotificationPresenter,
+ listContainer: NotificationListContainer,
+ notificationActivityStarter: NotificationActivityStarter,
+ bindRowCallback: NotificationRowBinderImpl.BindRowCallback
+ ) {
+ // Always connect the listener even if notification-handling is disabled. Being a listener
+ // grants special permissions and it's not clear if other things will break if we lose those
+ notificationListener.registerAsSystemService()
+ }
+
+ override fun requestNotificationUpdate(reason: String) {
+ }
+
+ override fun resetUserExpandedStates() {
+ }
+
+ override fun setNotificationSnoozed(sbn: StatusBarNotification, snoozeOption: SnoozeOption) {
+ }
+
+ override fun setNotificationSnoozed(sbn: StatusBarNotification, hoursToSnooze: Int) {
+ }
+
+ override fun getActiveNotificationsCount(): Int {
+ return 0
+ }
+
+ override fun dump(
+ fd: FileDescriptor,
+ pw: PrintWriter,
+ args: Array<String>,
+ dumpTruck: Boolean
+ ) {
+ pw.println()
+ pw.println("Notification handling disabled")
+ pw.println()
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 1726b488..11f7079 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -168,12 +168,10 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.EmptyShadeView;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.GestureRecorder;
import com.android.systemui.statusbar.KeyboardShortcuts;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NavigationBarController;
-import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationPresenter;
@@ -191,15 +189,11 @@
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationAlertingManager;
-import com.android.systemui.statusbar.notification.NotificationClicker;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
-import com.android.systemui.statusbar.notification.NotificationListController;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
-import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer;
+import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -218,7 +212,6 @@
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
-import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.volume.VolumeComponent;
@@ -356,7 +349,6 @@
private final Object mQueueLock = new Object();
- private final FeatureFlags mFeatureFlags;
private final StatusBarIconController mIconController;
private final PulseExpansionHandler mPulseExpansionHandler;
private final NotificationWakeUpCoordinator mWakeUpCoordinator;
@@ -365,7 +357,6 @@
private final HeadsUpManagerPhone mHeadsUpManager;
private final DynamicPrivacyController mDynamicPrivacyController;
private final BypassHeadsUpNotifier mBypassHeadsUpNotifier;
- private final Lazy<NotifPipelineInitializer> mNewNotifPipeline;
private final FalsingManager mFalsingManager;
private final BroadcastDispatcher mBroadcastDispatcher;
private final ConfigurationController mConfigurationController;
@@ -374,7 +365,6 @@
private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
private final Provider<StatusBarComponent.Builder> mStatusBarComponentBuilder;
private final PluginManager mPluginManager;
- private final RemoteInputUriController mRemoteInputUriController;
private final Optional<Divider> mDividerOptional;
private final StatusBarNotificationActivityStarter.Builder
mStatusBarNotificationActivityStarterBuilder;
@@ -387,8 +377,8 @@
private final KeyguardDismissUtil mKeyguardDismissUtil;
private final ExtensionController mExtensionController;
private final UserInfoControllerImpl mUserInfoControllerImpl;
- private final NotificationRowBinderImpl mNotificationRowBinder;
private final DismissCallbackRegistry mDismissCallbackRegistry;
+ private NotificationsController mNotificationsController;
// expanded notifications
// the sliding/resizing panel within the notification window
@@ -412,8 +402,6 @@
private final NotificationGutsManager mGutsManager;
private final NotificationLogger mNotificationLogger;
- private final NotificationEntryManager mEntryManager;
- private NotificationListController mNotificationListController;
private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
private final NotificationViewHierarchyManager mViewHierarchyManager;
private final KeyguardViewMediator mKeyguardViewMediator;
@@ -589,7 +577,7 @@
@Override
public void onStrongAuthStateChanged(int userId) {
super.onStrongAuthStateChanged(userId);
- mEntryManager.updateNotifications("onStrongAuthStateChanged");
+ mNotificationsController.requestNotificationUpdate("onStrongAuthStateChanged");
}
};
private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
@@ -614,7 +602,7 @@
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
public StatusBar(
Context context,
- FeatureFlags featureFlags,
+ NotificationsController notificationsController,
LightBarController lightBarController,
AutoHideController autoHideController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -626,13 +614,11 @@
HeadsUpManagerPhone headsUpManagerPhone,
DynamicPrivacyController dynamicPrivacyController,
BypassHeadsUpNotifier bypassHeadsUpNotifier,
- Lazy<NotifPipelineInitializer> newNotifPipeline,
FalsingManager falsingManager,
BroadcastDispatcher broadcastDispatcher,
RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
- NotificationEntryManager notificationEntryManager,
NotificationInterruptionStateProvider notificationInterruptionStateProvider,
NotificationViewHierarchyManager notificationViewHierarchyManager,
KeyguardViewMediator keyguardViewMediator,
@@ -653,12 +639,10 @@
VibratorHelper vibratorHelper,
BubbleController bubbleController,
NotificationGroupManager groupManager,
- NotificationGroupAlertTransferHelper groupAlertTransferHelper,
VisualStabilityManager visualStabilityManager,
DeviceProvisionedController deviceProvisionedController,
NavigationBarController navigationBarController,
Lazy<AssistManager> assistManagerLazy,
- NotificationListener notificationListener,
ConfigurationController configurationController,
NotificationShadeWindowController notificationShadeWindowController,
LockscreenLockIconController lockscreenLockIconController,
@@ -676,7 +660,6 @@
Optional<Recents> recentsOptional,
Provider<StatusBarComponent.Builder> statusBarComponentBuilder,
PluginManager pluginManager,
- RemoteInputUriController remoteInputUriController,
Optional<Divider> dividerOptional,
LightsOutNotifController lightsOutNotifController,
StatusBarNotificationActivityStarter.Builder
@@ -692,10 +675,9 @@
KeyguardDismissUtil keyguardDismissUtil,
ExtensionController extensionController,
UserInfoControllerImpl userInfoControllerImpl,
- NotificationRowBinderImpl notificationRowBinder,
DismissCallbackRegistry dismissCallbackRegistry) {
super(context);
- mFeatureFlags = featureFlags;
+ mNotificationsController = notificationsController;
mLightBarController = lightBarController;
mAutoHideController = autoHideController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -707,13 +689,11 @@
mHeadsUpManager = headsUpManagerPhone;
mDynamicPrivacyController = dynamicPrivacyController;
mBypassHeadsUpNotifier = bypassHeadsUpNotifier;
- mNewNotifPipeline = newNotifPipeline;
mFalsingManager = falsingManager;
mBroadcastDispatcher = broadcastDispatcher;
mRemoteInputQuickSettingsDisabler = remoteInputQuickSettingsDisabler;
mGutsManager = notificationGutsManager;
mNotificationLogger = notificationLogger;
- mEntryManager = notificationEntryManager;
mNotificationInterruptionStateProvider = notificationInterruptionStateProvider;
mViewHierarchyManager = notificationViewHierarchyManager;
mKeyguardViewMediator = keyguardViewMediator;
@@ -734,12 +714,10 @@
mVibratorHelper = vibratorHelper;
mBubbleController = bubbleController;
mGroupManager = groupManager;
- mGroupAlertTransferHelper = groupAlertTransferHelper;
mVisualStabilityManager = visualStabilityManager;
mDeviceProvisionedController = deviceProvisionedController;
mNavigationBarController = navigationBarController;
mAssistManagerLazy = assistManagerLazy;
- mNotificationListener = notificationListener;
mConfigurationController = configurationController;
mNotificationShadeWindowController = notificationShadeWindowController;
mLockscreenLockIconController = lockscreenLockIconController;
@@ -757,7 +735,6 @@
mRecentsOptional = recentsOptional;
mStatusBarComponentBuilder = statusBarComponentBuilder;
mPluginManager = pluginManager;
- mRemoteInputUriController = remoteInputUriController;
mDividerOptional = dividerOptional;
mStatusBarNotificationActivityStarterBuilder = statusBarNotificationActivityStarterBuilder;
mShadeController = shadeController;
@@ -771,12 +748,11 @@
mKeyguardDismissUtil = keyguardDismissUtil;
mExtensionController = extensionController;
mUserInfoControllerImpl = userInfoControllerImpl;
- mNotificationRowBinder = notificationRowBinder;
mDismissCallbackRegistry = dismissCallbackRegistry;
mBubbleExpandListener =
(isExpanding, key) -> {
- mEntryManager.updateNotifications("onBubbleExpandChanged");
+ mNotificationsController.requestNotificationUpdate("onBubbleExpandChanged");
updateScrimController();
};
@@ -786,11 +762,10 @@
@Override
public void start() {
- mNotificationListener.registerAsSystemService();
mScreenLifecycle.addObserver(mScreenObserver);
mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
mUiModeManager = mContext.getSystemService(UiModeManager.class);
- mBypassHeadsUpNotifier.setUp(mEntryManager);
+ mBypassHeadsUpNotifier.setUp();
mBubbleController.setExpandListener(mBubbleExpandListener);
mActivityIntentHelper = new ActivityIntentHelper(mContext);
@@ -1060,12 +1035,8 @@
mConfigurationController.addCallback(mHeadsUpManager);
mHeadsUpManager.addListener(this);
mHeadsUpManager.addListener(mNotificationPanelViewController.getOnHeadsUpChangedListener());
- mHeadsUpManager.addListener(mGroupManager);
- mHeadsUpManager.addListener(mGroupAlertTransferHelper);
mHeadsUpManager.addListener(mVisualStabilityManager);
mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
- mGroupManager.setHeadsUpManager(mHeadsUpManager);
- mGroupAlertTransferHelper.setHeadsUpManager(mHeadsUpManager);
mNotificationLogger.setHeadsUpManager(mHeadsUpManager);
createNavigationBar(result);
@@ -1243,16 +1214,10 @@
mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanelViewController,
mHeadsUpManager, mNotificationShadeWindowView, mStackScroller, mDozeScrimController,
mScrimController, mActivityLaunchAnimator, mDynamicPrivacyController,
- mNotificationAlertingManager, mNotificationRowBinder, mKeyguardStateController,
+ mNotificationAlertingManager, mKeyguardStateController,
mKeyguardIndicationController,
this /* statusBar */, mShadeController, mCommandQueue, mInitController);
- mNotificationListController =
- new NotificationListController(
- mEntryManager,
- (NotificationListContainer) mStackScroller,
- mDeviceProvisionedController);
-
mNotificationShelf.setOnActivatedListener(mPresenter);
mRemoteInputManager.getController().addCallback(mNotificationShadeWindowController);
@@ -1266,22 +1231,12 @@
mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
- if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
- mNotificationRowBinder.setInflationCallback(mEntryManager);
- }
-
- mRemoteInputUriController.attach(mEntryManager);
-
- mNotificationRowBinder.setNotificationClicker(new NotificationClicker(
- Optional.of(this), mBubbleController, mNotificationActivityStarter));
-
- mGroupAlertTransferHelper.bind(mEntryManager, mGroupManager);
- mNotificationListController.bind();
-
- if (mFeatureFlags.isNewNotifPipelineEnabled()) {
- mNewNotifPipeline.get().initialize(mNotificationListener, mNotificationRowBinder);
- }
- mEntryManager.attach(mNotificationListener);
+ mNotificationsController.initialize(
+ this,
+ mPresenter,
+ (NotificationListContainer) mStackScroller,
+ mNotificationActivityStarter,
+ mPresenter);
}
/**
@@ -1518,7 +1473,7 @@
* @param reason why we're requesting a notification update
*/
public void requestNotificationUpdate(String reason) {
- mEntryManager.updateNotifications(reason);
+ mNotificationsController.requestNotificationUpdate(reason);
}
/**
@@ -1726,7 +1681,7 @@
@Override
public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
- mEntryManager.updateNotifications("onHeadsUpStateChanged");
+ mNotificationsController.requestNotificationUpdate("onHeadsUpStateChanged");
if (mStatusBarStateController.isDozing() && isHeadsUp) {
entry.setPulseSuppressed(false);
mDozeServiceHost.fireNotificationPulse(entry);
@@ -2485,11 +2440,9 @@
mStatusBarKeyguardViewManager.dump(pw);
}
- if (DUMPTRUCK) {
- synchronized (mEntryManager) {
- mEntryManager.dump(pw, " ");
- }
+ mNotificationsController.dump(fd, pw, args, DUMPTRUCK);
+ if (DUMPTRUCK) {
if (false) {
pw.println("see the logcat for a dump of the views we have created.");
// must happen on ui thread
@@ -2512,11 +2465,6 @@
} else {
pw.println(" mHeadsUpManager: null");
}
- if (mGroupManager != null) {
- mGroupManager.dump(fd, pw, args);
- } else {
- pw.println(" mGroupManager: null");
- }
if (mBubbleController != null) {
mBubbleController.dump(fd, pw, args);
@@ -2747,9 +2695,7 @@
};
public void resetUserExpandedStates() {
- for (NotificationEntry entry : mEntryManager.getVisibleNotifications()) {
- entry.resetUserExpansion();
- }
+ mNotificationsController.resetUserExpandedStates();
}
private void executeWhenUnlocked(OnDismissAction action, boolean requiresShadeOpen) {
@@ -2855,7 +2801,7 @@
try {
// consider the transition from peek to expanded to be a panel open,
// but not one that clears notification effects.
- int notificationLoad = mEntryManager.getActiveNotificationsCount();
+ int notificationLoad = mNotificationsController.getActiveNotificationsCount();
mBarService.onPanelRevealed(false, notificationLoad);
} catch (RemoteException ex) {
// Won't fail unless the world has ended.
@@ -2873,7 +2819,7 @@
!mPresenter.isPresenterFullyCollapsed() &&
(mState == StatusBarState.SHADE
|| mState == StatusBarState.SHADE_LOCKED);
- int notificationLoad = mEntryManager.getActiveNotificationsCount();
+ int notificationLoad = mNotificationsController.getActiveNotificationsCount();
if (pinnedHeadsUp && mPresenter.isPresenterFullyCollapsed()) {
notificationLoad = 1;
}
@@ -3514,7 +3460,7 @@
updateQsExpansionEnabled();
mKeyguardViewMediator.setDozing(mDozing);
- mEntryManager.updateNotifications("onDozingChanged");
+ mNotificationsController.requestNotificationUpdate("onDozingChanged");
updateDozingState();
mDozeServiceHost.updateDozing();
updateScrimController();
@@ -3965,7 +3911,6 @@
protected ViewGroup mStackScroller;
private final NotificationGroupManager mGroupManager;
- private final NotificationGroupAlertTransferHelper mGroupAlertTransferHelper;
// handling reordering
private final VisualStabilityManager mVisualStabilityManager;
@@ -4032,21 +3977,12 @@
}
};
- private final NotificationListener mNotificationListener;
-
public void setNotificationSnoozed(StatusBarNotification sbn, SnoozeOption snoozeOption) {
- if (snoozeOption.getSnoozeCriterion() != null) {
- mNotificationListener.snoozeNotification(sbn.getKey(),
- snoozeOption.getSnoozeCriterion().getId());
- } else {
- mNotificationListener.snoozeNotification(sbn.getKey(),
- snoozeOption.getMinutesToSnoozeFor() * 60 * 1000);
- }
+ mNotificationsController.setNotificationSnoozed(sbn, snoozeOption);
}
public void setNotificationSnoozed(StatusBarNotification sbn, int hoursToSnooze) {
- mNotificationListener.snoozeNotification(sbn.getKey(),
- hoursToSnooze * 60 * 60 * 1000);
+ mNotificationsController.setNotificationSnoozed(sbn, hoursToSnooze);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
index 7615bf8..15a0e08 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
@@ -46,9 +46,7 @@
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NavigationBarController;
-import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -61,12 +59,10 @@
import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationAlertingManager;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
-import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer;
+import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
@@ -77,7 +73,6 @@
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
-import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.volume.VolumeComponent;
@@ -105,7 +100,7 @@
@Singleton
static StatusBar provideStatusBar(
Context context,
- FeatureFlags featureFlags,
+ NotificationsController notificationsController,
LightBarController lightBarController,
AutoHideController autoHideController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -117,13 +112,11 @@
HeadsUpManagerPhone headsUpManagerPhone,
DynamicPrivacyController dynamicPrivacyController,
BypassHeadsUpNotifier bypassHeadsUpNotifier,
- Lazy<NotifPipelineInitializer> newNotifPipeline,
FalsingManager falsingManager,
BroadcastDispatcher broadcastDispatcher,
RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
- NotificationEntryManager notificationEntryManager,
NotificationInterruptionStateProvider notificationInterruptionStateProvider,
NotificationViewHierarchyManager notificationViewHierarchyManager,
KeyguardViewMediator keyguardViewMediator,
@@ -144,12 +137,10 @@
VibratorHelper vibratorHelper,
BubbleController bubbleController,
NotificationGroupManager groupManager,
- NotificationGroupAlertTransferHelper groupAlertTransferHelper,
VisualStabilityManager visualStabilityManager,
DeviceProvisionedController deviceProvisionedController,
NavigationBarController navigationBarController,
Lazy<AssistManager> assistManagerLazy,
- NotificationListener notificationListener,
ConfigurationController configurationController,
NotificationShadeWindowController notificationShadeWindowController,
LockscreenLockIconController lockscreenLockIconController,
@@ -167,7 +158,6 @@
Optional<Recents> recentsOptional,
Provider<StatusBarComponent.Builder> statusBarComponentBuilder,
PluginManager pluginManager,
- RemoteInputUriController remoteInputUriController,
Optional<Divider> dividerOptional,
LightsOutNotifController lightsOutNotifController,
StatusBarNotificationActivityStarter.Builder
@@ -183,11 +173,10 @@
KeyguardDismissUtil keyguardDismissUtil,
ExtensionController extensionController,
UserInfoControllerImpl userInfoControllerImpl,
- NotificationRowBinderImpl notificationRowBinder,
DismissCallbackRegistry dismissCallbackRegistry) {
return new StatusBar(
context,
- featureFlags,
+ notificationsController,
lightBarController,
autoHideController,
keyguardUpdateMonitor,
@@ -199,13 +188,11 @@
headsUpManagerPhone,
dynamicPrivacyController,
bypassHeadsUpNotifier,
- newNotifPipeline,
falsingManager,
broadcastDispatcher,
remoteInputQuickSettingsDisabler,
notificationGutsManager,
notificationLogger,
- notificationEntryManager,
notificationInterruptionStateProvider,
notificationViewHierarchyManager,
keyguardViewMediator,
@@ -226,12 +213,10 @@
vibratorHelper,
bubbleController,
groupManager,
- groupAlertTransferHelper,
visualStabilityManager,
deviceProvisionedController,
navigationBarController,
assistManagerLazy,
- notificationListener,
configurationController,
notificationShadeWindowController,
lockscreenLockIconController,
@@ -249,7 +234,6 @@
recentsOptional,
statusBarComponentBuilder,
pluginManager,
- remoteInputUriController,
dividerOptional,
lightsOutNotifController,
statusBarNotificationActivityStarterBuilder,
@@ -264,7 +248,6 @@
keyguardDismissUtil,
extensionController,
userInfoControllerImpl,
- notificationRowBinder,
dismissCallbackRegistry);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 1336b2de..2485513 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -141,7 +141,6 @@
ActivityLaunchAnimator activityLaunchAnimator,
DynamicPrivacyController dynamicPrivacyController,
NotificationAlertingManager notificationAlertingManager,
- NotificationRowBinderImpl notificationRowBinder,
KeyguardStateController keyguardStateController,
KeyguardIndicationController keyguardIndicationController,
StatusBar statusBar,
@@ -216,8 +215,6 @@
mEntryManager.addNotificationLifetimeExtender(mGutsManager);
mEntryManager.addNotificationLifetimeExtenders(
remoteInputManager.getLifetimeExtenders());
- notificationRowBinder.setUpWithPresenter(this, notifListContainer, mHeadsUpManager,
- this);
mNotificationInterruptionStateProvider.setUpWithPresenter(
this, mHeadsUpManager, this::canHeadsUp);
mLockscreenUserManager.setUpWithPresenter(this);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
index 7c8c7c8..c1ebae7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
@@ -19,6 +19,7 @@
import android.content.ComponentName
import android.content.Context
import android.os.Binder
+import android.os.UserHandle
import android.service.controls.Control
import android.service.controls.DeviceTypes
import android.testing.AndroidTestingRunner
@@ -38,6 +39,7 @@
import org.mockito.Mockito.`when`
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -55,6 +57,9 @@
@Mock
private lateinit var mockControlsController: ControlsController
+ private val user = UserHandle.of(mContext.userId)
+ private val otherUser = UserHandle.of(user.identifier + 1)
+
private val executor = FakeExecutor(FakeSystemClock())
private lateinit var controller: ControlsBindingController
private val providers = TestableControlsBindingControllerImpl.providers
@@ -75,6 +80,11 @@
}
@Test
+ fun testStartOnUser() {
+ assertEquals(user.identifier, controller.currentUserId)
+ }
+
+ @Test
fun testBindAndLoad() {
val callback: (List<Control>) -> Unit = {}
controller.bindAndLoad(TEST_COMPONENT_NAME_1, callback)
@@ -145,6 +155,41 @@
verify(it).unsubscribe()
}
}
+
+ @Test
+ fun testCurrentUserId() {
+ controller.changeUser(otherUser)
+ assertEquals(otherUser.identifier, controller.currentUserId)
+ }
+
+ @Test
+ fun testChangeUsers_providersHaveCorrectUser() {
+ controller.bindServices(listOf(TEST_COMPONENT_NAME_1))
+ controller.changeUser(otherUser)
+ controller.bindServices(listOf(TEST_COMPONENT_NAME_2))
+
+ val provider1 = providers.first { it.componentName == TEST_COMPONENT_NAME_1 }
+ assertEquals(user, provider1.user)
+ val provider2 = providers.first { it.componentName == TEST_COMPONENT_NAME_2 }
+ assertEquals(otherUser, provider2.user)
+ }
+
+ @Test
+ fun testChangeUsers_providersUnbound() {
+ controller.bindServices(listOf(TEST_COMPONENT_NAME_1))
+ controller.changeUser(otherUser)
+
+ val provider1 = providers.first { it.componentName == TEST_COMPONENT_NAME_1 }
+ verify(provider1).unbindService()
+
+ controller.bindServices(listOf(TEST_COMPONENT_NAME_2))
+ controller.changeUser(user)
+
+ reset(provider1)
+ val provider2 = providers.first { it.componentName == TEST_COMPONENT_NAME_2 }
+ verify(provider2).unbindService()
+ verify(provider1, never()).unbindService()
+ }
}
class TestableControlsBindingControllerImpl(
@@ -157,12 +202,16 @@
val providers = mutableSetOf<ControlsProviderLifecycleManager>()
}
+ // Replaces the real provider with a mock and puts the mock in a visible set.
+ // The mock has the same componentName and user as the real one would have
override fun createProviderManager(component: ComponentName):
ControlsProviderLifecycleManager {
+ val realProvider = super.createProviderManager(component)
val provider = mock(ControlsProviderLifecycleManager::class.java)
val token = Binder()
- `when`(provider.componentName).thenReturn(component)
+ `when`(provider.componentName).thenReturn(realProvider.componentName)
`when`(provider.token).thenReturn(token)
+ `when`(provider.user).thenReturn(realProvider.user)
providers.add(provider)
return provider
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
index be86a9c..897091f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
@@ -17,7 +17,12 @@
package com.android.systemui.controls.controller
import android.app.PendingIntent
+import android.content.BroadcastReceiver
import android.content.ComponentName
+import android.content.Context
+import android.content.ContextWrapper
+import android.content.Intent
+import android.os.UserHandle
import android.provider.Settings
import android.service.controls.Control
import android.service.controls.DeviceTypes
@@ -26,6 +31,8 @@
import androidx.test.filters.SmallTest
import com.android.systemui.DumpController
import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.controls.management.ControlsListingController
import com.android.systemui.controls.ControlStatus
import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.util.concurrency.FakeExecutor
@@ -37,10 +44,11 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers
-import org.mockito.ArgumentMatchers.eq
import org.mockito.Captor
import org.mockito.Mock
+import org.mockito.Mockito
import org.mockito.Mockito.`when`
+import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
@@ -61,18 +69,25 @@
private lateinit var pendingIntent: PendingIntent
@Mock
private lateinit var persistenceWrapper: ControlsFavoritePersistenceWrapper
+ @Mock
+ private lateinit var broadcastDispatcher: BroadcastDispatcher
+ @Mock
+ private lateinit var listingController: ControlsListingController
@Captor
private lateinit var controlInfoListCaptor: ArgumentCaptor<List<ControlInfo>>
@Captor
private lateinit var controlLoadCallbackCaptor: ArgumentCaptor<(List<Control>) -> Unit>
+ @Captor
+ private lateinit var broadcastReceiverCaptor: ArgumentCaptor<BroadcastReceiver>
private lateinit var delayableExecutor: FakeExecutor
private lateinit var controller: ControlsController
companion object {
fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
- fun <T : Any> safeEq(value: T): T = eq(value) ?: value
+ fun <T> eq(value: T): T = Mockito.eq(value) ?: value
+ fun <T> any(): T = Mockito.any<T>()
private val TEST_COMPONENT = ComponentName("test.pkg", "test.class")
private const val TEST_CONTROL_ID = "control1"
@@ -89,24 +104,39 @@
TEST_COMPONENT_2, TEST_CONTROL_ID_2, TEST_CONTROL_TITLE_2, TEST_DEVICE_TYPE_2)
}
+ private val user = mContext.userId
+ private val otherUser = user + 1
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
Settings.Secure.putInt(mContext.contentResolver,
ControlsControllerImpl.CONTROLS_AVAILABLE, 1)
+ Settings.Secure.putIntForUser(mContext.contentResolver,
+ ControlsControllerImpl.CONTROLS_AVAILABLE, 1, otherUser)
delayableExecutor = FakeExecutor(FakeSystemClock())
+ val wrapper = object : ContextWrapper(mContext) {
+ override fun createContextAsUser(user: UserHandle, flags: Int): Context {
+ return baseContext
+ }
+ }
+
controller = ControlsControllerImpl(
- mContext,
+ wrapper,
delayableExecutor,
uiController,
bindingController,
+ listingController,
+ broadcastDispatcher,
Optional.of(persistenceWrapper),
dumpController
)
assertTrue(controller.available)
+ verify(broadcastDispatcher).registerReceiver(
+ capture(broadcastReceiverCaptor), any(), any(), eq(UserHandle.ALL))
}
private fun builderFromInfo(controlInfo: ControlInfo): Control.StatelessBuilder {
@@ -115,6 +145,11 @@
}
@Test
+ fun testStartOnUser() {
+ assertEquals(user, controller.currentUserId)
+ }
+
+ @Test
fun testStartWithoutFavorites() {
assertTrue(controller.getFavoriteControls().isEmpty())
}
@@ -127,6 +162,8 @@
delayableExecutor,
uiController,
bindingController,
+ listingController,
+ broadcastDispatcher,
Optional.of(persistenceWrapper),
dumpController
)
@@ -190,7 +227,7 @@
controller.loadForComponent(TEST_COMPONENT) {}
reset(persistenceWrapper)
- verify(bindingController).bindAndLoad(safeEq(TEST_COMPONENT),
+ verify(bindingController).bindAndLoad(eq(TEST_COMPONENT),
capture(controlLoadCallbackCaptor))
controlLoadCallbackCaptor.value.invoke(listOf(control))
@@ -262,7 +299,7 @@
assertEquals(ControlStatus(control, false), controlStatus)
}
- verify(bindingController).bindAndLoad(safeEq(TEST_COMPONENT),
+ verify(bindingController).bindAndLoad(eq(TEST_COMPONENT),
capture(controlLoadCallbackCaptor))
controlLoadCallbackCaptor.value.invoke(listOf(control))
@@ -287,7 +324,7 @@
assertEquals(ControlStatus(control2, false), controlStatus2)
}
- verify(bindingController).bindAndLoad(safeEq(TEST_COMPONENT),
+ verify(bindingController).bindAndLoad(eq(TEST_COMPONENT),
capture(controlLoadCallbackCaptor))
controlLoadCallbackCaptor.value.invoke(listOf(control, control2))
@@ -309,7 +346,7 @@
assertTrue(controlStatus.removed)
}
- verify(bindingController).bindAndLoad(safeEq(TEST_COMPONENT),
+ verify(bindingController).bindAndLoad(eq(TEST_COMPONENT),
capture(controlLoadCallbackCaptor))
controlLoadCallbackCaptor.value.invoke(emptyList())
@@ -325,7 +362,7 @@
controller.loadForComponent(TEST_COMPONENT) {}
- verify(bindingController).bindAndLoad(safeEq(TEST_COMPONENT),
+ verify(bindingController).bindAndLoad(eq(TEST_COMPONENT),
capture(controlLoadCallbackCaptor))
controlLoadCallbackCaptor.value.invoke(listOf(control))
@@ -358,4 +395,26 @@
controller.clearFavorites()
assertTrue(controller.getFavoriteControls().isEmpty())
}
-}
+
+ @Test
+ fun testSwitchUsers() {
+ controller.changeFavoriteStatus(TEST_CONTROL_INFO, true)
+
+ reset(persistenceWrapper)
+ val intent = Intent(Intent.ACTION_USER_SWITCHED).apply {
+ putExtra(Intent.EXTRA_USER_HANDLE, otherUser)
+ }
+ val pendingResult = mock(BroadcastReceiver.PendingResult::class.java)
+ `when`(pendingResult.sendingUserId).thenReturn(otherUser)
+ broadcastReceiverCaptor.value.pendingResult = pendingResult
+
+ broadcastReceiverCaptor.value.onReceive(mContext, intent)
+
+ verify(persistenceWrapper).changeFile(any())
+ verify(persistenceWrapper).readFavorites()
+ verify(bindingController).changeUser(UserHandle.of(otherUser))
+ verify(listingController).changeUser(UserHandle.of(otherUser))
+ assertTrue(controller.getFavoriteControls().isEmpty())
+ assertEquals(otherUser, controller.currentUserId)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
index 4fc1cca..3f1435b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.controls.controller
import android.content.ComponentName
+import android.os.UserHandle
import android.service.controls.Control
import android.service.controls.IControlsActionCallback
import android.service.controls.IControlsLoadCallback
@@ -86,6 +87,7 @@
loadCallback,
actionCallback,
subscriber,
+ UserHandle.of(0),
componentName
)
}
@@ -148,4 +150,4 @@
eq(actionCallback))
assertEquals(action, wrapperCaptor.getValue().getWrappedAction())
}
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
index f09aab9..85e937e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
@@ -17,12 +17,15 @@
package com.android.systemui.controls.management
import android.content.ComponentName
+import android.content.Context
+import android.content.ContextWrapper
import android.content.pm.ServiceInfo
+import android.os.UserHandle
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.settingslib.applications.ServiceListing
-import com.android.settingslib.widget.CandidateInfo
import com.android.systemui.SysuiTestCase
+import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
import org.junit.After
@@ -69,13 +72,22 @@
private var serviceListingCallbackCaptor =
ArgumentCaptor.forClass(ServiceListing.Callback::class.java)
+ private val user = mContext.userId
+ private val otherUser = user + 1
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
`when`(serviceInfo.componentName).thenReturn(componentName)
- controller = ControlsListingControllerImpl(mContext, executor, mockSL)
+ val wrapper = object : ContextWrapper(mContext) {
+ override fun createContextAsUser(user: UserHandle, flags: Int): Context {
+ return baseContext
+ }
+ }
+
+ controller = ControlsListingControllerImpl(wrapper, executor, { mockSL })
verify(mockSL).addCallback(capture(serviceListingCallbackCaptor))
}
@@ -86,6 +98,11 @@
}
@Test
+ fun testStartsOnUser() {
+ assertEquals(user, controller.currentUserId)
+ }
+
+ @Test
fun testNoServices_notListening() {
assertTrue(controller.getCurrentServices().isEmpty())
}
@@ -167,8 +184,9 @@
controller.addCallback(mockCallbackOther)
@Suppress("unchecked_cast")
- val captor: ArgumentCaptor<List<CandidateInfo>> =
- ArgumentCaptor.forClass(List::class.java) as ArgumentCaptor<List<CandidateInfo>>
+ val captor: ArgumentCaptor<List<ControlsServiceInfo>> =
+ ArgumentCaptor.forClass(List::class.java)
+ as ArgumentCaptor<List<ControlsServiceInfo>>
executor.runAllReady()
reset(mockCallback)
@@ -185,4 +203,11 @@
assertEquals(1, captor.value.size)
assertEquals(componentName.flattenToString(), captor.value[0].key)
}
+
+ @Test
+ fun testChangeUser() {
+ controller.changeUser(UserHandle.of(otherUser))
+ executor.runAllReady()
+ assertEquals(otherUser, controller.currentUserId)
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
index 752e145..00d333fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
@@ -53,6 +53,7 @@
when(config.pickupGestureEnabled(anyInt())).thenReturn(false);
when(config.pulseOnNotificationEnabled(anyInt())).thenReturn(true);
when(config.alwaysOnEnabled(anyInt())).thenReturn(false);
+ when(config.dozeSuppressed(anyInt())).thenReturn(false);
when(config.enabled(anyInt())).thenReturn(true);
when(config.getWakeLockScreenDebounce()).thenReturn(0L);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
index f4cf314..63cbca9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -139,6 +139,65 @@
}
@Test
+ public void testInitialize_dozeSuppressed_alwaysOnDisabled_goesToDoze() {
+ when(mConfigMock.dozeSuppressed(anyInt())).thenReturn(true);
+ when(mConfigMock.alwaysOnEnabled(anyInt())).thenReturn(false);
+
+ mMachine.requestState(INITIALIZED);
+
+ verify(mPartMock).transitionTo(INITIALIZED, DOZE);
+ assertEquals(DOZE, mMachine.getState());
+ }
+
+ @Test
+ public void testInitialize_dozeSuppressed_alwaysOnEnabled_goesToDoze() {
+ when(mConfigMock.dozeSuppressed(anyInt())).thenReturn(true);
+ when(mConfigMock.alwaysOnEnabled(anyInt())).thenReturn(true);
+
+ mMachine.requestState(INITIALIZED);
+
+ verify(mPartMock).transitionTo(INITIALIZED, DOZE);
+ assertEquals(DOZE, mMachine.getState());
+ }
+
+ @Test
+ public void testInitialize_dozeSuppressed_afterDocked_goesToDoze() {
+ when(mConfigMock.dozeSuppressed(anyInt())).thenReturn(true);
+ when(mDockManager.isDocked()).thenReturn(true);
+
+ mMachine.requestState(INITIALIZED);
+
+ verify(mPartMock).transitionTo(INITIALIZED, DOZE);
+ assertEquals(DOZE, mMachine.getState());
+ }
+
+ @Test
+ public void testInitialize_dozeSuppressed_alwaysOnDisabled_afterDockPaused_goesToDoze() {
+ when(mConfigMock.dozeSuppressed(anyInt())).thenReturn(true);
+ when(mConfigMock.alwaysOnEnabled(anyInt())).thenReturn(false);
+ when(mDockManager.isDocked()).thenReturn(true);
+ when(mDockManager.isHidden()).thenReturn(true);
+
+ mMachine.requestState(INITIALIZED);
+
+ verify(mPartMock).transitionTo(INITIALIZED, DOZE);
+ assertEquals(DOZE, mMachine.getState());
+ }
+
+ @Test
+ public void testInitialize_dozeSuppressed_alwaysOnEnabled_afterDockPaused_goesToDoze() {
+ when(mConfigMock.dozeSuppressed(anyInt())).thenReturn(true);
+ when(mConfigMock.alwaysOnEnabled(anyInt())).thenReturn(true);
+ when(mDockManager.isDocked()).thenReturn(true);
+ when(mDockManager.isHidden()).thenReturn(true);
+
+ mMachine.requestState(INITIALIZED);
+
+ verify(mPartMock).transitionTo(INITIALIZED, DOZE);
+ assertEquals(DOZE, mMachine.getState());
+ }
+
+ @Test
public void testPulseDone_goesToDoze() {
when(mConfigMock.alwaysOnEnabled(anyInt())).thenReturn(false);
mMachine.requestState(INITIALIZED);
@@ -165,6 +224,20 @@
}
@Test
+ public void testPulseDone_dozeSuppressed_goesToSuppressed() {
+ when(mConfigMock.dozeSuppressed(anyInt())).thenReturn(true);
+ when(mConfigMock.alwaysOnEnabled(anyInt())).thenReturn(true);
+ mMachine.requestState(INITIALIZED);
+ mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
+ mMachine.requestState(DOZE_PULSING);
+
+ mMachine.requestState(DOZE_PULSE_DONE);
+
+ verify(mPartMock).transitionTo(DOZE_PULSE_DONE, DOZE);
+ assertEquals(DOZE, mMachine.getState());
+ }
+
+ @Test
public void testPulseDone_afterDocked_goesToDockedAoD() {
when(mDockManager.isDocked()).thenReturn(true);
mMachine.requestState(INITIALIZED);
@@ -178,6 +251,20 @@
}
@Test
+ public void testPulseDone_dozeSuppressed_afterDocked_goesToDoze() {
+ when(mConfigMock.dozeSuppressed(anyInt())).thenReturn(true);
+ when(mDockManager.isDocked()).thenReturn(true);
+ mMachine.requestState(INITIALIZED);
+ mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
+ mMachine.requestState(DOZE_PULSING);
+
+ mMachine.requestState(DOZE_PULSE_DONE);
+
+ verify(mPartMock).transitionTo(DOZE_PULSE_DONE, DOZE);
+ assertEquals(DOZE, mMachine.getState());
+ }
+
+ @Test
public void testPulseDone_afterDockPaused_goesToDoze() {
when(mConfigMock.alwaysOnEnabled(anyInt())).thenReturn(true);
when(mDockManager.isDocked()).thenReturn(true);
@@ -193,6 +280,22 @@
}
@Test
+ public void testPulseDone_dozeSuppressed_afterDockPaused_goesToDoze() {
+ when(mConfigMock.dozeSuppressed(anyInt())).thenReturn(true);
+ when(mConfigMock.alwaysOnEnabled(anyInt())).thenReturn(true);
+ when(mDockManager.isDocked()).thenReturn(true);
+ when(mDockManager.isHidden()).thenReturn(true);
+ mMachine.requestState(INITIALIZED);
+ mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
+ mMachine.requestState(DOZE_PULSING);
+
+ mMachine.requestState(DOZE_PULSE_DONE);
+
+ verify(mPartMock).transitionTo(DOZE_PULSE_DONE, DOZE);
+ assertEquals(DOZE, mMachine.getState());
+ }
+
+ @Test
public void testFinished_staysFinished() {
mMachine.requestState(INITIALIZED);
mMachine.requestState(FINISH);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressedHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressedHandlerTest.java
new file mode 100644
index 0000000..5bdca76
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressedHandlerTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.doze;
+
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.hardware.display.AmbientDisplayConfiguration;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.doze.DozeSuppressedHandler.DozeSuppressedSettingObserver;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class DozeSuppressedHandlerTest extends SysuiTestCase {
+ @Mock private DozeMachine mMachine;
+ @Mock private DozeSuppressedSettingObserver mObserver;
+ private AmbientDisplayConfiguration mConfig;
+ private DozeSuppressedHandler mSuppressedHandler;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mConfig = DozeConfigurationUtil.createMockConfig();
+ mSuppressedHandler = new DozeSuppressedHandler(mContext, mConfig, mMachine, mObserver);
+ }
+
+ @Test
+ public void transitionTo_initialized_registersObserver() throws Exception {
+ mSuppressedHandler.transitionTo(DozeMachine.State.UNINITIALIZED,
+ DozeMachine.State.INITIALIZED);
+
+ verify(mObserver).register();
+ }
+
+ @Test
+ public void transitionTo_finish_unregistersObserver() throws Exception {
+ mSuppressedHandler.transitionTo(DozeMachine.State.INITIALIZED,
+ DozeMachine.State.FINISH);
+
+ verify(mObserver).unregister();
+ }
+
+ @Test
+ public void transitionTo_doze_doesNothing() throws Exception {
+ mSuppressedHandler.transitionTo(DozeMachine.State.INITIALIZED,
+ DozeMachine.State.DOZE);
+
+ verify(mObserver, never()).register();
+ verify(mObserver, never()).unregister();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
index 47933ba..e58a3a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
@@ -16,8 +16,11 @@
import static junit.framework.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -447,6 +450,21 @@
view.findViewById(R.id.vpn_subtitle).getVisibility());
}
+ @Test
+ public void testNoClickWhenGone() {
+ QSTileHost mockHost = mock(QSTileHost.class);
+ mFooter.setHostEnvironment(mockHost);
+ mFooter.refreshState();
+
+ TestableLooper.get(this).processAllMessages();
+
+ assertFalse(mFooter.hasFooter());
+ mFooter.onClick(mFooter.getView());
+
+ // Proxy for dialog being created
+ verify(mockHost, never()).collapsePanels();
+ }
+
private CharSequence addLink(CharSequence description) {
final SpannableStringBuilder message = new SpannableStringBuilder();
message.append(description);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index 9a7e97b..b652980 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -139,9 +139,10 @@
.setRank(4747));
// THEN the listener is notified
- verify(mCollectionListener).onEntryAdded(mEntryCaptor.capture());
-
+ verify(mCollectionListener).onEntryInit(mEntryCaptor.capture());
NotificationEntry entry = mEntryCaptor.getValue();
+
+ verify(mCollectionListener).onEntryAdded(entry);
assertEquals(notif1.key, entry.getKey());
assertEquals(notif1.sbn, entry.getSbn());
assertEquals(notif1.ranking, entry.getRanking());
@@ -236,6 +237,7 @@
// THEN the listener is notified
verify(mCollectionListener).onEntryRemoved(entry, REASON_APP_CANCEL, false);
+ verify(mCollectionListener).onEntryCleanUp(entry);
assertEquals(notif.sbn, entry.getSbn());
assertEquals(notif.ranking, entry.getRanking());
}
@@ -606,6 +608,10 @@
private final Map<String, NotificationEntry> mLastSeenEntries = new ArrayMap<>();
@Override
+ public void onEntryInit(NotificationEntry entry) {
+ }
+
+ @Override
public void onEntryAdded(NotificationEntry entry) {
mLastSeenEntries.put(entry.getKey(), entry);
}
@@ -618,6 +624,10 @@
public void onEntryRemoved(NotificationEntry entry, int reason, boolean removedByUser) {
}
+ @Override
+ public void onEntryCleanUp(NotificationEntry entry) {
+ }
+
public NotificationEntry getEntry(String key) {
if (!mLastSeenEntries.containsKey(key)) {
throw new RuntimeException("Key not found: " + key);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index dd896be..b9d2d22 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -54,7 +54,6 @@
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -113,10 +112,9 @@
notificationShadeWindowView, mock(NotificationListContainerViewGroup.class),
mock(DozeScrimController.class), mock(ScrimController.class),
mock(ActivityLaunchAnimator.class), mock(DynamicPrivacyController.class),
- mock(NotificationAlertingManager.class),
- mock(NotificationRowBinderImpl.class), mock(KeyguardStateController.class),
- mock(KeyguardIndicationController.class),
- mStatusBar, mock(ShadeControllerImpl.class), mCommandQueue, mInitController);
+ mock(NotificationAlertingManager.class), mock(KeyguardStateController.class),
+ mock(KeyguardIndicationController.class), mStatusBar,
+ mock(ShadeControllerImpl.class), mCommandQueue, mInitController);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index e90e398..db17a6e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -95,7 +95,6 @@
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NavigationBarController;
import com.android.systemui.statusbar.NotificationListener;
@@ -120,8 +119,7 @@
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
-import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer;
+import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
@@ -133,10 +131,8 @@
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
-import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
-import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.volume.VolumeComponent;
@@ -149,7 +145,6 @@
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
-import java.util.ArrayList;
import java.util.Optional;
import javax.inject.Provider;
@@ -166,7 +161,7 @@
private PowerManager mPowerManager;
private TestableNotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
- @Mock private FeatureFlags mFeatureFlags;
+ @Mock private NotificationsController mNotificationsController;
@Mock private LightBarController mLightBarController;
@Mock private StatusBarIconController mStatusBarIconController;
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -180,7 +175,6 @@
@Mock private IDreamManager mDreamManager;
@Mock private ScrimController mScrimController;
@Mock private DozeScrimController mDozeScrimController;
- @Mock private ArrayList<NotificationEntry> mNotificationList;
@Mock private Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
@Mock private BiometricUnlockController mBiometricUnlockController;
@Mock private NotificationInterruptionStateProvider.HeadsUpSuppressor mHeadsUpSuppressor;
@@ -190,7 +184,6 @@
@Mock private NotificationLockscreenUserManager mLockscreenUserManager;
@Mock private NotificationRemoteInputManager mRemoteInputManager;
@Mock private RemoteInputController mRemoteInputController;
- @Mock private RemoteInputUriController mRemoteInputUriController;
@Mock private StatusBarStateControllerImpl mStatusBarStateController;
@Mock private BatteryController mBatteryController;
@Mock private DeviceProvisionedController mDeviceProvisionedController;
@@ -214,8 +207,6 @@
@Mock private NotificationWakeUpCoordinator mNotificationWakeUpCoordinator;
@Mock private KeyguardBypassController mKeyguardBypassController;
@Mock private DynamicPrivacyController mDynamicPrivacyController;
- @Mock private NotifPipelineInitializer mNewNotifPipeline;
- @Mock private ZenModeController mZenModeController;
@Mock private AutoHideController mAutoHideController;
@Mock private NotificationViewHierarchyManager mNotificationViewHierarchyManager;
@Mock private UserSwitcherController mUserSwitcherController;
@@ -223,7 +214,6 @@
@Mock private VibratorHelper mVibratorHelper;
@Mock private BubbleController mBubbleController;
@Mock private NotificationGroupManager mGroupManager;
- @Mock private NotificationGroupAlertTransferHelper mGroupAlertTransferHelper;
@Mock private NotificationShadeWindowController mNotificationShadeWindowController;
@Mock private NotificationIconAreaController mNotificationIconAreaController;
@Mock private NotificationShadeWindowViewController mNotificationShadeWindowViewController;
@@ -247,7 +237,6 @@
@Mock private ViewMediatorCallback mViewMediatorCallback;
@Mock private DismissCallbackRegistry mDismissCallbackRegistry;
@Mock private ScreenPinningRequest mScreenPinningRequest;
- @Mock private NotificationEntryManager mEntryManager;
@Mock private LockscreenLockIconController mLockscreenLockIconController;
@Mock private StatusBarNotificationActivityStarter.Builder
mStatusBarNotificationActivityStarterBuilder;
@@ -256,7 +245,6 @@
@Mock private KeyguardDismissUtil mKeyguardDismissUtil;
@Mock private ExtensionController mExtensionController;
@Mock private UserInfoControllerImpl mUserInfoControllerImpl;
- @Mock private NotificationRowBinderImpl mNotificationRowBinder;
private ShadeController mShadeController;
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
private InitController mInitController = new InitController();
@@ -280,7 +268,7 @@
mMetricsLogger = new FakeMetricsLogger();
NotificationLogger notificationLogger = new NotificationLogger(mNotificationListener,
- mUiBgExecutor, mEntryManager, mStatusBarStateController,
+ mUiBgExecutor, mock(NotificationEntryManager.class), mStatusBarStateController,
mExpansionStateLogger);
notificationLogger.setVisibilityReporter(mock(Runnable.class));
@@ -334,7 +322,7 @@
mStatusBar = new StatusBar(
mContext,
- mFeatureFlags,
+ mNotificationsController,
mLightBarController,
mAutoHideController,
mKeyguardUpdateMonitor,
@@ -346,7 +334,6 @@
mHeadsUpManager,
mDynamicPrivacyController,
mBypassHeadsUpNotifier,
- () -> mNewNotifPipeline,
new FalsingManagerFake(),
mBroadcastDispatcher,
new RemoteInputQuickSettingsDisabler(
@@ -356,7 +343,6 @@
),
mNotificationGutsManager,
notificationLogger,
- mEntryManager,
mNotificationInterruptionStateProvider,
mNotificationViewHierarchyManager,
mKeyguardViewMediator,
@@ -377,12 +363,10 @@
mVibratorHelper,
mBubbleController,
mGroupManager,
- mGroupAlertTransferHelper,
mVisualStabilityManager,
mDeviceProvisionedController,
mNavigationBarController,
() -> mAssistManager,
- mNotificationListener,
configurationController,
mNotificationShadeWindowController,
mLockscreenLockIconController,
@@ -399,7 +383,6 @@
Optional.of(mRecents),
mStatusBarComponentBuilderProvider,
mPluginManager,
- mRemoteInputUriController,
Optional.of(mDivider),
mLightsOutNotifController,
mStatusBarNotificationActivityStarterBuilder,
@@ -414,7 +397,6 @@
mKeyguardDismissUtil,
mExtensionController,
mUserInfoControllerImpl,
- mNotificationRowBinder,
mDismissCallbackRegistry);
when(mNotificationShadeWindowView.findViewById(R.id.lock_icon_container)).thenReturn(
@@ -675,7 +657,7 @@
public void testPanelOpenForHeadsUp() {
when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
- when(mEntryManager.getActiveNotificationsCount()).thenReturn(5);
+ when(mNotificationsController.getActiveNotificationsCount()).thenReturn(5);
when(mNotificationPresenter.isPresenterFullyCollapsed()).thenReturn(true);
mStatusBar.setBarStateForTest(StatusBarState.SHADE);
@@ -693,7 +675,7 @@
@Test
public void testPanelOpenAndClear() {
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
- when(mEntryManager.getActiveNotificationsCount()).thenReturn(5);
+ when(mNotificationsController.getActiveNotificationsCount()).thenReturn(5);
when(mNotificationPresenter.isPresenterFullyCollapsed()).thenReturn(false);
mStatusBar.setBarStateForTest(StatusBarState.SHADE);
@@ -712,7 +694,7 @@
@Test
public void testPanelOpenAndNoClear() {
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
- when(mEntryManager.getActiveNotificationsCount()).thenReturn(5);
+ when(mNotificationsController.getActiveNotificationsCount()).thenReturn(5);
when(mNotificationPresenter.isPresenterFullyCollapsed()).thenReturn(false);
mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
index 7dc5c5f..020b32a 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
@@ -77,8 +77,8 @@
mLog.mark("onCreate");
mDeps = getTetheringDependencies();
mContext = mDeps.getContext();
- mTethering = makeTethering(mDeps);
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ mTethering = makeTethering(mDeps);
}
/**
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
index 1fe162c..5d170d3 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
@@ -18,9 +18,17 @@
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_DOWN;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_LEFT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_RIGHT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_UP;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_DOWN;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_LEFT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_RIGHT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_UP;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_TRIPLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_DOUBLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_DOUBLE_TAP_AND_HOLD;
@@ -110,6 +118,7 @@
mGestures.add(new Swipe(context, UP, DOWN, GESTURE_SWIPE_UP_AND_DOWN, this));
mGestures.add(new Swipe(context, UP, LEFT, GESTURE_SWIPE_UP_AND_LEFT, this));
mGestures.add(new Swipe(context, UP, RIGHT, GESTURE_SWIPE_UP_AND_RIGHT, this));
+ // Set up multi-finger gestures to be enabled later.
// Two-finger taps.
mMultiFingerGestures.add(
new MultiFingerMultiTap(mContext, 2, 1, GESTURE_2_FINGER_SINGLE_TAP, this));
@@ -124,6 +133,24 @@
new MultiFingerMultiTap(mContext, 3, 2, GESTURE_3_FINGER_DOUBLE_TAP, this));
mMultiFingerGestures.add(
new MultiFingerMultiTap(mContext, 3, 3, GESTURE_3_FINGER_TRIPLE_TAP, this));
+ // Two-finger swipes.
+ mMultiFingerGestures.add(
+ new MultiFingerSwipe(context, 2, DOWN, GESTURE_2_FINGER_SWIPE_DOWN, this));
+ mMultiFingerGestures.add(
+ new MultiFingerSwipe(context, 2, LEFT, GESTURE_2_FINGER_SWIPE_LEFT, this));
+ mMultiFingerGestures.add(
+ new MultiFingerSwipe(context, 2, RIGHT, GESTURE_2_FINGER_SWIPE_RIGHT, this));
+ mMultiFingerGestures.add(
+ new MultiFingerSwipe(context, 2, UP, GESTURE_2_FINGER_SWIPE_UP, this));
+ // Three-finger swipes.
+ mMultiFingerGestures.add(
+ new MultiFingerSwipe(context, 3, DOWN, GESTURE_3_FINGER_SWIPE_DOWN, this));
+ mMultiFingerGestures.add(
+ new MultiFingerSwipe(context, 3, LEFT, GESTURE_3_FINGER_SWIPE_LEFT, this));
+ mMultiFingerGestures.add(
+ new MultiFingerSwipe(context, 3, RIGHT, GESTURE_3_FINGER_SWIPE_RIGHT, this));
+ mMultiFingerGestures.add(
+ new MultiFingerSwipe(context, 3, UP, GESTURE_3_FINGER_SWIPE_UP, this));
}
/**
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/GestureUtils.java b/services/accessibility/java/com/android/server/accessibility/gestures/GestureUtils.java
index 0f5dd08..ac67480 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureUtils.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureUtils.java
@@ -8,6 +8,9 @@
*/
public final class GestureUtils {
+ public static int MM_PER_CM = 10;
+ public static float CM_PER_INCH = 2.54f;
+
private GestureUtils() {
/* cannot be instantiated */
}
@@ -85,4 +88,12 @@
return true;
}
+
+ /**
+ * Gets the index of the pointer that went up or down from a motion event.
+ */
+ public static int getActionIndex(MotionEvent event) {
+ return (event.getAction()
+ & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java
new file mode 100644
index 0000000..8249239
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java
@@ -0,0 +1,496 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility.gestures;
+
+import static android.view.MotionEvent.INVALID_POINTER_ID;
+
+import static com.android.server.accessibility.gestures.GestureUtils.MM_PER_CM;
+import static com.android.server.accessibility.gestures.GestureUtils.getActionIndex;
+import static com.android.server.accessibility.gestures.Swipe.CANCEL_ON_PAUSE_THRESHOLD_NOT_STARTED_MS;
+import static com.android.server.accessibility.gestures.Swipe.CANCEL_ON_PAUSE_THRESHOLD_STARTED_MS;
+import static com.android.server.accessibility.gestures.Swipe.GESTURE_CONFIRM_CM;
+import static com.android.server.accessibility.gestures.TouchExplorer.DEBUG;
+
+import android.content.Context;
+import android.graphics.PointF;
+import android.os.Handler;
+import android.util.DisplayMetrics;
+import android.util.Slog;
+import android.util.TypedValue;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * This class is responsible for matching one-finger swipe gestures. Each instance matches one swipe
+ * gesture. A swipe is specified as a series of one or more directions e.g. left, left and up, etc.
+ * At this time swipes with more than two directions are not supported.
+ */
+class MultiFingerSwipe extends GestureMatcher {
+
+ // Direction constants.
+ public static final int LEFT = 0;
+ public static final int RIGHT = 1;
+ public static final int UP = 2;
+ public static final int DOWN = 3;
+ // This is the calculated movement threshold used track if the user is still
+ // moving their finger.
+ private final float mGestureDetectionThresholdPixels;
+
+ // Buffer for storing points for gesture detection.
+ private final ArrayList<PointF>[] mStrokeBuffers;
+
+ // The swipe direction for this matcher.
+ private int mDirection;
+ private int[] mPointerIds;
+ // The starting point of each finger's path in the gesture.
+ private PointF[] mBase;
+ // The most recent entry in each finger's gesture path.
+ private PointF[] mPreviousGesturePoint;
+ private int mTargetFingerCount;
+ private int mCurrentFingerCount;
+ // Whether the appropriate number of fingers have gone down at some point. This is reset only on
+ // clear.
+ private boolean mTargetFingerCountReached = false;
+ // Constants for sampling motion event points.
+ // We sample based on a minimum distance between points, primarily to improve accuracy by
+ // reducing noisy minor changes in direction.
+ private static final float MIN_CM_BETWEEN_SAMPLES = 0.25f;
+ private final float mMinPixelsBetweenSamplesX;
+ private final float mMinPixelsBetweenSamplesY;
+ // The minmimum distance the finger must travel before we evaluate the initial direction of the
+ // swipe.
+ // Anything less is still considered a touch.
+ private int mTouchSlop;
+
+ MultiFingerSwipe(
+ Context context,
+ int fingerCount,
+ int direction,
+ int gesture,
+ GestureMatcher.StateChangeListener listener) {
+ super(gesture, new Handler(context.getMainLooper()), listener);
+ mTargetFingerCount = fingerCount;
+ mPointerIds = new int[mTargetFingerCount];
+ mBase = new PointF[mTargetFingerCount];
+ mPreviousGesturePoint = new PointF[mTargetFingerCount];
+ mStrokeBuffers = new ArrayList[mTargetFingerCount];
+ mDirection = direction;
+ DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
+ mGestureDetectionThresholdPixels =
+ TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, MM_PER_CM, displayMetrics)
+ * GESTURE_CONFIRM_CM;
+ // Calculate minimum gesture velocity
+ final float pixelsPerCmX = displayMetrics.xdpi / GestureUtils.CM_PER_INCH;
+ final float pixelsPerCmY = displayMetrics.ydpi / GestureUtils.CM_PER_INCH;
+ mMinPixelsBetweenSamplesX = MIN_CM_BETWEEN_SAMPLES * pixelsPerCmX;
+ mMinPixelsBetweenSamplesY = MIN_CM_BETWEEN_SAMPLES * pixelsPerCmY;
+ mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+ clear();
+ }
+
+ @Override
+ protected void clear() {
+ mTargetFingerCountReached = false;
+ mCurrentFingerCount = 0;
+ for (int i = 0; i < mTargetFingerCount; ++i) {
+ mPointerIds[i] = INVALID_POINTER_ID;
+ if (mBase[i] == null) {
+ mBase[i] = new PointF();
+ }
+ mBase[i].x = Float.NaN;
+ mBase[i].y = Float.NaN;
+ if (mPreviousGesturePoint[i] == null) {
+ mPreviousGesturePoint[i] = new PointF();
+ }
+ mPreviousGesturePoint[i].x = Float.NaN;
+ mPreviousGesturePoint[i].y = Float.NaN;
+ if (mStrokeBuffers[i] == null) {
+ mStrokeBuffers[i] = new ArrayList<>(100);
+ }
+ mStrokeBuffers[i].clear();
+ }
+ super.clear();
+ }
+
+ @Override
+ protected void onDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (mCurrentFingerCount > 0) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ mCurrentFingerCount = 1;
+ final int actionIndex = getActionIndex(rawEvent);
+ final int pointerId = rawEvent.getPointerId(actionIndex);
+ int pointerIndex = rawEvent.getPointerCount() - 1;
+ if (pointerId < 0 || pointerId > rawEvent.getPointerCount() - 1) {
+ // Nonsensical pointer id.
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ if (mPointerIds[pointerIndex] != INVALID_POINTER_ID) {
+ // Inconsistent event stream.
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ mPointerIds[pointerIndex] = pointerId;
+ cancelAfterPauseThreshold(event, rawEvent, policyFlags);
+ if (Float.isNaN(mBase[pointerIndex].x) && Float.isNaN(mBase[pointerIndex].y)) {
+ final float x = rawEvent.getX(actionIndex);
+ final float y = rawEvent.getY(actionIndex);
+ if (x < 0f || y < 0f) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ mBase[pointerIndex].x = x;
+ mBase[pointerIndex].y = y;
+ mPreviousGesturePoint[pointerIndex].x = x;
+ mPreviousGesturePoint[pointerIndex].y = y;
+ } else {
+ // This event doesn't make sense in the middle of a gesture.
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ }
+
+ @Override
+ protected void onPointerDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (event.getPointerCount() > mTargetFingerCount) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ mCurrentFingerCount += 1;
+ if (mCurrentFingerCount != rawEvent.getPointerCount()) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ if (mCurrentFingerCount == mTargetFingerCount) {
+ mTargetFingerCountReached = true;
+ }
+ final int actionIndex = getActionIndex(rawEvent);
+ final int pointerId = rawEvent.getPointerId(actionIndex);
+ if (pointerId < 0 || pointerId > rawEvent.getPointerCount() - 1) {
+ // Nonsensical pointer id.
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ int pointerIndex = mCurrentFingerCount - 1;
+ if (mPointerIds[pointerIndex] != INVALID_POINTER_ID) {
+ // Inconsistent event stream.
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ mPointerIds[pointerIndex] = pointerId;
+ cancelAfterPauseThreshold(event, rawEvent, policyFlags);
+ if (Float.isNaN(mBase[pointerIndex].x) && Float.isNaN(mBase[pointerIndex].y)) {
+ final float x = rawEvent.getX(actionIndex);
+ final float y = rawEvent.getY(actionIndex);
+ if (x < 0f || y < 0f) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ mBase[pointerIndex].x = x;
+ mBase[pointerIndex].y = y;
+ mPreviousGesturePoint[pointerIndex].x = x;
+ mPreviousGesturePoint[pointerIndex].y = y;
+ } else {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ }
+
+ @Override
+ protected void onPointerUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (!mTargetFingerCountReached) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ mCurrentFingerCount -= 1;
+ final int actionIndex = getActionIndex(event);
+ final int pointerId = event.getPointerId(actionIndex);
+ if (pointerId < 0 || pointerId > rawEvent.getPointerCount() - 1) {
+ // Nonsensical pointer id.
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ final int pointerIndex = Arrays.binarySearch(mPointerIds, pointerId);
+ if (pointerIndex < 0) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ final float x = rawEvent.getX(actionIndex);
+ final float y = rawEvent.getY(actionIndex);
+ if (x < 0f || y < 0f) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ final float dX = Math.abs(x - mPreviousGesturePoint[pointerIndex].x);
+ final float dY = Math.abs(y - mPreviousGesturePoint[pointerIndex].y);
+ if (dX >= mMinPixelsBetweenSamplesX || dY >= mMinPixelsBetweenSamplesY) {
+ mStrokeBuffers[pointerIndex].add(new PointF(x, y));
+ }
+ // We will evaluate all the paths on ACTION_UP.
+ }
+
+ @Override
+ protected void onMove(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ for (int pointerIndex = 0; pointerIndex < rawEvent.getPointerCount(); ++pointerIndex) {
+ if (DEBUG) {
+ Slog.d(getGestureName(), "Processing move on finger " + pointerIndex);
+ }
+ int index = rawEvent.findPointerIndex(mPointerIds[pointerIndex]);
+ final float x = rawEvent.getX(index);
+ final float y = rawEvent.getY(index);
+ if (x < 0f || y < 0f) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ final float dX = Math.abs(x - mPreviousGesturePoint[pointerIndex].x);
+ final float dY = Math.abs(y - mPreviousGesturePoint[pointerIndex].y);
+ final double moveDelta =
+ Math.hypot(
+ Math.abs(x - mBase[pointerIndex].x),
+ Math.abs(y - mBase[pointerIndex].y));
+ if (DEBUG) {
+ Slog.d(
+ getGestureName(),
+ "moveDelta:"
+ + Double.toString(moveDelta)
+ + " mGestureDetectionThreshold: "
+ + Float.toString(mGestureDetectionThresholdPixels));
+ }
+ if (getState() == STATE_CLEAR) {
+ if (moveDelta < mTouchSlop) {
+ // This still counts as a touch not a swipe.
+ continue;
+ } else if (mStrokeBuffers[pointerIndex].size() == 0) {
+ // First, make sure we have the right number of fingers down.
+ if (mCurrentFingerCount != mTargetFingerCount) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ // Then, make sure the pointer is going in the right direction.
+ int direction =
+ toDirection(x - mBase[pointerIndex].x, y - mBase[pointerIndex].y);
+ if (direction != mDirection) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ } else {
+ // This is confirmed to be some kind of swipe so start tracking points.
+ cancelAfterPauseThreshold(event, rawEvent, policyFlags);
+ for (int i = 0; i < mTargetFingerCount; ++i) {
+ mStrokeBuffers[i].add(new PointF(mBase[i]));
+ }
+ }
+ }
+ if (moveDelta > mGestureDetectionThresholdPixels) {
+ // Try to cancel if the finger starts to go the wrong way.
+ // Note that this only works because this matcher assumes one direction.
+ int direction =
+ toDirection(x - mBase[pointerIndex].x, y - mBase[pointerIndex].y);
+ if (direction != mDirection) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ // If the pointer has moved more than the threshold,
+ // update the stored values.
+ mBase[pointerIndex].x = x;
+ mBase[pointerIndex].y = y;
+ mPreviousGesturePoint[pointerIndex].x = x;
+ mPreviousGesturePoint[pointerIndex].y = y;
+ if (getState() == STATE_CLEAR) {
+ startGesture(event, rawEvent, policyFlags);
+ cancelAfterPauseThreshold(event, rawEvent, policyFlags);
+ }
+ }
+ }
+ if (getState() == STATE_GESTURE_STARTED) {
+ if (dX >= mMinPixelsBetweenSamplesX || dY >= mMinPixelsBetweenSamplesY) {
+ // Sample every 2.5 MM in order to guard against minor variations in path.
+ mPreviousGesturePoint[pointerIndex].x = x;
+ mPreviousGesturePoint[pointerIndex].y = y;
+ mStrokeBuffers[pointerIndex].add(new PointF(x, y));
+ cancelAfterPauseThreshold(event, rawEvent, policyFlags);
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void onUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (getState() != STATE_GESTURE_STARTED) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ mCurrentFingerCount = 0;
+ final int actionIndex = getActionIndex(event);
+ final int pointerId = event.getPointerId(actionIndex);
+ final int pointerIndex = Arrays.binarySearch(mPointerIds, pointerId);
+ if (pointerIndex < 0) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ final float x = rawEvent.getX(actionIndex);
+ final float y = rawEvent.getY(actionIndex);
+ if (x < 0f || y < 0f) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ final float dX = Math.abs(x - mPreviousGesturePoint[pointerIndex].x);
+ final float dY = Math.abs(y - mPreviousGesturePoint[pointerIndex].y);
+ if (dX >= mMinPixelsBetweenSamplesX || dY >= mMinPixelsBetweenSamplesY) {
+ mStrokeBuffers[pointerIndex].add(new PointF(x, y));
+ }
+ recognizeGesture(event, rawEvent, policyFlags);
+ }
+
+ /**
+ * queues a transition to STATE_GESTURE_CANCEL based on the current state. If we have
+ * transitioned to STATE_GESTURE_STARTED the delay is longer.
+ */
+ private void cancelAfterPauseThreshold(
+ MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ cancelPendingTransitions();
+ switch (getState()) {
+ case STATE_CLEAR:
+ cancelAfter(CANCEL_ON_PAUSE_THRESHOLD_NOT_STARTED_MS, event, rawEvent, policyFlags);
+ break;
+ case STATE_GESTURE_STARTED:
+ cancelAfter(CANCEL_ON_PAUSE_THRESHOLD_STARTED_MS, event, rawEvent, policyFlags);
+ break;
+ default:
+ break;
+ }
+ }
+ /**
+ * Looks at the sequence of motions in mStrokeBuffer, classifies the gesture, then transitions
+ * to the complete or cancel state depending on the result.
+ */
+ private void recognizeGesture(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ // Check the path of each finger against the specified direction.
+ // Note that we sample every 2.5 MMm, and the direction matching is extremely tolerant (each
+ // direction has a 90-degree arch of tolerance) meaning that minor perpendicular movements
+ // should not create false negatives.
+ for (int i = 0; i < mTargetFingerCount; ++i) {
+ if (DEBUG) {
+ Slog.d(getGestureName(), "Recognizing finger: " + i);
+ }
+ if (mStrokeBuffers[i].size() < 2) {
+ Slog.d(getGestureName(), "Too few points.");
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ ArrayList<PointF> path = mStrokeBuffers[i];
+
+ if (DEBUG) {
+ Slog.d(getGestureName(), "path=" + path.toString());
+ }
+ // Classify line segments, and call Listener callbacks.
+ if (!recognizeGesturePath(event, rawEvent, policyFlags, path)) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ }
+ // If we reach this point then all paths match.
+ completeGesture(event, rawEvent, policyFlags);
+ }
+
+ /**
+ * Tests the path of a given finger against the direction specified in this matcher.
+ *
+ * @return True if the path matches the specified direction for this matcher, otherwise false.
+ */
+ private boolean recognizeGesturePath(
+ MotionEvent event, MotionEvent rawEvent, int policyFlags, ArrayList<PointF> path) {
+
+ final int displayId = event.getDisplayId();
+ for (int i = 0; i < path.size() - 1; ++i) {
+ PointF start = path.get(i);
+ PointF end = path.get(i + 1);
+
+ float dX = end.x - start.x;
+ float dY = end.y - start.y;
+ int direction = toDirection(dX, dY);
+ if (direction != mDirection) {
+ if (DEBUG) {
+ Slog.d(
+ getGestureName(),
+ "Found direction "
+ + directionToString(direction)
+ + " when expecting "
+ + directionToString(mDirection));
+ }
+ return false;
+ }
+ }
+ if (DEBUG) {
+ Slog.d(getGestureName(), "Completed.");
+ }
+ return true;
+ }
+
+ private static int toDirection(float dX, float dY) {
+ if (Math.abs(dX) > Math.abs(dY)) {
+ // Horizontal
+ return (dX < 0) ? LEFT : RIGHT;
+ } else {
+ // Vertical
+ return (dY < 0) ? UP : DOWN;
+ }
+ }
+
+ public static String directionToString(int direction) {
+ switch (direction) {
+ case LEFT:
+ return "left";
+ case RIGHT:
+ return "right";
+ case UP:
+ return "up";
+ case DOWN:
+ return "down";
+ default:
+ return "Unknown Direction";
+ }
+ }
+
+ @Override
+ String getGestureName() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(mTargetFingerCount).append("-finger ");
+ builder.append("Swipe ").append(directionToString(mDirection));
+ return builder.toString();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder(super.toString());
+ if (getState() != STATE_GESTURE_CANCELED) {
+ builder.append(", mBase: ")
+ .append(mBase.toString())
+ .append(", mGestureDetectionThreshold:")
+ .append(mGestureDetectionThresholdPixels)
+ .append(", mMinPixelsBetweenSamplesX:")
+ .append(mMinPixelsBetweenSamplesX)
+ .append(", mMinPixelsBetweenSamplesY:")
+ .append(mMinPixelsBetweenSamplesY);
+ }
+ return builder.toString();
+ }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/SecondFingerMultiTap.java b/services/accessibility/java/com/android/server/accessibility/gestures/SecondFingerMultiTap.java
index 8a566fc..ada251f 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/SecondFingerMultiTap.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/SecondFingerMultiTap.java
@@ -18,6 +18,8 @@
import static android.view.MotionEvent.INVALID_POINTER_ID;
+import static com.android.server.accessibility.gestures.GestureUtils.getActionIndex;
+
import android.content.Context;
import android.os.Handler;
import android.view.MotionEvent;
@@ -155,11 +157,6 @@
return moveDelta <= slop;
}
- private int getActionIndex(MotionEvent event) {
- return event.getAction()
- & MotionEvent.ACTION_POINTER_INDEX_MASK << MotionEvent.ACTION_POINTER_INDEX_SHIFT;
- }
-
@Override
public String toString() {
return super.toString()
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/Swipe.java b/services/accessibility/java/com/android/server/accessibility/gestures/Swipe.java
index a285c10..9108c69 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/Swipe.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/Swipe.java
@@ -16,10 +16,10 @@
package com.android.server.accessibility.gestures;
+import static com.android.server.accessibility.gestures.GestureUtils.MM_PER_CM;
import static com.android.server.accessibility.gestures.TouchExplorer.DEBUG;
import android.content.Context;
-import android.gesture.GesturePoint;
import android.graphics.PointF;
import android.os.Handler;
import android.util.DisplayMetrics;
@@ -44,10 +44,10 @@
public static final int DOWN = 3;
// This is the calculated movement threshold used track if the user is still
// moving their finger.
- private final float mGestureDetectionThreshold;
+ private final float mGestureDetectionThresholdPixels;
// Buffer for storing points for gesture detection.
- private final ArrayList<GesturePoint> mStrokeBuffer = new ArrayList<GesturePoint>(100);
+ private final ArrayList<PointF> mStrokeBuffer = new ArrayList<>(100);
// The minimal delta between moves to add a gesture point.
private static final int TOUCH_TOLERANCE_PIX = 3;
@@ -56,7 +56,7 @@
private static final float MIN_PREDICTION_SCORE = 2.0f;
// Distance a finger must travel before we decide if it is a gesture or not.
- private static final int GESTURE_CONFIRM_CM = 1;
+ public static final int GESTURE_CONFIRM_CM = 1;
// Time threshold used to determine if an interaction is a gesture or not.
// If the first movement of 1cm takes longer than this value, we assume it's
@@ -67,17 +67,16 @@
// all gestures started with the initial movement taking less than 100ms.
// When touch exploring, the first movement almost always takes longer than
// 200ms.
- private static final long CANCEL_ON_PAUSE_THRESHOLD_NOT_STARTED_MS = 150;
+ public static final long CANCEL_ON_PAUSE_THRESHOLD_NOT_STARTED_MS = 150;
// Time threshold used to determine if a gesture should be cancelled. If
// the finger takes more than this time to move 1cm, the ongoing gesture is
// cancelled.
- private static final long CANCEL_ON_PAUSE_THRESHOLD_STARTED_MS = 300;
+ public static final long CANCEL_ON_PAUSE_THRESHOLD_STARTED_MS = 300;
private int[] mDirections;
private float mBaseX;
private float mBaseY;
- private long mBaseTime;
private float mPreviousGestureX;
private float mPreviousGestureY;
// Constants for sampling motion event points.
@@ -119,8 +118,8 @@
super(gesture, new Handler(context.getMainLooper()), listener);
mDirections = directions;
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
- mGestureDetectionThreshold =
- TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, 10, displayMetrics)
+ mGestureDetectionThresholdPixels =
+ TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, MM_PER_CM, displayMetrics)
* GESTURE_CONFIRM_CM;
// Calculate minimum gesture velocity
final float pixelsPerCmX = displayMetrics.xdpi / 2.54f;
@@ -135,18 +134,16 @@
protected void clear() {
mBaseX = Float.NaN;
mBaseY = Float.NaN;
- mBaseTime = 0;
mStrokeBuffer.clear();
super.clear();
}
@Override
protected void onDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
- cancelAfterDelay(event, rawEvent, policyFlags);
+ cancelAfterPauseThreshold(event, rawEvent, policyFlags);
if (Float.isNaN(mBaseX) && Float.isNaN(mBaseY)) {
mBaseX = rawEvent.getX();
mBaseY = rawEvent.getY();
- mBaseTime = event.getEventTime();
mPreviousGestureX = mBaseX;
mPreviousGestureY = mBaseY;
}
@@ -157,10 +154,8 @@
protected void onMove(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
final float x = rawEvent.getX();
final float y = rawEvent.getY();
- final long time = event.getEventTime();
final float dX = Math.abs(x - mPreviousGestureX);
final float dY = Math.abs(y - mPreviousGestureY);
- final long timeDelta = time - mBaseTime;
final double moveDelta = Math.hypot(Math.abs(x - mBaseX), Math.abs(y - mBaseY));
if (DEBUG) {
Slog.d(
@@ -168,7 +163,7 @@
"moveDelta:"
+ Double.toString(moveDelta)
+ " mGestureDetectionThreshold: "
- + Float.toString(mGestureDetectionThreshold));
+ + Float.toString(mGestureDetectionThresholdPixels));
}
if (getState() == STATE_CLEAR) {
if (moveDelta < mTouchSlop) {
@@ -176,25 +171,24 @@
return;
} else if (mStrokeBuffer.size() == 0) {
// First, make sure the pointer is going in the right direction.
- cancelAfterDelay(event, rawEvent, policyFlags);
+ cancelAfterPauseThreshold(event, rawEvent, policyFlags);
int direction = toDirection(x - mBaseX, y - mBaseY);
if (direction != mDirections[0]) {
cancelGesture(event, rawEvent, policyFlags);
return;
} else {
// This is confirmed to be some kind of swipe so start tracking points.
- mStrokeBuffer.add(new GesturePoint(mBaseX, mBaseY, mBaseTime));
+ mStrokeBuffer.add(new PointF(mBaseX, mBaseY));
}
}
- if (moveDelta > mGestureDetectionThreshold) {
+ if (moveDelta > mGestureDetectionThresholdPixels) {
// If the pointer has moved more than the threshold,
// update the stored values.
mBaseX = x;
mBaseY = y;
- mBaseTime = time;
if (getState() == STATE_CLEAR) {
startGesture(event, rawEvent, policyFlags);
- cancelAfterDelay(event, rawEvent, policyFlags);
+ cancelAfterPauseThreshold(event, rawEvent, policyFlags);
}
}
}
@@ -202,8 +196,8 @@
if (dX >= mMinPixelsBetweenSamplesX || dY >= mMinPixelsBetweenSamplesY) {
mPreviousGestureX = x;
mPreviousGestureY = y;
- mStrokeBuffer.add(new GesturePoint(x, y, time));
- cancelAfterDelay(event, rawEvent, policyFlags);
+ mStrokeBuffer.add(new PointF(x, y));
+ cancelAfterPauseThreshold(event, rawEvent, policyFlags);
}
}
}
@@ -217,11 +211,10 @@
final float x = rawEvent.getX();
final float y = rawEvent.getY();
- final long time = event.getEventTime();
final float dX = Math.abs(x - mPreviousGestureX);
final float dY = Math.abs(y - mPreviousGestureY);
if (dX >= mMinPixelsBetweenSamplesX || dY >= mMinPixelsBetweenSamplesY) {
- mStrokeBuffer.add(new GesturePoint(x, y, time));
+ mStrokeBuffer.add(new PointF(x, y));
}
recognizeGesture(event, rawEvent, policyFlags);
}
@@ -240,7 +233,8 @@
* queues a transition to STATE_GESTURE_CANCEL based on the current state. If we have
* transitioned to STATE_GESTURE_STARTED the delay is longer.
*/
- private void cancelAfterDelay(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ private void cancelAfterPauseThreshold(
+ MotionEvent event, MotionEvent rawEvent, int policyFlags) {
cancelPendingTransitions();
switch (getState()) {
case STATE_CLEAR:
@@ -275,7 +269,7 @@
// 90 degrees.
ArrayList<PointF> path = new ArrayList<>();
- PointF lastDelimiter = new PointF(mStrokeBuffer.get(0).x, mStrokeBuffer.get(0).y);
+ PointF lastDelimiter = mStrokeBuffer.get(0);
path.add(lastDelimiter);
float dX = 0; // Sum of unit vectors from last delimiter to each following point
@@ -283,9 +277,9 @@
int count = 0; // Number of points since last delimiter
float length = 0; // Vector length from delimiter to most recent point
- PointF next = new PointF();
+ PointF next = null;
for (int i = 1; i < mStrokeBuffer.size(); ++i) {
- next = new PointF(mStrokeBuffer.get(i).x, mStrokeBuffer.get(i).y);
+ next = mStrokeBuffer.get(i);
if (count > 0) {
// Average of unit vectors from delimiter to following points
float currentDX = dX / count;
@@ -428,7 +422,7 @@
.append(", mBaseY: ")
.append(mBaseY)
.append(", mGestureDetectionThreshold:")
- .append(mGestureDetectionThreshold)
+ .append(mGestureDetectionThresholdPixels)
.append(", mMinPixelsBetweenSamplesX:")
.append(mMinPixelsBetweenSamplesX)
.append(", mMinPixelsBetweenSamplesY:")
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 4f18f35..1a4fc32 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -154,6 +154,13 @@
@GuardedBy("mLock")
private FillEventHistory mEventHistory;
+ /**
+ * The last inline augmented autofill selection. Note that we don't log the selection from the
+ * dropdown UI since the service owns the UI in that case.
+ */
+ @GuardedBy("mLock")
+ private FillEventHistory mAugmentedAutofillEventHistory;
+
/** Shared instance, doesn't need to be logged */
private final AutofillCompatState mAutofillCompatState;
@@ -707,6 +714,13 @@
}
}
+ void setLastAugmentedAutofillResponse(int sessionId) {
+ synchronized (mLock) {
+ mAugmentedAutofillEventHistory = new FillEventHistory(sessionId, /* clientState= */
+ null);
+ }
+ }
+
/**
* Resets the last fill selection.
*/
@@ -716,6 +730,12 @@
}
}
+ void resetLastAugmentedAutofillResponse() {
+ synchronized (mLock) {
+ mAugmentedAutofillEventHistory = null;
+ }
+ }
+
@GuardedBy("mLock")
private boolean isValidEventLocked(String method, int sessionId) {
if (mEventHistory == null) {
@@ -798,6 +818,32 @@
}
}
+ void logAugmentedAutofillSelected(int sessionId, @Nullable String suggestionId,
+ @Nullable Bundle clientState) {
+ synchronized (mLock) {
+ if (mAugmentedAutofillEventHistory == null
+ || mAugmentedAutofillEventHistory.getSessionId() != sessionId) {
+ return;
+ }
+ mAugmentedAutofillEventHistory.addEvent(
+ new Event(Event.TYPE_DATASET_SELECTED, suggestionId, clientState, null, null,
+ null, null, null, null, null, null));
+ }
+ }
+
+ void logAugmentedAutofillShown(int sessionId, @Nullable Bundle clientState) {
+ synchronized (mLock) {
+ if (mAugmentedAutofillEventHistory == null
+ || mAugmentedAutofillEventHistory.getSessionId() != sessionId) {
+ return;
+ }
+ mAugmentedAutofillEventHistory.addEvent(
+ new Event(Event.TYPE_DATASETS_SHOWN, null, clientState, null, null, null,
+ null, null, null, null, null));
+
+ }
+ }
+
/**
* Updates the last fill response when an autofill context is committed.
*/
@@ -881,8 +927,8 @@
* Gets the fill event history.
*
* @param callingUid The calling uid
- *
- * @return The history or {@code null} if there is none.
+ * @return The history for the autofill or the augmented autofill events depending on the {@code
+ * callingUid}, or {@code null} if there is none.
*/
FillEventHistory getFillEventHistory(int callingUid) {
synchronized (mLock) {
@@ -890,6 +936,10 @@
&& isCalledByServiceLocked("getFillEventHistory", callingUid)) {
return mEventHistory;
}
+ if (mAugmentedAutofillEventHistory != null && isCalledByAugmentedAutofillServiceLocked(
+ "getFillEventHistory", callingUid)) {
+ return mAugmentedAutofillEventHistory;
+ }
}
return null;
}
@@ -1163,8 +1213,32 @@
Slog.v(TAG, "getRemoteAugmentedAutofillServiceLocked(): " + componentName);
}
- mRemoteAugmentedAutofillService = new RemoteAugmentedAutofillService(getContext(),
- componentName, mUserId, new RemoteAugmentedAutofillServiceCallbacks() {
+ final RemoteAugmentedAutofillServiceCallbacks callbacks =
+ new RemoteAugmentedAutofillServiceCallbacks() {
+ @Override
+ public void resetLastResponse() {
+ AutofillManagerServiceImpl.this.resetLastAugmentedAutofillResponse();
+ }
+
+ @Override
+ public void setLastResponse(int sessionId) {
+ AutofillManagerServiceImpl.this.setLastAugmentedAutofillResponse(
+ sessionId);
+ }
+
+ @Override
+ public void logAugmentedAutofillShown(int sessionId, Bundle clientState) {
+ AutofillManagerServiceImpl.this.logAugmentedAutofillShown(sessionId,
+ clientState);
+ }
+
+ @Override
+ public void logAugmentedAutofillSelected(int sessionId, String suggestionId,
+ Bundle clientState) {
+ AutofillManagerServiceImpl.this.logAugmentedAutofillSelected(sessionId,
+ suggestionId, clientState);
+ }
+
@Override
public void onServiceDied(@NonNull RemoteAugmentedAutofillService service) {
Slog.w(TAG, "remote augmented autofill service died");
@@ -1175,8 +1249,10 @@
}
mRemoteAugmentedAutofillService = null;
}
- }, mMaster.isInstantServiceAllowed(), mMaster.verbose,
- mMaster.mAugmentedServiceIdleUnbindTimeoutMs,
+ };
+ mRemoteAugmentedAutofillService = new RemoteAugmentedAutofillService(getContext(),
+ componentName, mUserId, callbacks, mMaster.isInstantServiceAllowed(),
+ mMaster.verbose, mMaster.mAugmentedServiceIdleUnbindTimeoutMs,
mMaster.mAugmentedServiceRequestTimeoutMs);
}
diff --git a/services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java
index c7be80c..cb6c8f5 100644
--- a/services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java
@@ -28,7 +28,6 @@
import android.view.SurfaceControl;
import android.view.View;
import android.view.autofill.AutofillId;
-import android.view.autofill.IAutoFillManagerClient;
import android.view.inputmethod.InlineSuggestion;
import android.view.inputmethod.InlineSuggestionInfo;
import android.view.inputmethod.InlineSuggestionsResponse;
@@ -49,15 +48,24 @@
private static final String TAG = "InlineSuggestionFactory";
/**
+ * Callback from the inline suggestion Ui.
+ */
+ public interface InlineSuggestionUiCallback {
+ /**
+ * Callback to autofill a dataset to the client app.
+ */
+ void autofill(@NonNull Dataset dataset);
+ }
+
+ /**
* Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by
* augmented autofill service.
*/
public static InlineSuggestionsResponse createAugmentedInlineSuggestionsResponse(
- int sessionId,
@NonNull Dataset[] datasets,
@NonNull AutofillId autofillId,
@NonNull Context context,
- @NonNull IAutoFillManagerClient client) {
+ @NonNull InlineSuggestionUiCallback inlineSuggestionUiCallback) {
if (sDebug) Slog.d(TAG, "createAugmentedInlineSuggestionsResponse called");
final ArrayList<InlineSuggestion> inlineSuggestions = new ArrayList<>();
@@ -74,8 +82,8 @@
Slog.w(TAG, "InlinePresentation not found in dataset");
return null;
}
- InlineSuggestion inlineSuggestion = createAugmentedInlineSuggestion(sessionId, dataset,
- inlinePresentation, inlineSuggestionUi, client);
+ InlineSuggestion inlineSuggestion = createAugmentedInlineSuggestion(dataset,
+ inlinePresentation, inlineSuggestionUi, inlineSuggestionUiCallback);
inlineSuggestions.add(inlineSuggestion);
}
return new InlineSuggestionsResponse(inlineSuggestions);
@@ -114,22 +122,17 @@
return new InlineSuggestionsResponse(inlineSuggestions);
}
- private static InlineSuggestion createAugmentedInlineSuggestion(int sessionId,
- @NonNull Dataset dataset,
+ private static InlineSuggestion createAugmentedInlineSuggestion(@NonNull Dataset dataset,
@NonNull InlinePresentation inlinePresentation,
@NonNull InlineSuggestionUi inlineSuggestionUi,
- @NonNull IAutoFillManagerClient client) {
+ @NonNull InlineSuggestionUiCallback inlineSuggestionUiCallback) {
// TODO(b/146453195): fill in the autofill hint properly.
final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
inlinePresentation.getInlinePresentationSpec(),
InlineSuggestionInfo.SOURCE_PLATFORM, new String[]{""},
InlineSuggestionInfo.TYPE_SUGGESTION);
final View.OnClickListener onClickListener = v -> {
- try {
- client.autofill(sessionId, dataset.getFieldIds(), dataset.getFieldValues());
- } catch (RemoteException e) {
- Slog.w(TAG, "Encounter exception autofilling the values");
- }
+ inlineSuggestionUiCallback.autofill(dataset);
};
final InlineSuggestion inlineSuggestion = new InlineSuggestion(inlineSuggestionInfo,
createInlineContentProvider(inlinePresentation, inlineSuggestionUi,
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index 5fbdd25..5e6f6fea 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -69,6 +69,7 @@
private final int mIdleUnbindTimeoutMs;
private final int mRequestTimeoutMs;
private final ComponentName mComponentName;
+ private final RemoteAugmentedAutofillServiceCallbacks mCallbacks;
RemoteAugmentedAutofillService(Context context, ComponentName serviceName,
int userId, RemoteAugmentedAutofillServiceCallbacks callbacks,
@@ -81,6 +82,7 @@
mIdleUnbindTimeoutMs = idleUnbindTimeoutMs;
mRequestTimeoutMs = requestTimeoutMs;
mComponentName = serviceName;
+ mCallbacks = callbacks;
// Bind right away.
connect();
@@ -159,9 +161,12 @@
focusedId, focusedValue, requestTime, inlineSuggestionsRequest,
new IFillCallback.Stub() {
@Override
- public void onSuccess(@Nullable Dataset[] inlineSuggestionsData) {
- maybeHandleInlineSuggestions(sessionId, inlineSuggestionsData,
- focusedId, inlineSuggestionsCallback, client);
+ public void onSuccess(@Nullable Dataset[] inlineSuggestionsData,
+ @Nullable Bundle clientState) {
+ mCallbacks.resetLastResponse();
+ maybeRequestShowInlineSuggestions(sessionId,
+ inlineSuggestionsData, focusedId,
+ inlineSuggestionsCallback, client, clientState);
requestAutofill.complete(null);
}
@@ -223,20 +228,32 @@
});
}
- private void maybeHandleInlineSuggestions(int sessionId,
+ private void maybeRequestShowInlineSuggestions(int sessionId,
@Nullable Dataset[] inlineSuggestionsData, @NonNull AutofillId focusedId,
@Nullable IInlineSuggestionsResponseCallback inlineSuggestionsCallback,
- @NonNull IAutoFillManagerClient client) {
+ @NonNull IAutoFillManagerClient client, @Nullable Bundle clientState) {
if (ArrayUtils.isEmpty(inlineSuggestionsData) || inlineSuggestionsCallback == null) {
return;
}
+ mCallbacks.setLastResponse(sessionId);
try {
inlineSuggestionsCallback.onInlineSuggestionsResponse(
- InlineSuggestionFactory.createAugmentedInlineSuggestionsResponse(sessionId,
- inlineSuggestionsData, focusedId, mContext, client));
+ InlineSuggestionFactory.createAugmentedInlineSuggestionsResponse(
+ inlineSuggestionsData, focusedId, mContext,
+ dataset -> {
+ mCallbacks.logAugmentedAutofillSelected(sessionId,
+ dataset.getId(), clientState);
+ try {
+ client.autofill(sessionId, dataset.getFieldIds(),
+ dataset.getFieldValues());
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Encounter exception autofilling the values");
+ }
+ }));
} catch (RemoteException e) {
Slog.w(TAG, "Exception sending inline suggestions response back to IME.");
}
+ mCallbacks.logAugmentedAutofillShown(sessionId, clientState);
}
@Override
@@ -254,8 +271,13 @@
public interface RemoteAugmentedAutofillServiceCallbacks
extends AbstractRemoteService.VultureCallback<RemoteAugmentedAutofillService> {
- // NOTE: so far we don't need to notify the callback implementation (an inner class on
- // AutofillManagerServiceImpl) of the request results (success, timeouts, etc..), so this
- // callback interface is empty.
+ void resetLastResponse();
+
+ void setLastResponse(int sessionId);
+
+ void logAugmentedAutofillShown(int sessionId, @Nullable Bundle clientState);
+
+ void logAugmentedAutofillSelected(int sessionId, @Nullable String suggestionId,
+ @Nullable Bundle clientState);
}
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index ee37de5..415ecd8 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -2674,7 +2674,9 @@
if (response.supportsInlineSuggestions()) {
if (requestShowInlineSuggestions(response)) {
- //TODO(b/137800469): Add logging instead of bypassing below logic.
+ //TODO(b/137800469): Fix it to log showed only when IME asks for inflation, rather
+ // than here where framework sends back the response.
+ mService.logDatasetShown(id, mClientState);
return;
}
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 2982dc9..0194a21 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -557,13 +557,17 @@
.asInterface(ServiceManager.getService("dnsresolver"));
}
- /** Handler thread used for both of the handlers below. */
+ /** Handler thread used for all of the handlers below. */
@VisibleForTesting
protected final HandlerThread mHandlerThread;
/** Handler used for internal events. */
final private InternalHandler mHandler;
/** Handler used for incoming {@link NetworkStateTracker} events. */
final private NetworkStateTrackerHandler mTrackerHandler;
+ /** Handler used for processing {@link android.net.ConnectivityDiagnosticsManager} events */
+ @VisibleForTesting
+ final ConnectivityDiagnosticsHandler mConnectivityDiagnosticsHandler;
+
private final DnsManager mDnsManager;
private boolean mSystemReady;
@@ -630,6 +634,10 @@
@VisibleForTesting
final MultipathPolicyTracker mMultipathPolicyTracker;
+ @VisibleForTesting
+ final Map<IConnectivityDiagnosticsCallback, ConnectivityDiagnosticsCallbackInfo>
+ mConnectivityDiagnosticsCallbacks = new HashMap<>();
+
/**
* Implements support for the legacy "one network per network type" model.
*
@@ -962,6 +970,8 @@
mHandlerThread.start();
mHandler = new InternalHandler(mHandlerThread.getLooper());
mTrackerHandler = new NetworkStateTrackerHandler(mHandlerThread.getLooper());
+ mConnectivityDiagnosticsHandler =
+ new ConnectivityDiagnosticsHandler(mHandlerThread.getLooper());
mReleasePendingIntentDelayMs = Settings.Secure.getInt(context.getContentResolver(),
Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, 5_000);
@@ -3391,18 +3401,7 @@
nri.unlinkDeathRecipient();
mNetworkRequests.remove(nri.request);
- synchronized (mUidToNetworkRequestCount) {
- int requests = mUidToNetworkRequestCount.get(nri.mUid, 0);
- if (requests < 1) {
- Slog.wtf(TAG, "BUG: too small request count " + requests + " for UID " +
- nri.mUid);
- } else if (requests == 1) {
- mUidToNetworkRequestCount.removeAt(
- mUidToNetworkRequestCount.indexOfKey(nri.mUid));
- } else {
- mUidToNetworkRequestCount.put(nri.mUid, requests - 1);
- }
- }
+ decrementNetworkRequestPerUidCount(nri);
mNetworkRequestInfoLogs.log("RELEASE " + nri);
if (nri.request.isRequest()) {
@@ -3473,6 +3472,19 @@
}
}
+ private void decrementNetworkRequestPerUidCount(final NetworkRequestInfo nri) {
+ synchronized (mUidToNetworkRequestCount) {
+ final int requests = mUidToNetworkRequestCount.get(nri.mUid, 0);
+ if (requests < 1) {
+ Slog.wtf(TAG, "BUG: too small request count " + requests + " for UID " + nri.mUid);
+ } else if (requests == 1) {
+ mUidToNetworkRequestCount.removeAt(mUidToNetworkRequestCount.indexOfKey(nri.mUid));
+ } else {
+ mUidToNetworkRequestCount.put(nri.mUid, requests - 1);
+ }
+ }
+ }
+
@Override
public void setAcceptUnvalidated(Network network, boolean accept, boolean always) {
enforceNetworkStackSettingsOrSetup();
@@ -5091,6 +5103,10 @@
}
}
+ NetworkRequestInfo(NetworkRequest r) {
+ this(r, null);
+ }
+
private void enforceRequestCountLimit() {
synchronized (mUidToNetworkRequestCount) {
int networkRequests = mUidToNetworkRequestCount.get(mUid, 0) + 1;
@@ -6165,7 +6181,10 @@
private void callCallbackForRequest(NetworkRequestInfo nri,
NetworkAgentInfo networkAgent, int notificationType, int arg1) {
if (nri.messenger == null) {
- return; // Default request has no msgr
+ // Default request has no msgr. Also prevents callbacks from being invoked for
+ // NetworkRequestInfos registered with ConnectivityDiagnostics requests. Those callbacks
+ // are Type.LISTEN, but should not have NetworkCallbacks invoked.
+ return;
}
Bundle bundle = new Bundle();
// TODO: check if defensive copies of data is needed.
@@ -6319,12 +6338,34 @@
}
}
+ static class RequestReassignment {
+ @NonNull public final NetworkRequestInfo mRequest;
+ @Nullable public final NetworkAgentInfo mOldNetwork;
+ @Nullable public final NetworkAgentInfo mNewNetwork;
+ RequestReassignment(@NonNull final NetworkRequestInfo request,
+ @Nullable final NetworkAgentInfo oldNetwork,
+ @Nullable final NetworkAgentInfo newNetwork) {
+ mRequest = request;
+ mOldNetwork = oldNetwork;
+ mNewNetwork = newNetwork;
+ }
+ }
+
@NonNull private final Set<NetworkBgStatePair> mRematchedNetworks = new ArraySet<>();
+ @NonNull private final List<RequestReassignment> mReassignments = new ArrayList<>();
@NonNull Iterable<NetworkBgStatePair> getRematchedNetworks() {
return mRematchedNetworks;
}
+ @NonNull Iterable<RequestReassignment> getRequestReassignments() {
+ return mReassignments;
+ }
+
+ void addRequestReassignment(@NonNull final RequestReassignment reassignment) {
+ mReassignments.add(reassignment);
+ }
+
void addRematchedNetwork(@NonNull final NetworkBgStatePair network) {
mRematchedNetworks.add(network);
}
@@ -6400,20 +6441,13 @@
changes.addRematchedNetwork(new NetworkReassignment.NetworkBgStatePair(newNetwork,
newNetwork.isBackgroundNetwork()));
- final int score = newNetwork.getCurrentScore();
-
if (VDBG || DDBG) log("rematching " + newNetwork.name());
final ArrayMap<NetworkRequestInfo, NetworkAgentInfo> reassignedRequests =
computeRequestReassignmentForNetwork(newNetwork);
- NetworkCapabilities nc = newNetwork.networkCapabilities;
- if (VDBG) log(" network has: " + nc);
-
// Find and migrate to this Network any NetworkRequests for
// which this network is now the best.
- final ArrayList<NetworkAgentInfo> removedRequests = new ArrayList<>();
- final ArrayList<NetworkRequestInfo> addedRequests = new ArrayList<>();
for (final Map.Entry<NetworkRequestInfo, NetworkAgentInfo> entry :
reassignedRequests.entrySet()) {
final NetworkRequestInfo nri = entry.getKey();
@@ -6427,7 +6461,6 @@
}
previousSatisfier.removeRequest(nri.request.requestId);
previousSatisfier.lingerRequest(nri.request, now, mLingerDelayMs);
- removedRequests.add(previousSatisfier);
} else {
if (VDBG || DDBG) log(" accepting network in place of null");
}
@@ -6436,7 +6469,8 @@
if (!newSatisfier.addRequest(nri.request)) {
Slog.wtf(TAG, "BUG: " + newSatisfier.name() + " already has " + nri.request);
}
- addedRequests.add(nri);
+ changes.addRequestReassignment(new NetworkReassignment.RequestReassignment(
+ nri, previousSatisfier, newSatisfier));
// Tell NetworkProviders about the new score, so they can stop
// trying to connect if they know they cannot match it.
// TODO - this could get expensive if we have a lot of requests for this
@@ -6493,21 +6527,6 @@
// Have a new default network, release the transition wakelock in
scheduleReleaseNetworkTransitionWakelock();
}
-
- if (!newNetwork.networkCapabilities.equalRequestableCapabilities(nc)) {
- Slog.wtf(TAG, String.format(
- "BUG: %s changed requestable capabilities during rematch: %s -> %s",
- newNetwork.name(), nc, newNetwork.networkCapabilities));
- }
- if (newNetwork.getCurrentScore() != score) {
- Slog.wtf(TAG, String.format(
- "BUG: %s changed score during rematch: %d -> %d",
- newNetwork.name(), score, newNetwork.getCurrentScore()));
- }
-
- // Notify requested networks are available after the default net is switched, but
- // before LegacyTypeTracker sends legacy broadcasts
- for (NetworkRequestInfo nri : addedRequests) notifyNetworkAvailable(newNetwork, nri);
}
/**
@@ -6536,6 +6555,15 @@
final NetworkAgentInfo newDefaultNetwork = getDefaultNetwork();
+ // Notify requested networks are available after the default net is switched, but
+ // before LegacyTypeTracker sends legacy broadcasts
+ for (final NetworkReassignment.RequestReassignment event :
+ changes.getRequestReassignments()) {
+ if (null != event.mNewNetwork) {
+ notifyNetworkAvailable(event.mNewNetwork, event.mRequest);
+ }
+ }
+
for (final NetworkReassignment.NetworkBgStatePair event : changes.getRematchedNetworks()) {
// Process listen requests and update capabilities if the background state has
// changed for this network. For consistency with previous behavior, send onLost
@@ -7321,19 +7349,161 @@
}
}
+ /**
+ * Handler used for managing all Connectivity Diagnostics related functions.
+ *
+ * @see android.net.ConnectivityDiagnosticsManager
+ *
+ * TODO(b/147816404): Explore moving ConnectivityDiagnosticsHandler to a separate file
+ */
+ @VisibleForTesting
+ class ConnectivityDiagnosticsHandler extends Handler {
+ /**
+ * Used to handle ConnectivityDiagnosticsCallback registration events from {@link
+ * android.net.ConnectivityDiagnosticsManager}.
+ * obj = ConnectivityDiagnosticsCallbackInfo with IConnectivityDiagnosticsCallback and
+ * NetworkRequestInfo to be registered
+ */
+ private static final int EVENT_REGISTER_CONNECTIVITY_DIAGNOSTICS_CALLBACK = 1;
+
+ /**
+ * Used to handle ConnectivityDiagnosticsCallback unregister events from {@link
+ * android.net.ConnectivityDiagnosticsManager}.
+ * obj = the IConnectivityDiagnosticsCallback to be unregistered
+ * arg1 = the uid of the caller
+ */
+ private static final int EVENT_UNREGISTER_CONNECTIVITY_DIAGNOSTICS_CALLBACK = 2;
+
+ private ConnectivityDiagnosticsHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case EVENT_REGISTER_CONNECTIVITY_DIAGNOSTICS_CALLBACK: {
+ handleRegisterConnectivityDiagnosticsCallback(
+ (ConnectivityDiagnosticsCallbackInfo) msg.obj);
+ break;
+ }
+ case EVENT_UNREGISTER_CONNECTIVITY_DIAGNOSTICS_CALLBACK: {
+ handleUnregisterConnectivityDiagnosticsCallback(
+ (IConnectivityDiagnosticsCallback) msg.obj, msg.arg1);
+ break;
+ }
+ }
+ }
+ }
+
+ /** Class used for cleaning up IConnectivityDiagnosticsCallback instances after their death. */
+ @VisibleForTesting
+ class ConnectivityDiagnosticsCallbackInfo implements Binder.DeathRecipient {
+ @NonNull private final IConnectivityDiagnosticsCallback mCb;
+ @NonNull private final NetworkRequestInfo mRequestInfo;
+
+ @VisibleForTesting
+ ConnectivityDiagnosticsCallbackInfo(
+ @NonNull IConnectivityDiagnosticsCallback cb, @NonNull NetworkRequestInfo nri) {
+ mCb = cb;
+ mRequestInfo = nri;
+ }
+
+ @Override
+ public void binderDied() {
+ log("ConnectivityDiagnosticsCallback IBinder died.");
+ unregisterConnectivityDiagnosticsCallback(mCb);
+ }
+ }
+
+ private void handleRegisterConnectivityDiagnosticsCallback(
+ @NonNull ConnectivityDiagnosticsCallbackInfo cbInfo) {
+ ensureRunningOnConnectivityServiceThread();
+
+ final IConnectivityDiagnosticsCallback cb = cbInfo.mCb;
+ final NetworkRequestInfo nri = cbInfo.mRequestInfo;
+
+ // This means that the client registered the same callback multiple times. Do
+ // not override the previous entry, and exit silently.
+ if (mConnectivityDiagnosticsCallbacks.containsKey(cb)) {
+ if (VDBG) log("Diagnostics callback is already registered");
+
+ // Decrement the reference count for this NetworkRequestInfo. The reference count is
+ // incremented when the NetworkRequestInfo is created as part of
+ // enforceRequestCountLimit().
+ decrementNetworkRequestPerUidCount(nri);
+ return;
+ }
+
+ mConnectivityDiagnosticsCallbacks.put(cb, cbInfo);
+
+ try {
+ cb.asBinder().linkToDeath(cbInfo, 0);
+ } catch (RemoteException e) {
+ cbInfo.binderDied();
+ }
+ }
+
+ private void handleUnregisterConnectivityDiagnosticsCallback(
+ @NonNull IConnectivityDiagnosticsCallback cb, int uid) {
+ ensureRunningOnConnectivityServiceThread();
+
+ if (!mConnectivityDiagnosticsCallbacks.containsKey(cb)) {
+ if (VDBG) log("Removing diagnostics callback that is not currently registered");
+ return;
+ }
+
+ final NetworkRequestInfo nri = mConnectivityDiagnosticsCallbacks.get(cb).mRequestInfo;
+
+ if (uid != nri.mUid) {
+ if (VDBG) loge("Different uid than registrant attempting to unregister cb");
+ return;
+ }
+
+ cb.asBinder().unlinkToDeath(mConnectivityDiagnosticsCallbacks.remove(cb), 0);
+ }
+
@Override
public void registerConnectivityDiagnosticsCallback(
@NonNull IConnectivityDiagnosticsCallback callback, @NonNull NetworkRequest request) {
- // TODO(b/146444622): implement register IConnectivityDiagnosticsCallback functionality
- throw new UnsupportedOperationException(
- "registerConnectivityDiagnosticsCallback not yet implemented");
+ if (request.legacyType != TYPE_NONE) {
+ throw new IllegalArgumentException("ConnectivityManager.TYPE_* are deprecated."
+ + " Please use NetworkCapabilities instead.");
+ }
+
+ // This NetworkCapabilities is only used for matching to Networks. Clear out its owner uid
+ // and administrator uids to be safe.
+ final NetworkCapabilities nc = new NetworkCapabilities(request.networkCapabilities);
+ restrictRequestUidsForCaller(nc);
+
+ final NetworkRequest requestWithId =
+ new NetworkRequest(
+ nc, TYPE_NONE, nextNetworkRequestId(), NetworkRequest.Type.LISTEN);
+
+ // NetworkRequestInfos created here count towards MAX_NETWORK_REQUESTS_PER_UID limit.
+ //
+ // nri is not bound to the death of callback. Instead, callback.bindToDeath() is set in
+ // handleRegisterConnectivityDiagnosticsCallback(). nri will be cleaned up as part of the
+ // callback's binder death.
+ final NetworkRequestInfo nri = new NetworkRequestInfo(requestWithId);
+ final ConnectivityDiagnosticsCallbackInfo cbInfo =
+ new ConnectivityDiagnosticsCallbackInfo(callback, nri);
+
+ mConnectivityDiagnosticsHandler.sendMessage(
+ mConnectivityDiagnosticsHandler.obtainMessage(
+ ConnectivityDiagnosticsHandler
+ .EVENT_REGISTER_CONNECTIVITY_DIAGNOSTICS_CALLBACK,
+ cbInfo));
}
@Override
public void unregisterConnectivityDiagnosticsCallback(
@NonNull IConnectivityDiagnosticsCallback callback) {
- // TODO(b/146444622): implement register IConnectivityDiagnosticsCallback functionality
- throw new UnsupportedOperationException(
- "unregisterConnectivityDiagnosticsCallback not yet implemented");
+ mConnectivityDiagnosticsHandler.sendMessage(
+ mConnectivityDiagnosticsHandler.obtainMessage(
+ ConnectivityDiagnosticsHandler
+ .EVENT_UNREGISTER_CONNECTIVITY_DIAGNOSTICS_CALLBACK,
+ Binder.getCallingUid(),
+ 0,
+ callback));
}
}
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index bacffb6..3e5c278 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -393,8 +393,8 @@
@Override
public int onHealthCheckFailed(@Nullable VersionedPackage failedPackage,
@FailureReasons int failureReason) {
- if (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH
- || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING) {
+ if (!isDisabled() && (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH
+ || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING)) {
return mapRescueLevelToUserImpact(getNextRescueLevel());
} else {
return PackageHealthObserverImpact.USER_IMPACT_NONE;
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index d5fc14b..39f79ca 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -550,7 +550,6 @@
return data;
}
}
- Slog.e(TAG, "no controller energy info supplied for " + receiver.getName());
} catch (TimeoutException e) {
Slog.w(TAG, "timeout reading " + receiver.getName() + " stats");
}
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index bb8b12e..2fc9d04 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -25,7 +25,6 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Slog;
-import android.util.StatsLog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.compat.AndroidBuildClassifier;
@@ -54,7 +53,7 @@
public PlatformCompat(Context context) {
mContext = context;
mChangeReporter = new ChangeReporter(
- StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__SYSTEM_SERVER);
+ ChangeReporter.SOURCE_SYSTEM_SERVER);
mCompatConfig = CompatConfig.create(new AndroidBuildClassifier(), mContext);
}
@@ -62,14 +61,14 @@
PlatformCompat(Context context, CompatConfig compatConfig) {
mContext = context;
mChangeReporter = new ChangeReporter(
- StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__SYSTEM_SERVER);
+ ChangeReporter.SOURCE_SYSTEM_SERVER);
mCompatConfig = compatConfig;
}
@Override
public void reportChange(long changeId, ApplicationInfo appInfo) {
reportChange(changeId, appInfo.uid,
- StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED);
+ ChangeReporter.STATE_LOGGED);
}
@Override
@@ -83,18 +82,18 @@
@Override
public void reportChangeByUid(long changeId, int uid) {
- reportChange(changeId, uid, StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED);
+ reportChange(changeId, uid, ChangeReporter.STATE_LOGGED);
}
@Override
public boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) {
if (mCompatConfig.isChangeEnabled(changeId, appInfo)) {
reportChange(changeId, appInfo.uid,
- StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__ENABLED);
+ ChangeReporter.STATE_ENABLED);
return true;
}
reportChange(changeId, appInfo.uid,
- StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__DISABLED);
+ ChangeReporter.STATE_DISABLED);
return false;
}
diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
index 8179c32..669f1ac 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
@@ -186,6 +186,7 @@
.setConnectionState(MediaRoute2Info.CONNECTION_STATE_DISCONNECTED)
.setDescription(mContext.getResources().getText(
R.string.bluetooth_a2dp_audio_route_name).toString())
+ .setDeviceType(MediaRoute2Info.DEVICE_TYPE_BLUETOOTH)
.build();
newBtRoute.connectedProfiles = new SparseBooleanArray();
return newBtRoute;
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index 1cd8aad..5123362 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -36,6 +36,7 @@
final Object mLock = new Object();
Callback mCallback;
+ boolean mIsSystemRouteProvider;
private volatile MediaRoute2ProviderInfo mProviderInfo;
@GuardedBy("mLock")
@@ -85,6 +86,7 @@
} else {
mProviderInfo = new MediaRoute2ProviderInfo.Builder(providerInfo)
.setUniqueId(mUniqueId)
+ .setSystemRouteProvider(mIsSystemRouteProvider)
.build();
}
}
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 558eb8d..924a9b7 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -83,6 +83,8 @@
super(sComponentName);
setCallback(callback);
+ mIsSystemRouteProvider = true;
+
mContext = context;
mHandler = new Handler(Looper.getMainLooper());
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 93a0506..148c5ad 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2728,7 +2728,7 @@
t.traceBegin("get system config");
SystemConfig systemConfig = SystemConfig.getInstance();
mAvailableFeatures = systemConfig.getAvailableFeatures();
- ApplicationPackageManager.invalidateSysFeatureCache();
+ ApplicationPackageManager.invalidateHasSystemFeatureCache();
t.traceEnd();
mProtectedPackages = new ProtectedPackages(mContext);
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 4bab224..4425c0a 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -52,6 +52,7 @@
import android.provider.DeviceConfig;
import android.util.ArraySet;
import android.util.IntArray;
+import android.util.Log;
import android.util.LongArrayQueue;
import android.util.Slog;
import android.util.SparseBooleanArray;
@@ -94,7 +95,7 @@
class RollbackManagerServiceImpl extends IRollbackManager.Stub {
private static final String TAG = "RollbackManager";
- private static final boolean LOCAL_LOGV = false;
+ private static final boolean LOCAL_LOGV = Log.isLoggable(TAG, Log.VERBOSE);
// Rollbacks expire after 14 days.
private static final long DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS =
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 0f79a11..96f1219 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -899,7 +899,6 @@
return data;
}
}
- Slog.e(TAG, "no controller energy info supplied for " + receiver.getName());
} catch (TimeoutException e) {
Slog.w(TAG, "timeout reading " + receiver.getName() + " stats");
}
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index e95fc4a..a1e643f 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -101,11 +101,12 @@
private TimestampedValue<Long> mLastAutoSystemClockTimeSet;
/**
- * A mapping from phoneId to a time suggestion. We typically expect one or two mappings: devices
- * will have a small number of telephony devices and phoneIds are assumed to be stable.
+ * A mapping from slotIndex to a time suggestion. We typically expect one or two mappings:
+ * devices will have a small number of telephony devices and slotIndexs are assumed to be
+ * stable.
*/
@GuardedBy("this")
- private final ArrayMapWithHistory<Integer, PhoneTimeSuggestion> mSuggestionByPhoneId =
+ private final ArrayMapWithHistory<Integer, PhoneTimeSuggestion> mSuggestionBySlotIndex =
new ArrayMapWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
@GuardedBy("this")
@@ -155,7 +156,7 @@
}
// Perform validation / input filtering and record the validated suggestion against the
- // phoneId.
+ // slotIndex.
if (!validateAndStorePhoneSuggestion(timeSuggestion)) {
return;
}
@@ -202,7 +203,7 @@
ipw.println("Phone suggestion history:");
ipw.increaseIndent(); // level 2
- mSuggestionByPhoneId.dump(ipw);
+ mSuggestionBySlotIndex.dump(ipw);
ipw.decreaseIndent(); // level 2
ipw.println("Network suggestion history:");
@@ -223,8 +224,8 @@
return false;
}
- int phoneId = suggestion.getPhoneId();
- PhoneTimeSuggestion previousSuggestion = mSuggestionByPhoneId.get(phoneId);
+ int slotIndex = suggestion.getSlotIndex();
+ PhoneTimeSuggestion previousSuggestion = mSuggestionBySlotIndex.get(slotIndex);
if (previousSuggestion != null) {
// We can log / discard suggestions with obvious issues with the reference time clock.
if (previousSuggestion.getUtcTime() == null
@@ -249,7 +250,7 @@
}
// Store the latest suggestion.
- mSuggestionByPhoneId.put(phoneId, suggestion);
+ mSuggestionBySlotIndex.put(slotIndex, suggestion);
return true;
}
@@ -323,15 +324,15 @@
//
// [1] https://en.wikipedia.org/wiki/NITZ
//
- // Generally, when there are suggestions from multiple phoneIds they should usually
+ // Generally, when there are suggestions from multiple slotIndexs they should usually
// approximately agree. In cases where signals *are* inaccurate we don't want to vacillate
- // between signals from two phoneIds. However, it is known for NITZ signals to be incorrect
- // occasionally, which means we also don't want to stick forever with one phoneId. Without
- // cross-referencing across sources (e.g. the current device time, NTP), or doing some kind
- // of statistical analysis of consistency within and across phoneIds, we can't know which
- // suggestions are more correct.
+ // between signals from two slotIndexs. However, it is known for NITZ signals to be
+ // incorrect occasionally, which means we also don't want to stick forever with one
+ // slotIndex. Without cross-referencing across sources (e.g. the current device time, NTP),
+ // or doing some kind of statistical analysis of consistency within and across slotIndexs,
+ // we can't know which suggestions are more correct.
//
- // For simplicity, we try to value recency, then consistency of phoneId.
+ // For simplicity, we try to value recency, then consistency of slotIndex.
//
// The heuristic works as follows:
// Recency: The most recent suggestion from each phone is scored. The score is based on a
@@ -339,20 +340,20 @@
// bucket, thus applying a loose reference time ordering. The suggestion with the highest
// score is used.
// Consistency: If there a multiple suggestions with the same score, the suggestion with the
- // lowest phoneId is always taken.
+ // lowest slotIndex is always taken.
//
// In the trivial case with a single ID this will just mean that the latest received
// suggestion is used.
PhoneTimeSuggestion bestSuggestion = null;
int bestScore = PHONE_INVALID_SCORE;
- for (int i = 0; i < mSuggestionByPhoneId.size(); i++) {
- Integer phoneId = mSuggestionByPhoneId.keyAt(i);
- PhoneTimeSuggestion candidateSuggestion = mSuggestionByPhoneId.valueAt(i);
+ for (int i = 0; i < mSuggestionBySlotIndex.size(); i++) {
+ Integer slotIndex = mSuggestionBySlotIndex.keyAt(i);
+ PhoneTimeSuggestion candidateSuggestion = mSuggestionBySlotIndex.valueAt(i);
if (candidateSuggestion == null) {
// Unexpected - null suggestions should never be stored.
- Slog.w(LOG_TAG, "Latest suggestion unexpectedly null for phoneId."
- + " phoneId=" + phoneId);
+ Slog.w(LOG_TAG, "Latest suggestion unexpectedly null for slotIndex."
+ + " slotIndex=" + slotIndex);
continue;
} else if (candidateSuggestion.getUtcTime() == null) {
// Unexpected - we do not store empty suggestions.
@@ -372,10 +373,10 @@
bestSuggestion = candidateSuggestion;
bestScore = candidateScore;
} else if (bestScore == candidateScore) {
- // Tie! Use the suggestion with the lowest phoneId.
- int candidatePhoneId = candidateSuggestion.getPhoneId();
- int bestPhoneId = bestSuggestion.getPhoneId();
- if (candidatePhoneId < bestPhoneId) {
+ // Tie! Use the suggestion with the lowest slotIndex.
+ int candidateSlotIndex = candidateSuggestion.getSlotIndex();
+ int bestSlotIndex = bestSuggestion.getSlotIndex();
+ if (candidateSlotIndex < bestSlotIndex) {
bestSuggestion = candidateSuggestion;
}
}
@@ -396,7 +397,7 @@
}
// The score is based on the age since receipt. Suggestions are bucketed so two
- // suggestions in the same bucket from different phoneIds are scored the same.
+ // suggestions in the same bucket from different slotIndexs are scored the same.
long ageMillis = elapsedRealtimeMillis - utcTime.getReferenceTimeMillis();
// Turn the age into a discrete value: 0 <= bucketIndex < PHONE_BUCKET_COUNT.
@@ -560,8 +561,8 @@
*/
@VisibleForTesting
@Nullable
- public synchronized PhoneTimeSuggestion getLatestPhoneSuggestion(int phoneId) {
- return mSuggestionByPhoneId.get(phoneId);
+ public synchronized PhoneTimeSuggestion getLatestPhoneSuggestion(int slotIndex) {
+ return mSuggestionBySlotIndex.get(slotIndex);
}
/**
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
index b4a4399..b0e0069 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
@@ -172,12 +172,12 @@
private final LocalLog mTimeZoneChangesLog = new LocalLog(30, false /* useLocalTimestamps */);
/**
- * A mapping from phoneId to a phone time zone suggestion. We typically expect one or two
- * mappings: devices will have a small number of telephony devices and phoneIds are assumed to
+ * A mapping from slotIndex to a phone time zone suggestion. We typically expect one or two
+ * mappings: devices will have a small number of telephony devices and slotIndexs are assumed to
* be stable.
*/
@GuardedBy("this")
- private ArrayMapWithHistory<Integer, QualifiedPhoneTimeZoneSuggestion> mSuggestionByPhoneId =
+ private ArrayMapWithHistory<Integer, QualifiedPhoneTimeZoneSuggestion> mSuggestionBySlotIndex =
new ArrayMapWithHistory<>(KEEP_PHONE_SUGGESTION_HISTORY_SIZE);
/**
@@ -205,7 +205,7 @@
/**
* Suggests a time zone for the device, or withdraws a previous suggestion if
* {@link PhoneTimeZoneSuggestion#getZoneId()} is {@code null}. The suggestion is scoped to a
- * specific {@link PhoneTimeZoneSuggestion#getPhoneId() phone}.
+ * specific {@link PhoneTimeZoneSuggestion#getSlotIndex() phone}.
* See {@link PhoneTimeZoneSuggestion} for an explanation of the metadata associated with a
* suggestion. The strategy uses suggestions to decide whether to modify the device's time zone
* setting and what to set it to.
@@ -221,8 +221,8 @@
QualifiedPhoneTimeZoneSuggestion scoredSuggestion =
new QualifiedPhoneTimeZoneSuggestion(suggestion, score);
- // Store the suggestion against the correct phoneId.
- mSuggestionByPhoneId.put(suggestion.getPhoneId(), scoredSuggestion);
+ // Store the suggestion against the correct slotIndex.
+ mSuggestionBySlotIndex.put(suggestion.getSlotIndex(), scoredSuggestion);
// Now perform auto time zone detection. The new suggestion may be used to modify the time
// zone setting.
@@ -384,8 +384,9 @@
// and find the best. Note that we deliberately do not look at age: the caller can
// rate-limit so age is not a strong indicator of confidence. Instead, the callers are
// expected to withdraw suggestions they no longer have confidence in.
- for (int i = 0; i < mSuggestionByPhoneId.size(); i++) {
- QualifiedPhoneTimeZoneSuggestion candidateSuggestion = mSuggestionByPhoneId.valueAt(i);
+ for (int i = 0; i < mSuggestionBySlotIndex.size(); i++) {
+ QualifiedPhoneTimeZoneSuggestion candidateSuggestion =
+ mSuggestionBySlotIndex.valueAt(i);
if (candidateSuggestion == null) {
// Unexpected
continue;
@@ -396,10 +397,10 @@
} else if (candidateSuggestion.score > bestSuggestion.score) {
bestSuggestion = candidateSuggestion;
} else if (candidateSuggestion.score == bestSuggestion.score) {
- // Tie! Use the suggestion with the lowest phoneId.
- int candidatePhoneId = candidateSuggestion.suggestion.getPhoneId();
- int bestPhoneId = bestSuggestion.suggestion.getPhoneId();
- if (candidatePhoneId < bestPhoneId) {
+ // Tie! Use the suggestion with the lowest slotIndex.
+ int candidateSlotIndex = candidateSuggestion.suggestion.getSlotIndex();
+ int bestSlotIndex = bestSuggestion.suggestion.getSlotIndex();
+ if (candidateSlotIndex < bestSlotIndex) {
bestSuggestion = candidateSuggestion;
}
}
@@ -455,7 +456,7 @@
ipw.println("Phone suggestion history:");
ipw.increaseIndent(); // level 2
- mSuggestionByPhoneId.dump(ipw);
+ mSuggestionBySlotIndex.dump(ipw);
ipw.decreaseIndent(); // level 2
ipw.decreaseIndent(); // level 1
ipw.flush();
@@ -465,8 +466,8 @@
* A method used to inspect strategy state during tests. Not intended for general use.
*/
@VisibleForTesting
- public synchronized QualifiedPhoneTimeZoneSuggestion getLatestPhoneSuggestion(int phoneId) {
- return mSuggestionByPhoneId.get(phoneId);
+ public synchronized QualifiedPhoneTimeZoneSuggestion getLatestPhoneSuggestion(int slotIndex) {
+ return mSuggestionBySlotIndex.get(slotIndex);
}
/**
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index b05c250..7af097b 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -196,6 +196,7 @@
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.TaskPersister.DEBUG;
import static com.android.server.wm.TaskPersister.IMAGE_EXTENSION;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
@@ -311,6 +312,8 @@
import com.android.server.uri.UriPermissionOwner;
import com.android.server.wm.ActivityMetricsLogger.TransitionInfoSnapshot;
import com.android.server.wm.ActivityStack.ActivityState;
+import com.android.server.wm.SurfaceAnimator.AnimationType;
+import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
import com.android.server.wm.WindowManagerService.H;
import com.android.server.wm.utils.InsetUtils;
@@ -4080,7 +4083,8 @@
@Override
boolean applyAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
- boolean isVoiceInteraction, @Nullable Runnable animationFinishedCallback) {
+ boolean isVoiceInteraction,
+ @Nullable OnAnimationFinishedCallback animationFinishedCallback) {
if (mUseTransferredAnimation) {
return false;
}
@@ -4158,7 +4162,7 @@
// We aren't delayed anything, but exiting windows rely on the animation finished
// callback being called in case the ActivityRecord was pretending to be delayed,
// which we might have done because we were in closing/opening apps list.
- onAnimationFinished();
+ onAnimationFinished(ANIMATION_TYPE_APP_TRANSITION, null /* AnimationAdapter */);
if (visible) {
// The token was made immediately visible, there will be no entrance animation.
// We need to inform the client the enter animation was finished.
@@ -6054,8 +6058,8 @@
}
@Override
- protected void onAnimationFinished() {
- super.onAnimationFinished();
+ protected void onAnimationFinished(@AnimationType int type, AnimationAdapter anim) {
+ super.onAnimationFinished(type, anim);
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AR#onAnimationFinished");
mTransit = TRANSIT_UNSET;
diff --git a/services/core/java/com/android/server/wm/AnimationAdapter.java b/services/core/java/com/android/server/wm/AnimationAdapter.java
index 1be3d61..0519b80 100644
--- a/services/core/java/com/android/server/wm/AnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/AnimationAdapter.java
@@ -21,6 +21,7 @@
import android.view.SurfaceControl.Transaction;
import android.view.animation.Animation;
+import com.android.server.wm.SurfaceAnimator.AnimationType;
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
import java.io.PrintWriter;
@@ -47,9 +48,10 @@
* component running the animation after {@code finishCallback} has been
* invoked, or after the animation was cancelled.
* @param t The Transaction to apply the initial frame of the animation.
+ * @param type The type of the animation.
* @param finishCallback The callback to be invoked when the animation has finished.
*/
- void startAnimation(SurfaceControl animationLeash, Transaction t,
+ void startAnimation(SurfaceControl animationLeash, Transaction t, @AnimationType int type,
OnAnimationFinishedCallback finishCallback);
/**
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 0798a91..f72020e 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -377,9 +377,9 @@
transitioningDecendants.add(app);
}
}
- wc.applyAnimation(animLp, transit, visible, voiceInteraction, () -> {
+ wc.applyAnimation(animLp, transit, visible, voiceInteraction, (type, anim) -> {
for (int j = 0; j < transitioningDecendants.size(); ++j) {
- transitioningDecendants.get(j).onAnimationFinished();
+ transitioningDecendants.get(j).onAnimationFinished(type, anim);
}
});
}
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index 16aff9c..537ca08 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -20,6 +20,7 @@
import static com.android.server.wm.AlphaAnimationSpecProto.FROM;
import static com.android.server.wm.AlphaAnimationSpecProto.TO;
import static com.android.server.wm.AnimationSpecProto.ALPHA;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_DIMMER;
import android.annotation.Nullable;
import android.graphics.Rect;
@@ -29,6 +30,8 @@
import android.view.SurfaceControl;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wm.SurfaceAnimator.AnimationType;
+import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
import java.io.PrintWriter;
@@ -135,7 +138,7 @@
mDimLayer = dimLayer;
mDimming = true;
final DimAnimatable dimAnimatable = new DimAnimatable(dimLayer);
- mSurfaceAnimator = new SurfaceAnimator(dimAnimatable, () -> {
+ mSurfaceAnimator = new SurfaceAnimator(dimAnimatable, (type, anim) -> {
if (!mDimming) {
dimAnimatable.removeSurface();
}
@@ -157,8 +160,8 @@
@VisibleForTesting
interface SurfaceAnimatorStarter {
void startAnimation(SurfaceAnimator surfaceAnimator, SurfaceControl.Transaction t,
- AnimationAdapter anim, boolean hidden,
- @Nullable Runnable animationFinishedCallback);
+ AnimationAdapter anim, boolean hidden, @AnimationType int type,
+ @Nullable OnAnimationFinishedCallback animationFinishedCallback);
}
Dimmer(WindowContainer host) {
@@ -345,7 +348,7 @@
mSurfaceAnimatorStarter.startAnimation(animator, t, new LocalAnimationAdapter(
new AlphaAnimationSpec(startAlpha, endAlpha, getDimDuration(container)),
mHost.mWmService.mSurfaceAnimationRunner), false /* hidden */,
- null /* animationFinishedCallback */);
+ ANIMATION_TYPE_DIMMER, null /* animationFinishedCallback */);
}
private long getDimDuration(WindowContainer container) {
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
new file mode 100644
index 0000000..b3edc91
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
+import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
+
+import static com.android.internal.util.Preconditions.checkState;
+import static com.android.server.wm.DisplayAreaChildProto.DISPLAY_AREA;
+import static com.android.server.wm.DisplayAreaChildProto.UNKNOWN;
+import static com.android.server.wm.DisplayAreaChildProto.WINDOW;
+import static com.android.server.wm.DisplayAreaProto.CHILDREN;
+import static com.android.server.wm.DisplayAreaProto.NAME;
+import static com.android.server.wm.DisplayAreaProto.WINDOW_CONTAINER;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
+
+import android.graphics.Rect;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.protolog.common.ProtoLog;
+
+import java.util.Comparator;
+import java.util.function.Predicate;
+
+/**
+ * Container for grouping WindowContainer below DisplayContent.
+ *
+ * DisplayAreas are managed by a {@link DisplayAreaPolicy}, and can override configurations and
+ * can be leashed.
+ *
+ * DisplayAreas can contain nested DisplayAreas.
+ *
+ * DisplayAreas come in three flavors, to ensure that windows have the right Z-Order:
+ * - BELOW_TASKS: Can only contain BELOW_TASK DisplayAreas and WindowTokens that go below tasks.
+ * - ABOVE_TASKS: Can only contain ABOVE_TASK DisplayAreas and WindowTokens that go above tasks.
+ * - ANY: Can contain any kind of DisplayArea, and any kind of WindowToken or the Task container.
+ * Cannot have a sibling that is of type ANY.
+ *
+ * @param <T> type of the children of the DisplayArea.
+ */
+public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
+
+ protected final Type mType;
+ private final String mName;
+
+ DisplayArea(WindowManagerService wms, Type type, String name) {
+ super(wms);
+ // TODO(display-area): move this up to ConfigurationContainer
+ mOrientation = SCREEN_ORIENTATION_UNSET;
+ mType = type;
+ mName = name;
+ }
+
+ @Override
+ void onChildPositionChanged(WindowContainer child) {
+ super.onChildPositionChanged(child);
+
+ // Verify that we have proper ordering
+ Type.checkChild(mType, Type.typeOf(child));
+
+ if (child instanceof ActivityStack) {
+ // TODO(display-area): ActivityStacks are type ANY, but are allowed to have siblings.
+ // They might need a separate type.
+ return;
+ }
+
+ for (int i = 1; i < getChildCount(); i++) {
+ final WindowContainer top = getChildAt(i - 1);
+ final WindowContainer bottom = getChildAt(i);
+ if (child == top || child == bottom) {
+ Type.checkSiblings(Type.typeOf(top), Type.typeOf(bottom));
+ }
+ }
+ }
+
+ @Override
+ boolean fillsParent() {
+ return true;
+ }
+
+ @Override
+ String getName() {
+ return mName;
+ }
+
+ @Override
+ public final void dumpDebug(ProtoOutputStream proto, long fieldId, int logLevel) {
+ final long token = proto.start(fieldId);
+ super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
+ proto.write(NAME, mName);
+ for (int i = 0; i < getChildCount(); i++) {
+ final long childToken = proto.start(CHILDREN);
+ final T child = getChildAt(i);
+ if (child instanceof ActivityStack) {
+ // TODO(display-area): Dump stacks & tasks here, instead of in DisplayContent's
+ // dumpDebug. For now, skip them here to avoid dumping them as UNKNOWN.
+ } else if (child instanceof WindowToken) {
+ ((WindowToken) child).dumpDebug(proto, WINDOW, logLevel);
+ } else if (child instanceof DisplayArea) {
+ child.dumpDebug(proto, DISPLAY_AREA, logLevel);
+ } else {
+ proto.write(UNKNOWN, child.getClass().getSimpleName());
+ }
+ proto.end(childToken);
+ }
+ proto.end(token);
+ }
+
+ /**
+ * DisplayArea that contains WindowTokens, and orders them according to their type.
+ */
+ public static class Tokens extends DisplayArea<WindowToken> {
+ int mLastKeyguardForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
+
+ private final Comparator<WindowToken> mWindowComparator =
+ Comparator.comparingInt(WindowToken::getWindowLayerFromType);
+
+ private final Predicate<WindowState> mGetOrientingWindow = w -> {
+ final WindowManagerPolicy policy = mWmService.mPolicy;
+ if (policy.isKeyguardHostWindow(w.mAttrs)) {
+ if (mWmService.mKeyguardGoingAway) {
+ return false;
+ }
+ // Consider unoccluding only when all unknown visibilities have been
+ // resolved, as otherwise we just may be starting another occluding activity.
+ final boolean isUnoccluding =
+ mDisplayContent.mAppTransition.getAppTransition()
+ == TRANSIT_KEYGUARD_UNOCCLUDE
+ && mDisplayContent.mUnknownAppVisibilityController.allResolved();
+ // If keyguard is showing, or we're unoccluding, force the keyguard's orientation,
+ // even if SystemUI hasn't updated the attrs yet.
+ if (policy.isKeyguardShowingAndNotOccluded() || isUnoccluding) {
+ return true;
+ }
+ }
+ final int req = w.mAttrs.screenOrientation;
+ if (req == SCREEN_ORIENTATION_UNSPECIFIED || req == SCREEN_ORIENTATION_BEHIND
+ || req == SCREEN_ORIENTATION_UNSET) {
+ return false;
+ }
+ return true;
+ };
+
+ Tokens(WindowManagerService wms, Type type, String name) {
+ super(wms, type, name);
+ }
+
+ void addChild(WindowToken token) {
+ addChild(token, mWindowComparator);
+ }
+
+ @Override
+ int getOrientation(int candidate) {
+ // Find a window requesting orientation.
+ final WindowState win = getWindow(mGetOrientingWindow);
+
+ if (win == null) {
+ return candidate;
+ }
+ int req = win.mAttrs.screenOrientation;
+ ProtoLog.v(WM_DEBUG_ORIENTATION, "%s forcing orientation to %d for display id=%d",
+ win, req, mDisplayContent.getDisplayId());
+ if (mWmService.mPolicy.isKeyguardHostWindow(win.mAttrs)) {
+ // SystemUI controls the Keyguard orientation asynchronously, and mAttrs may be
+ // stale. We record / use the last known override.
+ if (req != SCREEN_ORIENTATION_UNSET && req != SCREEN_ORIENTATION_UNSPECIFIED) {
+ mLastKeyguardForcedOrientation = req;
+ } else {
+ req = mLastKeyguardForcedOrientation;
+ }
+ }
+ return req;
+ }
+ }
+
+ /**
+ * Top-most DisplayArea under DisplayContent.
+ */
+ public static class Root extends DisplayArea<DisplayArea> {
+ private final Dimmer mDimmer = new Dimmer(this);
+ private final Rect mTmpDimBoundsRect = new Rect();
+
+ Root(WindowManagerService wms) {
+ super(wms, Type.ANY, "DisplayArea.Root");
+ }
+
+ @Override
+ Dimmer getDimmer() {
+ return mDimmer;
+ }
+
+ @Override
+ void prepareSurfaces() {
+ mDimmer.resetDimStates();
+ super.prepareSurfaces();
+ getBounds(mTmpDimBoundsRect);
+
+ if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
+ scheduleAnimation();
+ }
+ }
+ }
+
+ enum Type {
+ /** Can only contain WindowTokens above the APPLICATION_LAYER. */
+ ABOVE_TASKS,
+ /** Can only contain WindowTokens below the APPLICATION_LAYER. */
+ BELOW_TASKS,
+ /** Can contain anything. */
+ ANY;
+
+ static void checkSiblings(Type bottom, Type top) {
+ checkState(!(bottom == ANY && top == ANY), "ANY cannot be a sibling of ANY");
+ checkState(!(bottom != BELOW_TASKS && top == BELOW_TASKS),
+ bottom + " must be above BELOW_TASKS");
+ checkState(!(bottom == ABOVE_TASKS && top != ABOVE_TASKS),
+ top + " must be below ABOVE_TASKS");
+ }
+
+ static void checkChild(Type parent, Type child) {
+ switch (parent) {
+ case ABOVE_TASKS:
+ checkState(child == ABOVE_TASKS, "ABOVE_TASKS can only contain ABOVE_TASKS");
+ break;
+ case BELOW_TASKS:
+ checkState(child == BELOW_TASKS, "BELOW_TASKS can only contain BELOW_TASKS");
+ break;
+ }
+ }
+
+ static Type typeOf(WindowContainer c) {
+ if (c instanceof DisplayArea) {
+ return ((DisplayArea) c).mType;
+ } else if (c instanceof WindowToken && !(c instanceof ActivityRecord)) {
+ return typeOf((WindowToken) c);
+ } else if (c instanceof ActivityStack) {
+ return ANY;
+ } else {
+ throw new IllegalArgumentException("Unknown container: " + c);
+ }
+ }
+
+ private static Type typeOf(WindowToken c) {
+ return c.getWindowLayerFromType() < APPLICATION_LAYER ? BELOW_TASKS : ABOVE_TASKS;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
new file mode 100644
index 0000000..06e7b48
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+
+import com.android.server.wm.DisplayContent.TaskContainers;
+
+/**
+ * Policy that manages DisplayAreas.
+ */
+public abstract class DisplayAreaPolicy {
+ protected final WindowManagerService mWmService;
+ protected final DisplayContent mContent;
+
+ /**
+ * The root DisplayArea. Attach all DisplayAreas to this area (directly or indirectly).
+ */
+ protected final DisplayArea.Root mRoot;
+
+ /**
+ * The IME container. The IME's windows are automatically added to this container.
+ */
+ protected final DisplayArea<? extends WindowContainer> mImeContainer;
+
+ /**
+ * The Tasks container. Tasks etc. are automatically added to this container.
+ */
+ protected final TaskContainers mTaskContainers;
+
+ DisplayAreaPolicy(WindowManagerService wmService,
+ DisplayContent content, DisplayArea.Root root,
+ DisplayArea<? extends WindowContainer> imeContainer, TaskContainers taskContainers) {
+ mWmService = wmService;
+ mContent = content;
+ mRoot = root;
+ mImeContainer = imeContainer;
+ mTaskContainers = taskContainers;
+ }
+
+ /**
+ * Called to ask the policy to set up the DisplayArea hierarchy. At a minimum this must:
+ *
+ * - attach mImeContainer to mRoot (or one of its descendants)
+ * - attach mTaskStacks to mRoot (or one of its descendants)
+ *
+ * Additionally, this is the right place to set up any other DisplayAreas as desired.
+ */
+ public abstract void attachDisplayAreas();
+
+ /**
+ * Called to ask the policy to attach the given WindowToken to the DisplayArea hierarchy.
+ *
+ * This must attach the token to mRoot (or one of its descendants).
+ */
+ public abstract void addWindow(WindowToken token);
+
+ /**
+ * Default policy that has no special features.
+ */
+ public static class Default extends DisplayAreaPolicy {
+
+ public Default(WindowManagerService wmService, DisplayContent content,
+ DisplayArea.Root root,
+ DisplayArea<? extends WindowContainer> imeContainer,
+ TaskContainers taskContainers) {
+ super(wmService, content, root, imeContainer, taskContainers);
+ }
+
+ private final DisplayArea.Tokens mBelow = new DisplayArea.Tokens(mWmService,
+ DisplayArea.Type.BELOW_TASKS, "BelowTasks");
+ private final DisplayArea<DisplayArea> mAbove = new DisplayArea<>(mWmService,
+ DisplayArea.Type.ABOVE_TASKS, "AboveTasks");
+ private final DisplayArea.Tokens mAboveBelowIme = new DisplayArea.Tokens(mWmService,
+ DisplayArea.Type.ABOVE_TASKS, "AboveTasksBelowIme");
+ private final DisplayArea.Tokens mAboveAboveIme = new DisplayArea.Tokens(mWmService,
+ DisplayArea.Type.ABOVE_TASKS, "AboveTasksAboveIme");
+
+ @Override
+ public void attachDisplayAreas() {
+ mRoot.addChild(mBelow, 0);
+ mRoot.addChild(mTaskContainers, 1);
+ mRoot.addChild(mAbove, 2);
+
+ mAbove.addChild(mAboveBelowIme, 0);
+ mAbove.addChild(mImeContainer, 1);
+ mAbove.addChild(mAboveAboveIme, 2);
+ }
+
+ @Override
+ public void addWindow(WindowToken token) {
+ switch (DisplayArea.Type.typeOf(token)) {
+ case ABOVE_TASKS:
+ if (token.getWindowLayerFromType()
+ < mWmService.mPolicy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD)) {
+ mAboveBelowIme.addChild(token);
+ } else {
+ mAboveAboveIme.addChild(token);
+ }
+ break;
+ case BELOW_TASKS:
+ mBelow.addChild(token);
+ break;
+ default:
+ throw new IllegalArgumentException("don't know how to sort " + token);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 9f4cd88..81af0fe 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -80,7 +80,6 @@
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
-import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
@@ -98,9 +97,7 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
-import static com.android.server.wm.DisplayContentProto.ABOVE_APP_WINDOWS;
import static com.android.server.wm.DisplayContentProto.APP_TRANSITION;
-import static com.android.server.wm.DisplayContentProto.BELOW_APP_WINDOWS;
import static com.android.server.wm.DisplayContentProto.CHANGING_APPS;
import static com.android.server.wm.DisplayContentProto.CLOSING_APPS;
import static com.android.server.wm.DisplayContentProto.DISPLAY_FRAMES;
@@ -109,9 +106,9 @@
import static com.android.server.wm.DisplayContentProto.DPI;
import static com.android.server.wm.DisplayContentProto.FOCUSED_APP;
import static com.android.server.wm.DisplayContentProto.ID;
-import static com.android.server.wm.DisplayContentProto.IME_WINDOWS;
import static com.android.server.wm.DisplayContentProto.OPENING_APPS;
import static com.android.server.wm.DisplayContentProto.OVERLAY_WINDOWS;
+import static com.android.server.wm.DisplayContentProto.ROOT_DISPLAY_AREA;
import static com.android.server.wm.DisplayContentProto.ROTATION;
import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
import static com.android.server.wm.DisplayContentProto.STACKS;
@@ -296,14 +293,7 @@
/** The containers below are the only child containers {@link #mWindowContainers} can have. */
// Contains all window containers that are related to apps (Activities)
private final TaskContainers mTaskContainers = new TaskContainers(mWmService);
- // Contains all non-app window containers that should be displayed above the app containers
- // (e.g. Status bar)
- private final AboveAppWindowContainers mAboveAppWindowsContainers =
- new AboveAppWindowContainers("mAboveAppWindowsContainers", mWmService);
- // Contains all non-app window containers that should be displayed below the app containers
- // (e.g. Wallpaper).
- private final NonAppWindowContainers mBelowAppWindowsContainers =
- new NonAppWindowContainers("mBelowAppWindowsContainers", mWmService);
+
// Contains all IME window containers. Note that the z-ordering of the IME windows will depend
// on the IME target. We mainly have this container grouping so we can keep track of all the IME
// window containers together and move them in-sync if/when needed. We use a subclass of
@@ -311,6 +301,11 @@
// TODO(display-area): is "no magnification" in the comment still true?
private final ImeContainer mImeWindowsContainers = new ImeContainer(mWmService);
+ private final DisplayArea.Root mRootDisplayArea = new DisplayArea.Root(mWmService);
+
+ private final DisplayAreaPolicy mDisplayAreaPolicy = new DisplayAreaPolicy.Default(
+ mWmService, this, mRootDisplayArea, mImeWindowsContainers, mTaskContainers);
+
private WindowState mTmpWindow;
private WindowState mTmpWindow2;
private boolean mUpdateImeTarget;
@@ -402,14 +397,6 @@
private int mCurrentOverrideConfigurationChanges;
/**
- * Last orientation forced by the keyguard. It is applied when keyguard is shown and is not
- * occluded.
- *
- * @see NonAppWindowContainers#getOrientation()
- */
- private int mLastKeyguardForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
-
- /**
* The maximum aspect ratio (longerSide/shorterSide) that is treated as close-to-square. The
* orientation requests from apps would be ignored if the display is close-to-square.
*/
@@ -1108,18 +1095,16 @@
// Add non-app token to container hierarchy on the display. App tokens are added through
// the parent container managing them (e.g. Tasks).
switch (token.windowType) {
- case TYPE_WALLPAPER:
- mBelowAppWindowsContainers.addChild(token);
- break;
case TYPE_INPUT_METHOD:
case TYPE_INPUT_METHOD_DIALOG:
mImeWindowsContainers.addChild(token);
break;
case TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY:
+ // TODO(display-area): Migrate to DisplayArea
mOverlayContainers.addChild(token);
break;
default:
- mAboveAppWindowsContainers.addChild(token);
+ mDisplayAreaPolicy.addWindow(token);
break;
}
}
@@ -2133,13 +2118,7 @@
return getLastOrientation();
}
}
- final int orientation = mAboveAppWindowsContainers.getOrientation();
- if (orientation != SCREEN_ORIENTATION_UNSET) {
- return orientation;
- }
-
- // Top system windows are not requesting an orientation. Start searching from apps.
- return mTaskContainers.getOrientation();
+ return mRootDisplayArea.getOrientation();
}
void updateDisplayInfo() {
@@ -2786,23 +2765,12 @@
final long token = proto.start(fieldId);
super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
proto.write(ID, mDisplayId);
+ mRootDisplayArea.dumpDebug(proto, ROOT_DISPLAY_AREA, logLevel);
for (int stackNdx = mTaskContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = mTaskContainers.getChildAt(stackNdx);
stack.dumpDebugInnerStackOnly(proto, STACKS, logLevel);
}
mDividerControllerLocked.dumpDebug(proto, DOCKED_STACK_DIVIDER_CONTROLLER);
- for (int i = mAboveAppWindowsContainers.getChildCount() - 1; i >= 0; --i) {
- final WindowToken windowToken = mAboveAppWindowsContainers.getChildAt(i);
- windowToken.dumpDebug(proto, ABOVE_APP_WINDOWS, logLevel);
- }
- for (int i = mBelowAppWindowsContainers.getChildCount() - 1; i >= 0; --i) {
- final WindowToken windowToken = mBelowAppWindowsContainers.getChildAt(i);
- windowToken.dumpDebug(proto, BELOW_APP_WINDOWS, logLevel);
- }
- for (int i = mImeWindowsContainers.getChildCount() - 1; i >= 0; --i) {
- final WindowToken windowToken = mImeWindowsContainers.getChildAt(i);
- windowToken.dumpDebug(proto, IME_WINDOWS, logLevel);
- }
for (int i = mOverlayContainers.getChildCount() - 1; i >= 0; --i) {
final WindowToken windowToken = mOverlayContainers.getChildAt(i);
windowToken.dumpDebug(proto, OVERLAY_WINDOWS, logLevel);
@@ -4216,7 +4184,7 @@
* Window container class that contains all containers on this display relating to Apps.
* I.e Activities.
*/
- private final class TaskContainers extends DisplayChildWindowContainer<ActivityStack> {
+ final class TaskContainers extends DisplayArea<ActivityStack> {
/**
* A control placed at the appropriate level for transitions to occur.
*/
@@ -4226,16 +4194,15 @@
/**
* Given that the split-screen divider does not have an AppWindowToken, it
- * will have to live inside of a "NonAppWindowContainer", in particular
- * {@link DisplayContent#mAboveAppWindowsContainers}. However, in visual Z order
+ * will have to live inside of a "NonAppWindowContainer". However, in visual Z order
* it will need to be interleaved with some of our children, appearing on top of
* both docked stacks but underneath any assistant stacks.
*
* To solve this problem we have this anchor control, which will always exist so
* we can always assign it the correct value in our {@link #assignChildLayers}.
- * Likewise since it always exists, {@link AboveAppWindowContainers} can always
+ * Likewise since it always exists, we can always
* assign the divider a layer relative to it. This way we prevent linking lifecycle
- * events between the two containers.
+ * events between tasks and the divider window.
*/
SurfaceControl mSplitScreenDividerAnchor = null;
@@ -4246,7 +4213,7 @@
private ActivityStack mRootSplitScreenPrimaryTask = null;
TaskContainers(WindowManagerService service) {
- super(service);
+ super(service, Type.ANY, "TaskContainers");
}
/**
@@ -4763,32 +4730,6 @@
}
}
- private final class AboveAppWindowContainers extends NonAppWindowContainers {
- AboveAppWindowContainers(String name, WindowManagerService service) {
- super(name, service);
- }
-
- @Override
- void assignChildLayers(SurfaceControl.Transaction t) {
- boolean needAssignIme = mImeWindowsContainers.getSurfaceControl() != null;
- for (int j = 0; j < mChildren.size(); ++j) {
- final WindowToken wt = mChildren.get(j);
-
- wt.assignLayer(t, j);
- wt.assignChildLayers(t);
-
- int layer = mWmService.mPolicy.getWindowLayerFromTypeLw(
- wt.windowType, wt.mOwnerCanManageAppTokens);
-
- if (needAssignIme && layer >= mWmService.mPolicy.getWindowLayerFromTypeLw(
- TYPE_INPUT_METHOD_DIALOG, true)) {
- mImeWindowsContainers.assignRelativeLayer(t, wt.getSurfaceControl(), -1);
- needAssignIme = false;
- }
- }
- }
- }
-
private class WindowContainers extends DisplayChildWindowContainer<WindowContainer> {
private final String mName;
@@ -4800,13 +4741,10 @@
@Override
void assignChildLayers(SurfaceControl.Transaction t) {
mImeWindowsContainers.setNeedsLayer();
- mBelowAppWindowsContainers.assignLayer(t, 0);
- mTaskContainers.assignLayer(t, 1);
- mAboveAppWindowsContainers.assignLayer(t, 2);
+
+ mRootDisplayArea.assignLayer(t, 0);
final WindowState imeTarget = mInputMethodTarget;
- boolean needAssignIme = true;
-
// In the case where we have an IME target that is not in split-screen mode IME
// assignment is easy. We just need the IME to go directly above the target. This way
// children of the target will naturally go above the IME and everyone is happy.
@@ -4839,11 +4777,7 @@
// Above we have assigned layers to our children, now we ask them to assign
// layers to their children.
- mBelowAppWindowsContainers.assignChildLayers(t);
- mTaskContainers.assignChildLayers(t);
- mAboveAppWindowsContainers.assignChildLayers(t);
- mImeWindowsContainers.assignRelativeLayer(t, getSurfaceControl(), Integer.MAX_VALUE);
- mImeWindowsContainers.assignChildLayers(t);
+ mRootDisplayArea.assignChildLayers(t);
}
@Override
@@ -4852,10 +4786,8 @@
}
void addChildren() {
- addChild(mBelowAppWindowsContainers, null);
- addChild(mTaskContainers, null);
- addChild(mAboveAppWindowsContainers, null);
- addChild(mImeWindowsContainers, null);
+ addChild(mRootDisplayArea, 0);
+ mDisplayAreaPolicy.attachDisplayAreas();
}
@Override
@@ -4882,32 +4814,6 @@
< mWmService.mPolicy.getWindowLayerFromTypeLw(token2.windowType,
token2.mOwnerCanManageAppTokens) ? -1 : 1;
- private final Predicate<WindowState> mGetOrientingWindow = w -> {
- final WindowManagerPolicy policy = mWmService.mPolicy;
- if (policy.isKeyguardHostWindow(w.mAttrs)) {
- if (mWmService.mKeyguardGoingAway) {
- return false;
- }
- // Consider unoccluding only when all unknown visibilities have been
- // resolved, as otherwise we just may be starting another occluding activity.
- final boolean isUnoccluding =
- mDisplayContent.mAppTransition.getAppTransition()
- == TRANSIT_KEYGUARD_UNOCCLUDE
- && mDisplayContent.mUnknownAppVisibilityController.allResolved();
- // If keyguard is showing, or we're unoccluding, force the keyguard's orientation,
- // even if SystemUI hasn't updated the attrs yet.
- if (policy.isKeyguardShowingAndNotOccluded() || isUnoccluding) {
- return true;
- }
- }
- final int req = w.mAttrs.screenOrientation;
- if (req == SCREEN_ORIENTATION_UNSPECIFIED || req == SCREEN_ORIENTATION_BEHIND
- || req == SCREEN_ORIENTATION_UNSET) {
- return false;
- }
- return true;
- };
-
private final String mName;
private final Dimmer mDimmer = new Dimmer(this);
private final Rect mTmpDimBoundsRect = new Rect();
@@ -4929,26 +4835,9 @@
@Override
int getOrientation(int candidate) {
- // Find a window requesting orientation.
- final WindowState win = getWindow(mGetOrientingWindow);
-
- if (win != null) {
- int req = win.mAttrs.screenOrientation;
- ProtoLog.v(WM_DEBUG_ORIENTATION,
- "%s forcing orientation to %d for display id=%d", win, req,
- mDisplayId);
- if (mWmService.mPolicy.isKeyguardHostWindow(win.mAttrs)) {
- // SystemUI controls the Keyguard orientation asynchronously, and mAttrs may be
- // stale. We record / use the last known override.
- if (req != SCREEN_ORIENTATION_UNSET && req != SCREEN_ORIENTATION_UNSPECIFIED) {
- mDisplayContent.mLastKeyguardForcedOrientation = req;
- } else {
- req = mDisplayContent.mLastKeyguardForcedOrientation;
- }
- }
- return req;
- }
- return candidate;
+ ProtoLog.w(WM_DEBUG_ORIENTATION, "NonAppWindowContainer cannot set orientation: %s",
+ this);
+ return SCREEN_ORIENTATION_UNSET;
}
@Override
@@ -4983,11 +4872,11 @@
* - the container doesn't always participate in window traversal, according to
* {@link #skipImeWindowsDuringTraversal()}
*/
- private class ImeContainer extends NonAppWindowContainers {
+ private static class ImeContainer extends DisplayArea.Tokens {
boolean mNeedsLayer = false;
ImeContainer(WindowManagerService wms) {
- super("ImeContainer", wms);
+ super(wms, Type.ABOVE_TASKS, "ImeContainer");
}
public void setNeedsLayer() {
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 2d4211a..d0179ad 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -20,17 +20,31 @@
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_HIDDEN;
+import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
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.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
import android.annotation.Nullable;
import android.app.StatusBarManager;
import android.util.IntArray;
+import android.util.SparseArray;
+import android.view.InsetsAnimationControlCallbacks;
+import android.view.InsetsAnimationControlImpl;
+import android.view.InsetsController;
+import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.InsetsState.InternalInsetsType;
+import android.view.SurfaceControl;
+import android.view.SyncRtSurfaceTransactionApplier;
import android.view.ViewRootImpl;
+import android.view.WindowInsetsAnimationCallback;
+import android.view.WindowInsetsAnimationControlListener;
+
+import com.android.server.DisplayThread;
/**
* Policy that implements who gets control over the windows generating insets.
@@ -46,6 +60,8 @@
private WindowState mFocusedWin;
private BarWindow mStatusBar = new BarWindow(StatusBarManager.WINDOW_STATUS_BAR);
private BarWindow mNavBar = new BarWindow(StatusBarManager.WINDOW_NAVIGATION_BAR);
+ private boolean mAnimatingShown;
+ private final float[] mTmpFloat9 = new float[9];
InsetsPolicy(InsetsStateController stateController, DisplayContent displayContent) {
mStateController = stateController;
@@ -91,11 +107,14 @@
changed = true;
}
if (changed) {
- updateBarControlTarget(mFocusedWin);
- mPolicy.getStatusBarManagerInternal().showTransient(mDisplayContent.getDisplayId(),
- mShowingTransientTypes.toArray());
- mStateController.notifyInsetsChanged();
- // TODO(b/118118435): Animation
+ startAnimation(mShowingTransientTypes, true, () -> {
+ synchronized (mDisplayContent.mWmService.mGlobalLock) {
+ mPolicy.getStatusBarManagerInternal().showTransient(
+ mDisplayContent.getDisplayId(),
+ mShowingTransientTypes.toArray());
+ mStateController.notifyInsetsChanged();
+ }
+ });
}
}
@@ -103,11 +122,13 @@
if (mShowingTransientTypes.size() == 0) {
return;
}
-
- // TODO(b/118118435): Animation
- mShowingTransientTypes.clear();
- updateBarControlTarget(mFocusedWin);
- mStateController.notifyInsetsChanged();
+ startAnimation(mShowingTransientTypes, false, () -> {
+ synchronized (mDisplayContent.mWmService.mGlobalLock) {
+ mShowingTransientTypes.clear();
+ mStateController.notifyInsetsChanged();
+ updateBarControlTarget(mFocusedWin);
+ }
+ });
}
boolean isTransient(@InternalInsetsType int type) {
@@ -247,6 +268,29 @@
return isDockedStackVisible || isFreeformStackVisible || isResizing;
}
+ private void startAnimation(IntArray internalTypes, boolean show, Runnable callback) {
+ int typesReady = 0;
+ final SparseArray<InsetsSourceControl> controls = new SparseArray<>();
+ updateBarControlTarget(mFocusedWin);
+ for (int i = internalTypes.size() - 1; i >= 0; i--) {
+ InsetsSourceProvider provider =
+ mStateController.getSourceProvider(internalTypes.get(i));
+ if (provider == null) continue;
+ InsetsSourceControl control = provider.getControl(provider.getControlTarget());
+ if (control == null || control.getLeash() == null) continue;
+ typesReady |= InsetsState.toPublicType(internalTypes.get(i));
+ controls.put(control.getType(), control);
+ }
+ controlAnimationUnchecked(typesReady, controls, show, callback);
+ }
+
+ private void controlAnimationUnchecked(int typesReady,
+ SparseArray<InsetsSourceControl> controls, boolean show, Runnable callback) {
+ InsetsPolicyAnimationControlListener listener =
+ new InsetsPolicyAnimationControlListener(show, callback);
+ listener.mControlCallbacks.controlAnimationUnchecked(typesReady, controls, show);
+ }
+
private class BarWindow {
private final int mId;
@@ -267,7 +311,103 @@
}
}
- // TODO(b/118118435): Implement animations for it (with SurfaceAnimator)
+ private class InsetsPolicyAnimationControlListener extends
+ InsetsController.InternalAnimationControlListener {
+ Runnable mFinishCallback;
+ InsetsPolicyAnimationControlCallbacks mControlCallbacks;
+
+ InsetsPolicyAnimationControlListener(boolean show, Runnable finishCallback) {
+ super(show);
+ mFinishCallback = finishCallback;
+ mControlCallbacks = new InsetsPolicyAnimationControlCallbacks(this);
+ }
+
+ @Override
+ protected void onAnimationFinish() {
+ super.onAnimationFinish();
+ mControlCallbacks.mAnimationControl.finish(mAnimatingShown);
+ DisplayThread.getHandler().post(mFinishCallback);
+ }
+
+ private class InsetsPolicyAnimationControlCallbacks implements
+ InsetsAnimationControlCallbacks {
+ private InsetsAnimationControlImpl mAnimationControl = null;
+ private InsetsPolicyAnimationControlListener mListener;
+
+ InsetsPolicyAnimationControlCallbacks(InsetsPolicyAnimationControlListener listener) {
+ super();
+ mListener = listener;
+ }
+
+ private void controlAnimationUnchecked(int typesReady,
+ SparseArray<InsetsSourceControl> controls, boolean show) {
+ if (typesReady == 0) {
+ // nothing to animate.
+ return;
+ }
+ mAnimatingShown = show;
+
+ mAnimationControl = new InsetsAnimationControlImpl(controls,
+ mFocusedWin.getDisplayContent().getBounds(), getState(),
+ mListener, typesReady, this, mListener.getDurationMs(),
+ InsetsController.INTERPOLATOR, true,
+ show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN
+ : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN);
+ }
+
+ /** Called on SurfaceAnimationThread lock without global WM lock held. */
+ @Override
+ public void scheduleApplyChangeInsets() {
+ InsetsState state = getState();
+ if (mAnimationControl.applyChangeInsets(state)) {
+ mAnimationControl.finish(mAnimatingShown);
+ }
+ }
+
+ @Override
+ public void notifyFinished(InsetsAnimationControlImpl controller, boolean shown) {
+ // Nothing's needed here. Finish steps is handled in the listener
+ // onAnimationFinished callback.
+ }
+
+ /**
+ * This method will return a state with fullscreen frame override. No need to make copy
+ * after getting state from this method.
+ * @return The client insets state with full display frame override.
+ */
+ private InsetsState getState() {
+ // To animate the transient animation correctly, we need to let the state hold
+ // the full display frame.
+ InsetsState overrideState = new InsetsState(mFocusedWin.getRequestedInsetsState(),
+ true);
+ overrideState.setDisplayFrame(mFocusedWin.getDisplayContent().getBounds());
+ return overrideState;
+ }
+
+ /** Called on SurfaceAnimationThread lock without global WM lock held. */
+ @Override
+ public void applySurfaceParams(
+ final SyncRtSurfaceTransactionApplier.SurfaceParams... params) {
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ for (int i = params.length - 1; i >= 0; i--) {
+ SyncRtSurfaceTransactionApplier.SurfaceParams surfaceParams = params[i];
+ applyParams(t, surfaceParams, mTmpFloat9);
+ }
+ t.apply();
+ }
+
+ /** Called on SurfaceAnimationThread lock without global WM lock held. */
+ @Override
+ public void startAnimation(InsetsAnimationControlImpl controller,
+ WindowInsetsAnimationControlListener listener, int types,
+ WindowInsetsAnimationCallback.InsetsAnimation animation,
+ WindowInsetsAnimationCallback.AnimationBounds bounds,
+ int layoutDuringAnimation) {
+ SurfaceAnimationThread.getHandler().post(() -> listener.onReady(controller, types));
+ }
+ }
+ }
+
private class TransientControlTarget implements InsetsControlTarget {
@Override
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index db43480..e6c1969 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -24,6 +24,7 @@
import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
import static android.view.ViewRootImpl.sNewInsetsMode;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_INSETS_CONTROL;
import static com.android.server.wm.WindowManagerService.H.LAYOUT_AND_ASSIGN_WINDOW_LAYERS_IF_NEEDED;
import android.annotation.NonNull;
@@ -34,11 +35,11 @@
import android.view.InsetsSource;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
-import android.view.InsetsState.InternalInsetsType;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import com.android.internal.util.function.TriConsumer;
+import com.android.server.wm.SurfaceAnimator.AnimationType;
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
import java.io.PrintWriter;
@@ -243,7 +244,8 @@
mAdapter = new ControlAdapter();
setClientVisible(InsetsState.getDefaultVisibility(mSource.getType()));
final Transaction t = mDisplayContent.getPendingTransaction();
- mWin.startAnimation(t, mAdapter, !mClientVisible /* hidden */);
+ mWin.startAnimation(t, mAdapter, !mClientVisible /* hidden */,
+ ANIMATION_TYPE_INSETS_CONTROL, null /* animationFinishedCallback */);
final SurfaceControl leash = mAdapter.mCapturedLeash;
final long frameNumber = mFinishSeamlessRotateFrameNumber;
mFinishSeamlessRotateFrameNumber = -1;
@@ -348,7 +350,7 @@
@Override
public void startAnimation(SurfaceControl animationLeash, Transaction t,
- OnAnimationFinishedCallback finishCallback) {
+ @AnimationType int type, OnAnimationFinishedCallback finishCallback) {
// TODO(b/118118435): We can remove the type check when implementing the transient bar
// animation.
if (mSource.getType() == ITYPE_IME) {
diff --git a/services/core/java/com/android/server/wm/LocalAnimationAdapter.java b/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
index 5892239..7c1a616 100644
--- a/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
@@ -24,6 +24,7 @@
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
+import com.android.server.wm.SurfaceAnimator.AnimationType;
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
import java.io.PrintWriter;
@@ -50,9 +51,9 @@
@Override
public void startAnimation(SurfaceControl animationLeash, Transaction t,
- OnAnimationFinishedCallback finishCallback) {
+ @AnimationType int type, OnAnimationFinishedCallback finishCallback) {
mAnimator.startAnimation(mSpec, animationLeash, t,
- () -> finishCallback.onAnimationFinished(this));
+ () -> finishCallback.onAnimationFinished(type, this));
}
@Override
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index da9d074..944e0ae 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -30,6 +30,7 @@
import static com.android.server.wm.BoundsAnimationController.FADE_IN;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static com.android.server.wm.WindowManagerInternal.AppTransitionListener;
import android.annotation.IntDef;
@@ -61,6 +62,7 @@
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.protolog.common.ProtoLog;
import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.wm.SurfaceAnimator.AnimationType;
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
import com.android.server.wm.utils.InsetUtils;
@@ -432,7 +434,8 @@
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "addAnimation(%s)", task.getName());
final TaskAnimationAdapter taskAdapter = new TaskAnimationAdapter(task,
isRecentTaskInvisible);
- task.startAnimation(task.getPendingTransaction(), taskAdapter, false /* hidden */);
+ task.startAnimation(task.getPendingTransaction(), taskAdapter, false /* hidden */,
+ ANIMATION_TYPE_RECENTS);
task.commitPendingTransaction();
mPendingAnimations.add(taskAdapter);
return taskAdapter;
@@ -443,14 +446,16 @@
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
"removeAnimation(%d)", taskAdapter.mTask.mTaskId);
taskAdapter.mTask.setCanAffectSystemUiFlags(true);
- taskAdapter.mCapturedFinishCallback.onAnimationFinished(taskAdapter);
+ taskAdapter.mCapturedFinishCallback.onAnimationFinished(taskAdapter.mLastAnimationType,
+ taskAdapter);
mPendingAnimations.remove(taskAdapter);
}
@VisibleForTesting
void removeWallpaperAnimation(WallpaperAnimationAdapter wallpaperAdapter) {
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "removeWallpaperAnimation()");
- wallpaperAdapter.getLeashFinishedCallback().onAnimationFinished(wallpaperAdapter);
+ wallpaperAdapter.getLeashFinishedCallback().onAnimationFinished(
+ wallpaperAdapter.getLastAnimationType(), wallpaperAdapter);
mPendingWallpaperAnimations.remove(wallpaperAdapter);
}
@@ -638,7 +643,7 @@
taskSnapshot.getColorSpace(), false /* containsSecureLayers */));
mRecentScreenshotAnimator = new SurfaceAnimator(
animatable,
- () -> {
+ (type, anim) -> {
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "mRecentScreenshotAnimator finish");
mCallbacks.onAnimationFinished(reorderMode, false /* sendUserLeaveHint */);
}, mService);
@@ -827,6 +832,7 @@
private final Task mTask;
private SurfaceControl mCapturedLeash;
private OnAnimationFinishedCallback mCapturedFinishCallback;
+ private @AnimationType int mLastAnimationType;
private final boolean mIsRecentTaskInvisible;
private RemoteAnimationTarget mTarget;
private final Point mPosition = new Point();
@@ -868,7 +874,7 @@
@Override
public void startAnimation(SurfaceControl animationLeash, Transaction t,
- OnAnimationFinishedCallback finishCallback) {
+ @AnimationType int type, OnAnimationFinishedCallback finishCallback) {
// Restore z-layering, position and stack crop until client has a chance to modify it.
t.setLayer(animationLeash, mTask.getPrefixOrderIndex());
t.setPosition(animationLeash, mPosition.x, mPosition.y);
@@ -877,6 +883,7 @@
t.setWindowCrop(animationLeash, mTmpRect);
mCapturedLeash = animationLeash;
mCapturedFinishCallback = finishCallback;
+ mLastAnimationType = type;
}
@Override
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 6f7eeab..d2dbab8 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -40,6 +40,7 @@
import com.android.internal.util.FastPrintWriter;
import com.android.server.protolog.ProtoLogImpl;
import com.android.server.protolog.common.ProtoLog;
+import com.android.server.wm.SurfaceAnimator.AnimationType;
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
import java.io.PrintWriter;
@@ -180,12 +181,14 @@
if (wrappers.mAdapter != null
&& wrappers.mAdapter.mCapturedFinishCallback != null) {
wrappers.mAdapter.mCapturedFinishCallback
- .onAnimationFinished(wrappers.mAdapter);
+ .onAnimationFinished(wrappers.mAdapter.mAnimationType,
+ wrappers.mAdapter);
}
if (wrappers.mThumbnailAdapter != null
&& wrappers.mThumbnailAdapter.mCapturedFinishCallback != null) {
wrappers.mThumbnailAdapter.mCapturedFinishCallback
- .onAnimationFinished(wrappers.mThumbnailAdapter);
+ .onAnimationFinished(wrappers.mAdapter.mAnimationType,
+ wrappers.mThumbnailAdapter);
}
mPendingAnimations.remove(i);
}
@@ -221,11 +224,13 @@
final RemoteAnimationRecord adapters = mPendingAnimations.get(i);
if (adapters.mAdapter != null) {
adapters.mAdapter.mCapturedFinishCallback
- .onAnimationFinished(adapters.mAdapter);
+ .onAnimationFinished(adapters.mAdapter.mAnimationType,
+ adapters.mAdapter);
}
if (adapters.mThumbnailAdapter != null) {
adapters.mThumbnailAdapter.mCapturedFinishCallback
- .onAnimationFinished(adapters.mThumbnailAdapter);
+ .onAnimationFinished(adapters.mAdapter.mAnimationType,
+ adapters.mThumbnailAdapter);
}
mPendingAnimations.remove(i);
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tcontainer=%s",
@@ -234,7 +239,8 @@
for (int i = mPendingWallpaperAnimations.size() - 1; i >= 0; i--) {
final WallpaperAnimationAdapter adapter = mPendingWallpaperAnimations.get(i);
- adapter.getLeashFinishedCallback().onAnimationFinished(adapter);
+ adapter.getLeashFinishedCallback().onAnimationFinished(
+ adapter.getLastAnimationType(), adapter);
mPendingWallpaperAnimations.remove(i);
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\twallpaper=%s", adapter.getToken());
}
@@ -393,6 +399,7 @@
private final RemoteAnimationRecord mRecord;
SurfaceControl mCapturedLeash;
private OnAnimationFinishedCallback mCapturedFinishCallback;
+ private @AnimationType int mAnimationType;
final Point mPosition = new Point();
final Rect mStackBounds = new Rect();
@@ -410,7 +417,7 @@
@Override
public void startAnimation(SurfaceControl animationLeash, Transaction t,
- OnAnimationFinishedCallback finishCallback) {
+ @AnimationType int type, OnAnimationFinishedCallback finishCallback) {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation");
// Restore z-layering, position and stack crop until client has a chance to modify it.
@@ -425,6 +432,7 @@
}
mCapturedLeash = animationLeash;
mCapturedFinishCallback = finishCallback;
+ mAnimationType = type;
}
@Override
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index e7aca89..f6cdac5 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -24,6 +24,7 @@
import static com.android.server.wm.RotationAnimationSpecProto.START_LUMA;
import static com.android.server.wm.ScreenRotationAnimationProto.ANIMATION_RUNNING;
import static com.android.server.wm.ScreenRotationAnimationProto.STARTED;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_SCREEN_ROTATION;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
@@ -48,6 +49,8 @@
import com.android.internal.R;
import com.android.server.protolog.common.ProtoLog;
+import com.android.server.wm.SurfaceAnimator.AnimationType;
+import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
import com.android.server.wm.utils.RotationAnimationUtils;
import java.io.PrintWriter;
@@ -670,33 +673,35 @@
* Start an animation defined by animationSpec on a new {@link SurfaceAnimator}.
*
* @param animatable The animatable used for the animation.
- * @param animationSpec The spec of the animation.
- * @param animationFinishedCallback Callback passed to the {@link SurfaceAnimator} and
- * called when the animation finishes.
+ * @param animationSpec The spec of the animation.
+ * @param animationFinishedCallback Callback passed to the {@link SurfaceAnimator}
+ * and called when the animation finishes.
* @return The newly created {@link SurfaceAnimator} that as been started.
*/
private SurfaceAnimator startAnimation(
SurfaceAnimator.Animatable animatable,
LocalAnimationAdapter.AnimationSpec animationSpec,
- Runnable animationFinishedCallback) {
+ OnAnimationFinishedCallback animationFinishedCallback) {
SurfaceAnimator animator = new SurfaceAnimator(
animatable, animationFinishedCallback, mService);
LocalAnimationAdapter localAnimationAdapter = new LocalAnimationAdapter(
animationSpec, mService.mSurfaceAnimationRunner);
animator.startAnimation(mDisplayContent.getPendingTransaction(),
- localAnimationAdapter, false);
+ localAnimationAdapter, false, ANIMATION_TYPE_SCREEN_ROTATION);
return animator;
}
- private void onAnimationEnd() {
+ private void onAnimationEnd(@AnimationType int type, AnimationAdapter anim) {
synchronized (mService.mGlobalLock) {
if (isAnimating()) {
ProtoLog.v(WM_DEBUG_ORIENTATION,
- "ScreenRotation sill animating: mDisplayAnimator: %s\n"
- + "mEnterBlackFrameAnimator: "
- + "%s\nmRotateScreenAnimator: %s\n"
+ "ScreenRotation still animating: type: %d\n"
+ + "mDisplayAnimator: %s\n"
+ + "mEnterBlackFrameAnimator: %s\n"
+ + "mRotateScreenAnimator: %s\n"
+ "mScreenshotRotationAnimator: %s",
+ type,
mDisplayAnimator != null
? mDisplayAnimator.isAnimating() : null,
mEnterBlackFrameAnimator != null
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index de7f9e4..ab1f34a 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -238,16 +238,12 @@
@Override
public void setInTouchMode(boolean mode) {
- synchronized (mService.mGlobalLock) {
- mService.mInTouchMode = mode;
- }
+ mService.setInTouchMode(mode);
}
@Override
public boolean getInTouchMode() {
- synchronized (mService.mGlobalLock) {
- return mService.mInTouchMode;
- }
+ return mService.getInTouchMode();
}
@Override
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index cb1676f..7164cd8 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -23,6 +23,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.Slog;
@@ -33,6 +34,8 @@
import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
* A class that can run animations on objects that have a set of child surfaces. We do this by
@@ -47,6 +50,7 @@
private static final String TAG = TAG_WITH_CLASS_NAME ? "SurfaceAnimator" : TAG_WM;
private final WindowManagerService mService;
private AnimationAdapter mAnimation;
+ private @AnimationType int mAnimationType;
@VisibleForTesting
SurfaceControl mLeash;
@@ -55,30 +59,32 @@
private final OnAnimationFinishedCallback mInnerAnimationFinishedCallback;
@VisibleForTesting
@Nullable
- final Runnable mStaticAnimationFinishedCallback;
+ final OnAnimationFinishedCallback mStaticAnimationFinishedCallback;
@Nullable
- private Runnable mAnimationFinishedCallback;
+ private OnAnimationFinishedCallback mAnimationFinishedCallback;
private boolean mAnimationStartDelayed;
/**
* @param animatable The object to animate.
- * @param animationFinishedCallback Callback to invoke when an animation has finished running.
+ * @param staticAnimationFinishedCallback Callback to invoke when an animation has finished
+ * running.
*/
- SurfaceAnimator(Animatable animatable, @Nullable Runnable animationFinishedCallback,
+ SurfaceAnimator(Animatable animatable,
+ @Nullable OnAnimationFinishedCallback staticAnimationFinishedCallback,
WindowManagerService service) {
mAnimatable = animatable;
mService = service;
- mStaticAnimationFinishedCallback = animationFinishedCallback;
- mInnerAnimationFinishedCallback = getFinishedCallback(animationFinishedCallback);
+ mStaticAnimationFinishedCallback = staticAnimationFinishedCallback;
+ mInnerAnimationFinishedCallback = getFinishedCallback(staticAnimationFinishedCallback);
}
private OnAnimationFinishedCallback getFinishedCallback(
- @Nullable Runnable animationFinishedCallback) {
- return anim -> {
+ @Nullable OnAnimationFinishedCallback staticAnimationFinishedCallback) {
+ return (type, anim) -> {
synchronized (mService.mGlobalLock) {
final SurfaceAnimator target = mService.mAnimationTransferMap.remove(anim);
if (target != null) {
- target.mInnerAnimationFinishedCallback.onAnimationFinished(anim);
+ target.mInnerAnimationFinishedCallback.onAnimationFinished(type, anim);
return;
}
@@ -91,13 +97,14 @@
if (anim != mAnimation) {
return;
}
- final Runnable animationFinishCallback = mAnimationFinishedCallback;
+ final OnAnimationFinishedCallback animationFinishCallback =
+ mAnimationFinishedCallback;
reset(mAnimatable.getPendingTransaction(), true /* destroyLeash */);
- if (animationFinishedCallback != null) {
- animationFinishedCallback.run();
+ if (staticAnimationFinishedCallback != null) {
+ staticAnimationFinishedCallback.onAnimationFinished(type, anim);
}
if (animationFinishCallback != null) {
- animationFinishCallback.run();
+ animationFinishCallback.onAnimationFinished(type, anim);
}
};
if (!mAnimatable.shouldDeferAnimationFinish(resetAndInvokeFinish)) {
@@ -120,9 +127,11 @@
* @param animationFinishedCallback The callback being triggered when the animation finishes.
*/
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
- @Nullable Runnable animationFinishedCallback) {
+ @AnimationType int type,
+ @Nullable OnAnimationFinishedCallback animationFinishedCallback) {
cancelAnimation(t, true /* restarting */, true /* forwardCancel */);
mAnimation = anim;
+ mAnimationType = type;
mAnimationFinishedCallback = animationFinishedCallback;
final SurfaceControl surface = mAnimatable.getSurfaceControl();
if (surface == null) {
@@ -137,11 +146,12 @@
if (DEBUG_ANIM) Slog.i(TAG, "Animation start delayed");
return;
}
- mAnimation.startAnimation(mLeash, t, mInnerAnimationFinishedCallback);
+ mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);
}
- void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden) {
- startAnimation(t, anim, hidden, null /* animationFinishedCallback */);
+ void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
+ @AnimationType int type) {
+ startAnimation(t, anim, hidden, type, null /* animationFinishedCallback */);
}
/**
@@ -165,7 +175,7 @@
mAnimationStartDelayed = false;
if (delayed && mAnimation != null) {
mAnimation.startAnimation(mLeash, mAnimatable.getPendingTransaction(),
- mInnerAnimationFinishedCallback);
+ mAnimationType, mInnerAnimationFinishedCallback);
mAnimatable.commitPendingTransaction();
}
}
@@ -245,6 +255,7 @@
cancelAnimation(t, true /* restarting */, true /* forwardCancel */);
mLeash = from.mLeash;
mAnimation = from.mAnimation;
+ mAnimationType = from.mAnimationType;
mAnimationFinishedCallback = from.mAnimationFinishedCallback;
// Cancel source animation, but don't let animation runner cancel the animation.
@@ -272,7 +283,8 @@
if (DEBUG_ANIM) Slog.i(TAG, "Cancelling animation restarting=" + restarting);
final SurfaceControl leash = mLeash;
final AnimationAdapter animation = mAnimation;
- final Runnable animationFinishedCallback = mAnimationFinishedCallback;
+ final @AnimationType int animationType = mAnimationType;
+ final OnAnimationFinishedCallback animationFinishedCallback = mAnimationFinishedCallback;
reset(t, false);
if (animation != null) {
if (!mAnimationStartDelayed && forwardCancel) {
@@ -280,10 +292,10 @@
}
if (!restarting) {
if (mStaticAnimationFinishedCallback != null) {
- mStaticAnimationFinishedCallback.run();
+ mStaticAnimationFinishedCallback.onAnimationFinished(animationType, animation);
}
if (animationFinishedCallback != null) {
- animationFinishedCallback.run();
+ animationFinishedCallback.onAnimationFinished(animationType, animation);
}
}
}
@@ -325,6 +337,7 @@
mLeash = null;
mAnimation = null;
mAnimationFinishedCallback = null;
+ mAnimationType = ANIMATION_TYPE_NONE;
if (reparent) {
// Make sure to inform the animatable after the surface was reparented (or reparent
@@ -392,12 +405,72 @@
}
}
+
+ /**
+ * No animation is specified.
+ * @hide
+ */
+ static final int ANIMATION_TYPE_NONE = 0;
+
+ /**
+ * Animation for an app transition.
+ * @hide
+ */
+ static final int ANIMATION_TYPE_APP_TRANSITION = 1;
+
+ /**
+ * Animation for screen rotation.
+ * @hide
+ */
+ static final int ANIMATION_TYPE_SCREEN_ROTATION = 2;
+
+ /**
+ * Animation for dimming.
+ * @hide
+ */
+ static final int ANIMATION_TYPE_DIMMER = 3;
+
+ /**
+ * Animation for recent apps.
+ * @hide
+ */
+ static final int ANIMATION_TYPE_RECENTS = 4;
+
+ /**
+ * Animation for a {@link WindowState} without animating the activity.
+ * @hide
+ */
+ static final int ANIMATION_TYPE_WINDOW_ANIMATION = 5;
+
+ /**
+ * Animation to control insets. This is actually not an animation, but is used to give the
+ * client a leash over the system window causing insets.
+ * @hide
+ */
+ static final int ANIMATION_TYPE_INSETS_CONTROL = 6;
+
+ /**
+ * The type of the animation.
+ * @hide
+ */
+ @IntDef(flag = true, prefix = { "ANIMATION_TYPE_" }, value = {
+ ANIMATION_TYPE_NONE,
+ ANIMATION_TYPE_APP_TRANSITION,
+ ANIMATION_TYPE_SCREEN_ROTATION,
+ ANIMATION_TYPE_DIMMER,
+ ANIMATION_TYPE_RECENTS,
+ ANIMATION_TYPE_WINDOW_ANIMATION,
+ ANIMATION_TYPE_INSETS_CONTROL
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface AnimationType {}
+
/**
* Callback to be passed into {@link AnimationAdapter#startAnimation} to be invoked by the
* component that is running the animation when the animation is finished.
*/
interface OnAnimationFinishedCallback {
- void onAnimationFinished(AnimationAdapter anim);
+ void onAnimationFinished(@AnimationType int type, AnimationAdapter anim);
}
/**
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 271f559..28dc2a4 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3215,7 +3215,9 @@
info.displayId = getDisplayId();
info.isRunning = getTopNonFinishingActivity() != null;
final Intent baseIntent = getBaseIntent();
- info.baseIntent = baseIntent == null ? new Intent() : baseIntent;
+ // Make a copy of base intent because this is like a snapshot info.
+ // Besides, {@link RecentTasks#getRecentTasksImpl} may modify it.
+ info.baseIntent = baseIntent == null ? new Intent() : new Intent(baseIntent);
info.baseActivity = mReuseActivitiesReport.base != null
? mReuseActivitiesReport.base.intent.getComponent()
: null;
diff --git a/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java b/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java
index 801e521..bd70599 100644
--- a/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java
@@ -18,6 +18,7 @@
import static com.android.server.wm.AnimationAdapterProto.REMOTE;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_REMOTE_ANIMATIONS;
import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
import android.graphics.Point;
import android.os.SystemClock;
@@ -26,6 +27,7 @@
import android.view.SurfaceControl;
import com.android.server.protolog.common.ProtoLog;
+import com.android.server.wm.SurfaceAnimator.AnimationType;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -40,6 +42,7 @@
private final WallpaperWindowToken mWallpaperToken;
private SurfaceControl mCapturedLeash;
private SurfaceAnimator.OnAnimationFinishedCallback mCapturedLeashFinishCallback;
+ private @AnimationType int mLastAnimationType;
private long mDurationHint;
private long mStatusBarTransitionDelay;
@@ -77,7 +80,7 @@
wallpaperWindow, durationHint, statusBarTransitionDelay,
animationCanceledRunnable);
wallpaperWindow.startAnimation(wallpaperWindow.getPendingTransaction(),
- wallpaperAdapter, false /* hidden */);
+ wallpaperAdapter, false /* hidden */, ANIMATION_TYPE_WINDOW_ANIMATION);
targets.add(wallpaperAdapter.createRemoteAnimationTarget());
adaptersOut.add(wallpaperAdapter);
});
@@ -110,6 +113,13 @@
}
/**
+ * @return the type of animation.
+ */
+ @AnimationType int getLastAnimationType() {
+ return mLastAnimationType;
+ }
+
+ /**
* @return the wallpaper window
*/
WallpaperWindowToken getToken() {
@@ -124,13 +134,14 @@
@Override
public void startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t,
- SurfaceAnimator.OnAnimationFinishedCallback finishCallback) {
+ @AnimationType int type, SurfaceAnimator.OnAnimationFinishedCallback finishCallback) {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation");
// Restore z-layering until client has a chance to modify it.
t.setLayer(animationLeash, mWallpaperToken.getPrefixOrderIndex());
mCapturedLeash = animationLeash;
mCapturedLeashFinishCallback = finishCallback;
+ mLastAnimationType = type;
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index d828ca6..8672315 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -31,6 +31,7 @@
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -74,6 +75,8 @@
import com.android.internal.util.ToBooleanFunction;
import com.android.server.protolog.common.ProtoLog;
import com.android.server.wm.SurfaceAnimator.Animatable;
+import com.android.server.wm.SurfaceAnimator.AnimationType;
+import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
@@ -1851,19 +1854,24 @@
* @param anim The animation to run.
* @param hidden Whether our container is currently hidden. TODO This should use isVisible at
* some point but the meaning is too weird to work for all containers.
+ * @param type The type of animation defined as {@link AnimationType}.
* @param animationFinishedCallback The callback being triggered when the animation finishes.
*/
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
- @Nullable Runnable animationFinishedCallback) {
- if (DEBUG_ANIM) Slog.v(TAG, "Starting animation on " + this + ": " + anim);
+ @AnimationType int type,
+ @Nullable OnAnimationFinishedCallback animationFinishedCallback) {
+ if (DEBUG_ANIM) {
+ Slog.v(TAG, "Starting animation on " + this + ": type=" + type + ", anim=" + anim);
+ }
// TODO: This should use isVisible() but because isVisible has a really weird meaning at
// the moment this doesn't work for all animatable window containers.
- mSurfaceAnimator.startAnimation(t, anim, hidden, animationFinishedCallback);
+ mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback);
}
- void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden) {
- startAnimation(t, anim, hidden, null /* animationFinishedCallback */);
+ void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
+ @AnimationType int type) {
+ startAnimation(t, anim, hidden, type, null /* animationFinishedCallback */);
}
void transferAnimation(WindowContainer from) {
@@ -1916,7 +1924,8 @@
* @see #getAnimationAdapter
*/
boolean applyAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
- boolean isVoiceInteraction, @Nullable Runnable animationFinishedCallback) {
+ boolean isVoiceInteraction,
+ @Nullable OnAnimationFinishedCallback animationFinishedCallback) {
if (mWmService.mDisableTransitionAnimation) {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
"applyAnimation: transition animation is disabled or skipped. "
@@ -1937,7 +1946,7 @@
AnimationAdapter thumbnailAdapter = adapters.second;
if (adapter != null) {
startAnimation(getPendingTransaction(), adapter, !isVisible(),
- animationFinishedCallback);
+ ANIMATION_TYPE_APP_TRANSITION, animationFinishedCallback);
if (adapter.getShowWallpaper()) {
getDisplayContent().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
@@ -2128,7 +2137,7 @@
/**
* Called when an animation has finished running.
*/
- protected void onAnimationFinished() {
+ protected void onAnimationFinished(@AnimationType int type, AnimationAdapter anim) {
mWmService.onAnimationFinished();
}
diff --git a/services/core/java/com/android/server/wm/WindowContainerThumbnail.java b/services/core/java/com/android/server/wm/WindowContainerThumbnail.java
index 8948f6f..90e3be7 100644
--- a/services/core/java/com/android/server/wm/WindowContainerThumbnail.java
+++ b/services/core/java/com/android/server/wm/WindowContainerThumbnail.java
@@ -20,6 +20,7 @@
import static android.view.SurfaceControl.METADATA_WINDOW_TYPE;
import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static com.android.server.wm.WindowContainerThumbnailProto.HEIGHT;
import static com.android.server.wm.WindowContainerThumbnailProto.SURFACE_ANIMATOR;
import static com.android.server.wm.WindowContainerThumbnailProto.WIDTH;
@@ -40,6 +41,7 @@
import com.android.server.protolog.common.ProtoLog;
import com.android.server.wm.SurfaceAnimator.Animatable;
+import com.android.server.wm.SurfaceAnimator.AnimationType;
import java.util.function.Supplier;
@@ -85,7 +87,7 @@
// We can't use a delegating constructor since we need to
// reference this::onAnimationFinished
mSurfaceAnimator =
- new SurfaceAnimator(this, null /* animationFinishedCallback */,
+ new SurfaceAnimator(this, this::onAnimationFinished /* animationFinishedCallback */,
container.mWmService);
}
mWidth = thumbnailHeader.getWidth();
@@ -130,14 +132,19 @@
new WindowAnimationSpec(anim, position,
mWindowContainer.getDisplayContent().mAppTransition.canSkipFirstFrame(),
mWindowContainer.getDisplayContent().getWindowCornerRadius()),
- mWindowContainer.mWmService.mSurfaceAnimationRunner), false /* hidden */);
+ mWindowContainer.mWmService.mSurfaceAnimationRunner), false /* hidden */,
+ ANIMATION_TYPE_RECENTS, null /* animationFinishedCallback */);
}
/**
* Start animation with existing adapter.
*/
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden) {
- mSurfaceAnimator.startAnimation(t, anim, hidden);
+ mSurfaceAnimator.startAnimation(t, anim, hidden, ANIMATION_TYPE_RECENTS,
+ null /* animationFinishedCallback */);
+ }
+
+ private void onAnimationFinished(@AnimationType int type, AnimationAdapter anim) {
}
void setShowing(Transaction pendingTransaction, boolean show) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 7f84567..563bb26 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -397,7 +397,7 @@
* @see #HIERARCHICAL_ANIMATIONS_PROPERTY
*/
static boolean sHierarchicalAnimations =
- SystemProperties.getBoolean(HIERARCHICAL_ANIMATIONS_PROPERTY, false);
+ SystemProperties.getBoolean(HIERARCHICAL_ANIMATIONS_PROPERTY, true);
// Enums for animation scale update types.
@Retention(RetentionPolicy.SOURCE)
@@ -938,7 +938,7 @@
* Whether the UI is currently running in touch mode (not showing
* navigational focus because the user is directly pressing the screen).
*/
- boolean mInTouchMode;
+ private boolean mInTouchMode;
private ViewServer mViewServer;
final ArrayList<WindowChangeListener> mWindowChangeListeners = new ArrayList<>();
@@ -3461,6 +3461,12 @@
mInputManager.setInTouchMode(mode);
}
+ boolean getInTouchMode() {
+ synchronized (mGlobalLock) {
+ return mInTouchMode;
+ }
+ }
+
public void showEmulatorDisplayOverlayIfNeeded() {
if (mContext.getResources().getBoolean(
com.android.internal.R.bool.config_windowEnableCircularEmulatorDisplayOverlay)
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index fc04126..1faf48f 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -115,6 +115,7 @@
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_RESIZE;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
@@ -231,6 +232,7 @@
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.protolog.common.ProtoLog;
import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
+import com.android.server.wm.SurfaceAnimator.AnimationType;
import com.android.server.wm.utils.WmDisplayCutout;
import java.io.PrintWriter;
@@ -5027,12 +5029,13 @@
}
private void startAnimation(Transaction t, AnimationAdapter adapter) {
- startAnimation(t, adapter, mWinAnimator.mLastHidden, null /* animationFinishedCallback */);
+ startAnimation(t, adapter, mWinAnimator.mLastHidden, ANIMATION_TYPE_WINDOW_ANIMATION,
+ null /* animationFinishedCallback */);
}
@Override
- protected void onAnimationFinished() {
- super.onAnimationFinished();
+ protected void onAnimationFinished(@AnimationType int type, AnimationAdapter anim) {
+ super.onAnimationFinished(type, anim);
mWinAnimator.onAnimationFinished();
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index ec3ef78..65cabad 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -15077,9 +15077,16 @@
return packages;
}
- private List<String> getDefaultCrossProfilePackages() {
- return Arrays.asList(mContext.getResources()
+ @Override
+ public List<String> getDefaultCrossProfilePackages() {
+ Set<String> crossProfilePackages = new HashSet<>();
+
+ Collections.addAll(crossProfilePackages, mContext.getResources()
.getStringArray(R.array.cross_profile_apps));
+ Collections.addAll(crossProfilePackages, mContext.getResources()
+ .getStringArray(R.array.vendor_cross_profile_apps));
+
+ return new ArrayList<>(crossProfilePackages);
}
private List<ActiveAdmin> getProfileOwnerAdminsForCurrentProfileGroup() {
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 f7a9e54..b193a34 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -5817,6 +5817,7 @@
mContext.packageName = admin1.getPackageName();
setCrossProfileAppsList();
+ setVendorCrossProfileAppsList();
assertTrue(dpm.getAllCrossProfilePackages().isEmpty());
}
@@ -5827,6 +5828,7 @@
mContext.packageName = admin1.getPackageName();
setCrossProfileAppsList();
+ setVendorCrossProfileAppsList();
initializeDpms();
assertTrue(dpm.getAllCrossProfilePackages().isEmpty());
@@ -5839,9 +5841,11 @@
dpm.setCrossProfilePackages(admin1, packages);
setCrossProfileAppsList("TEST_DEFAULT_PACKAGE", "TEST_COMMON_PACKAGE");
+ setVendorCrossProfileAppsList("TEST_VENDOR_DEFAULT_PACKAGE");
assertEquals(Sets.newSet(
- "TEST_PACKAGE", "TEST_DEFAULT_PACKAGE", "TEST_COMMON_PACKAGE"),
+ "TEST_PACKAGE", "TEST_DEFAULT_PACKAGE", "TEST_COMMON_PACKAGE",
+ "TEST_VENDOR_DEFAULT_PACKAGE"),
dpm.getAllCrossProfilePackages());
}
@@ -5854,13 +5858,31 @@
dpm.setCrossProfilePackages(admin1, packages);
setCrossProfileAppsList("TEST_DEFAULT_PACKAGE", "TEST_COMMON_PACKAGE");
+ setVendorCrossProfileAppsList("TEST_VENDOR_DEFAULT_PACKAGE");
initializeDpms();
assertEquals(Sets.newSet(
- "TEST_PACKAGE", "TEST_DEFAULT_PACKAGE", "TEST_COMMON_PACKAGE"),
+ "TEST_PACKAGE", "TEST_DEFAULT_PACKAGE", "TEST_COMMON_PACKAGE",
+ "TEST_VENDOR_DEFAULT_PACKAGE"),
dpm.getAllCrossProfilePackages());
}
+ public void testGetDefaultCrossProfilePackages_noPackagesSet_returnsEmpty() {
+ setCrossProfileAppsList();
+ setVendorCrossProfileAppsList();
+
+ assertThat(dpm.getDefaultCrossProfilePackages()).isEmpty();
+ }
+
+ public void testGetDefaultCrossProfilePackages_packagesSet_returnsCombinedSet() {
+ setCrossProfileAppsList("TEST_DEFAULT_PACKAGE", "TEST_COMMON_PACKAGE");
+ setVendorCrossProfileAppsList("TEST_VENDOR_DEFAULT_PACKAGE");
+
+ assertThat(dpm.getDefaultCrossProfilePackages()).isEqualTo(Sets.newSet(
+ "TEST_DEFAULT_PACKAGE", "TEST_COMMON_PACKAGE", "TEST_VENDOR_DEFAULT_PACKAGE"
+ ));
+ }
+
public void testSetCommonCriteriaMode_asDeviceOwner() throws Exception {
setDeviceOwner();
@@ -5892,6 +5914,12 @@
.thenReturn(packages);
}
+ private void setVendorCrossProfileAppsList(String... packages) {
+ when(mContext.getResources()
+ .getStringArray(eq(R.array.vendor_cross_profile_apps)))
+ .thenReturn(packages);
+ }
+
// admin1 is the outgoing DPC, adminAnotherPakcage is the incoming one.
private void assertDeviceOwnershipRevertedWithFakeTransferMetadata() throws Exception {
writeFakeTransferMetadataFile(UserHandle.USER_SYSTEM,
diff --git a/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java b/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java
index 71390db..0f54895 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java
@@ -21,6 +21,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static org.junit.Assert.assertFalse;
@@ -69,8 +70,10 @@
final AnimatingActivityRegistry registry =
activity1.getStack().getAnimatingActivityRegistry();
- activity1.startAnimation(activity1.getPendingTransaction(), mAdapter, false /* hidden */);
- activity2.startAnimation(activity1.getPendingTransaction(), mAdapter, false /* hidden */);
+ activity1.startAnimation(activity1.getPendingTransaction(), mAdapter, false /* hidden */,
+ ANIMATION_TYPE_APP_TRANSITION);
+ activity2.startAnimation(activity1.getPendingTransaction(), mAdapter, false /* hidden */,
+ ANIMATION_TYPE_APP_TRANSITION);
assertTrue(activity1.isAnimating(TRANSITION));
assertTrue(activity2.isAnimating(TRANSITION));
@@ -91,8 +94,10 @@
final AnimatingActivityRegistry registry =
window1.getStack().getAnimatingActivityRegistry();
- window1.startAnimation(window1.getPendingTransaction(), mAdapter, false /* hidden */);
- window2.startAnimation(window1.getPendingTransaction(), mAdapter, false /* hidden */);
+ window1.startAnimation(window1.getPendingTransaction(), mAdapter, false /* hidden */,
+ ANIMATION_TYPE_APP_TRANSITION);
+ window2.startAnimation(window1.getPendingTransaction(), mAdapter, false /* hidden */,
+ ANIMATION_TYPE_APP_TRANSITION);
assertTrue(window1.isAnimating(TRANSITION));
assertTrue(window2.isAnimating(TRANSITION));
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
index b0f3b42..6e78a27 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
@@ -20,6 +20,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.google.common.truth.Truth.assertThat;
@@ -70,7 +71,8 @@
public void clipAfterAnim_boundsLayerIsCreated() {
mActivity.mNeedsAnimationBoundsLayer = true;
- mActivity.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+ mActivity.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */,
+ ANIMATION_TYPE_APP_TRANSITION);
verify(mTransaction).reparent(eq(mActivity.getSurfaceControl()),
eq(mActivity.mSurfaceAnimator.mLeash));
verify(mTransaction).reparent(eq(mActivity.mSurfaceAnimator.mLeash),
@@ -82,7 +84,8 @@
mActivity.mNeedsAnimationBoundsLayer = true;
mActivity.mNeedsZBoost = true;
- mActivity.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+ mActivity.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */,
+ ANIMATION_TYPE_APP_TRANSITION);
verify(mTransaction).setLayer(eq(mActivity.mAnimationBoundsLayer),
intThat(layer -> layer >= ActivityRecord.Z_BOOST_BASE));
}
@@ -91,15 +94,18 @@
@FlakyTest(bugId = 131005232)
public void clipAfterAnim_boundsLayerIsDestroyed() {
mActivity.mNeedsAnimationBoundsLayer = true;
- mActivity.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+ mActivity.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */,
+ ANIMATION_TYPE_APP_TRANSITION);
final SurfaceControl leash = mActivity.mSurfaceAnimator.mLeash;
final SurfaceControl animationBoundsLayer = mActivity.mAnimationBoundsLayer;
final ArgumentCaptor<SurfaceAnimator.OnAnimationFinishedCallback> callbackCaptor =
ArgumentCaptor.forClass(
SurfaceAnimator.OnAnimationFinishedCallback.class);
- verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture());
+ verify(mSpec).startAnimation(any(), any(), eq(ANIMATION_TYPE_APP_TRANSITION),
+ callbackCaptor.capture());
- callbackCaptor.getValue().onAnimationFinished(mSpec);
+ callbackCaptor.getValue().onAnimationFinished(
+ ANIMATION_TYPE_APP_TRANSITION, mSpec);
verify(mTransaction).remove(eq(leash));
verify(mTransaction).remove(eq(animationBoundsLayer));
assertThat(mActivity.mNeedsAnimationBoundsLayer).isFalse();
@@ -108,7 +114,8 @@
@Test
public void clipAfterAnimCancelled_boundsLayerIsDestroyed() {
mActivity.mNeedsAnimationBoundsLayer = true;
- mActivity.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+ mActivity.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */,
+ ANIMATION_TYPE_APP_TRANSITION);
final SurfaceControl leash = mActivity.mSurfaceAnimator.mLeash;
final SurfaceControl animationBoundsLayer = mActivity.mAnimationBoundsLayer;
@@ -123,7 +130,8 @@
public void clipNoneAnim_boundsLayerIsNotCreated() {
mActivity.mNeedsAnimationBoundsLayer = false;
- mActivity.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+ mActivity.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */,
+ ANIMATION_TYPE_APP_TRANSITION);
verify(mTransaction).reparent(eq(mActivity.getSurfaceControl()),
eq(mActivity.mSurfaceAnimator.mLeash));
assertThat(mActivity.mAnimationBoundsLayer).isNull();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index 7344fa4..77ceeed 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -22,11 +22,13 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_DIMMER;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.when;
@@ -37,6 +39,9 @@
import android.view.SurfaceControl;
import android.view.SurfaceSession;
+import com.android.server.wm.SurfaceAnimator.AnimationType;
+import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -114,11 +119,11 @@
private static class SurfaceAnimatorStarterImpl implements Dimmer.SurfaceAnimatorStarter {
@Override
public void startAnimation(SurfaceAnimator surfaceAnimator, SurfaceControl.Transaction t,
- AnimationAdapter anim, boolean hidden,
- @Nullable Runnable animationFinishedCallback) {
- surfaceAnimator.mStaticAnimationFinishedCallback.run();
+ AnimationAdapter anim, boolean hidden, @AnimationType int type,
+ @Nullable OnAnimationFinishedCallback animationFinishedCallback) {
+ surfaceAnimator.mStaticAnimationFinishedCallback.onAnimationFinished(type, anim);
if (animationFinishedCallback != null) {
- animationFinishedCallback.run();
+ animationFinishedCallback.onAnimationFinished(type, anim);
}
}
}
@@ -224,7 +229,7 @@
mDimmer.updateDims(mTransaction, new Rect());
verify(mSurfaceAnimatorStarter).startAnimation(any(SurfaceAnimator.class), any(
SurfaceControl.Transaction.class), any(AnimationAdapter.class), anyBoolean(),
- isNull());
+ eq(ANIMATION_TYPE_DIMMER), isNull());
verify(mHost.getPendingTransaction()).remove(dimLayer);
}
@@ -282,7 +287,7 @@
mDimmer.updateDims(mTransaction, new Rect());
verify(mSurfaceAnimatorStarter, never()).startAnimation(any(SurfaceAnimator.class), any(
SurfaceControl.Transaction.class), any(AnimationAdapter.class), anyBoolean(),
- isNull());
+ eq(ANIMATION_TYPE_DIMMER), isNull());
verify(mTransaction).remove(dimLayer);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
new file mode 100644
index 0000000..618e608
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+
+import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS;
+import static com.android.server.wm.DisplayArea.Type.ANY;
+import static com.android.server.wm.DisplayArea.Type.BELOW_TASKS;
+import static com.android.server.wm.DisplayArea.Type.checkChild;
+import static com.android.server.wm.DisplayArea.Type.checkSiblings;
+import static com.android.server.wm.DisplayArea.Type.typeOf;
+import static com.android.server.wm.testing.Assert.assertThrows;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+
+import android.os.Binder;
+import android.platform.test.annotations.Presubmit;
+import android.view.SurfaceControl;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+@Presubmit
+public class DisplayAreaTest {
+
+ @Rule
+ public SystemServicesTestRule mWmsRule = new SystemServicesTestRule();
+
+ @Test
+ public void testDisplayArea_positionChanged_throwsIfIncompatibleChild() {
+ WindowManagerService wms = mWmsRule.getWindowManagerService();
+ DisplayArea<WindowContainer> parent = new DisplayArea<>(wms, BELOW_TASKS, "Parent");
+ DisplayArea<WindowContainer> child = new DisplayArea<>(wms, ANY, "Child");
+
+ assertThrows(IllegalStateException.class, () -> parent.addChild(child, 0));
+ }
+
+ @Test
+ public void testDisplayArea_positionChanged_throwsIfIncompatibleSibling() {
+ WindowManagerService wms = mWmsRule.getWindowManagerService();
+ DisplayArea<WindowContainer> parent = new SurfacelessDisplayArea<>(wms, ANY, "Parent");
+ DisplayArea<WindowContainer> child1 = new DisplayArea<>(wms, ANY, "Child1");
+ DisplayArea<WindowContainer> child2 = new DisplayArea<>(wms, ANY, "Child2");
+
+ parent.addChild(child1, 0);
+ assertThrows(IllegalStateException.class, () -> parent.addChild(child2, 0));
+ }
+
+ @Test
+ public void testType_typeOf() {
+ WindowManagerService wms = mWmsRule.getWindowManagerService();
+
+ assertEquals(ABOVE_TASKS, typeOf(new DisplayArea<>(wms, ABOVE_TASKS, "test")));
+ assertEquals(ANY, typeOf(new DisplayArea<>(wms, ANY, "test")));
+ assertEquals(BELOW_TASKS, typeOf(new DisplayArea<>(wms, BELOW_TASKS, "test")));
+
+ assertEquals(ABOVE_TASKS, typeOf(createWindowToken(TYPE_APPLICATION_OVERLAY)));
+ assertEquals(ABOVE_TASKS, typeOf(createWindowToken(TYPE_PRESENTATION)));
+ assertEquals(BELOW_TASKS, typeOf(createWindowToken(TYPE_WALLPAPER)));
+
+ assertThrows(IllegalArgumentException.class, () -> typeOf(mock(ActivityRecord.class)));
+ assertThrows(IllegalArgumentException.class, () -> typeOf(mock(WindowContainer.class)));
+ }
+
+ @Test
+ public void testType_checkSiblings() {
+ checkSiblings(BELOW_TASKS, BELOW_TASKS);
+ checkSiblings(BELOW_TASKS, ANY);
+ checkSiblings(BELOW_TASKS, ABOVE_TASKS);
+ checkSiblings(ANY, ABOVE_TASKS);
+ checkSiblings(ABOVE_TASKS, ABOVE_TASKS);
+
+ assertThrows(IllegalStateException.class, () -> checkSiblings(ABOVE_TASKS, BELOW_TASKS));
+ assertThrows(IllegalStateException.class, () -> checkSiblings(ABOVE_TASKS, ANY));
+ assertThrows(IllegalStateException.class, () -> checkSiblings(ANY, ANY));
+ assertThrows(IllegalStateException.class, () -> checkSiblings(ANY, BELOW_TASKS));
+ }
+
+ @Test
+ public void testType_checkChild() {
+ checkChild(ANY, ANY);
+ checkChild(ANY, ABOVE_TASKS);
+ checkChild(ANY, BELOW_TASKS);
+ checkChild(ABOVE_TASKS, ABOVE_TASKS);
+ checkChild(BELOW_TASKS, BELOW_TASKS);
+
+ assertThrows(IllegalStateException.class, () -> checkChild(ABOVE_TASKS, BELOW_TASKS));
+ assertThrows(IllegalStateException.class, () -> checkChild(ABOVE_TASKS, ANY));
+ assertThrows(IllegalStateException.class, () -> checkChild(BELOW_TASKS, ABOVE_TASKS));
+ assertThrows(IllegalStateException.class, () -> checkChild(BELOW_TASKS, ANY));
+ }
+
+ private WindowToken createWindowToken(int type) {
+ return new WindowToken(mWmsRule.getWindowManagerService(), new Binder(),
+ type, false /* persist */, null /* displayContent */,
+ false /* canManageTokens */);
+ }
+
+ private static class SurfacelessDisplayArea<T extends WindowContainer> extends DisplayArea<T> {
+
+ SurfacelessDisplayArea(WindowManagerService wms, Type type, String name) {
+ super(wms, type, name);
+ }
+
+ @Override
+ SurfaceControl.Builder makeChildSurface(WindowContainer child) {
+ return new MockSurfaceControlBuilder();
+ }
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 1637370..2ea00ce 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -139,11 +139,11 @@
mAppWindow,
mChildAppWindowAbove,
mDockedDividerWindow,
+ mImeWindow,
+ mImeDialogWindow,
mStatusBarWindow,
mNotificationShadeWindow,
- mNavBarWindow,
- mImeWindow,
- mImeDialogWindow));
+ mNavBarWindow));
}
@Test
@@ -232,11 +232,11 @@
mChildAppWindowAbove,
mDockedDividerWindow,
voiceInteractionWindow,
+ mImeWindow,
+ mImeDialogWindow,
mStatusBarWindow,
mNotificationShadeWindow,
- mNavBarWindow,
- mImeWindow,
- mImeDialogWindow));
+ mNavBarWindow));
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 945928d..7753a32 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -37,6 +37,7 @@
import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_TOP;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -103,14 +104,15 @@
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
AnimationAdapter adapter = mController.addAnimation(activity.getTask(),
false /* isRecentTaskInvisible */);
- adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+ adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_RECENTS,
+ mFinishedCallback);
// Remove the app window so that the animation target can not be created
activity.removeImmediately();
mController.startAnimation();
// Verify that the finish callback to reparent the leash is called
- verify(mFinishedCallback).onAnimationFinished(eq(adapter));
+ verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_RECENTS), eq(adapter));
// Verify the animation canceled callback to the app was made
verify(mMockRunner).onAnimationCanceled(null /* taskSnapshot */);
verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
@@ -122,7 +124,8 @@
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
AnimationAdapter adapter = mController.addAnimation(activity.getTask(),
false /* isRecentTaskInvisible */);
- adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+ adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_RECENTS,
+ mFinishedCallback);
// Remove the app window so that the animation target can not be created
activity.removeImmediately();
diff --git a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
index a23425f..be25597 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
@@ -18,6 +18,8 @@
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
+
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -108,7 +110,7 @@
overrideWindow.mAttrs.preferredDisplayModeId = LOW_MODE_ID;
overrideWindow.mActivityRecord.mSurfaceAnimator.startAnimation(
overrideWindow.getPendingTransaction(), mock(AnimationAdapter.class),
- false /* hidden */);
+ false /* hidden */, ANIMATION_TYPE_APP_TRANSITION);
mPolicy.addNonHighRefreshRatePackage("com.android.test");
assertEquals(0, mPolicy.getPreferredModeId(overrideWindow));
}
@@ -124,7 +126,7 @@
cameraUsingWindow.mActivityRecord.mSurfaceAnimator.startAnimation(
cameraUsingWindow.getPendingTransaction(), mock(AnimationAdapter.class),
- false /* hidden */);
+ false /* hidden */, ANIMATION_TYPE_APP_TRANSITION);
assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow));
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 1a57596..3a724a1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -25,6 +25,8 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -97,7 +99,8 @@
try {
final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
- adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+ adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
+ mFinishedCallback);
mController.goodToGo();
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
@@ -122,7 +125,8 @@
verify(mMockTransaction).setWindowCrop(mMockLeash, 100, 50);
finishedCaptor.getValue().onAnimationFinished();
- verify(mFinishedCallback).onAnimationFinished(eq(adapter));
+ verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION),
+ eq(adapter));
} finally {
mDisplayContent.mOpeningApps.clear();
}
@@ -133,7 +137,8 @@
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
- adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+ adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
+ mFinishedCallback);
mController.goodToGo();
adapter.onAnimationCancelled(mMockLeash);
@@ -146,14 +151,16 @@
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
- adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+ adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
+ mFinishedCallback);
mController.goodToGo();
mClock.fastForward(2500);
mHandler.timeAdvance();
verify(mMockRunner).onAnimationCancelled();
- verify(mFinishedCallback).onAnimationFinished(eq(adapter));
+ verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION),
+ eq(adapter));
}
@Test
@@ -164,7 +171,8 @@
"testWin");
final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
win.mActivityRecord, new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
- adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+ adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
+ mFinishedCallback);
mController.goodToGo();
mClock.fastForward(2500);
@@ -176,7 +184,8 @@
mHandler.timeAdvance();
verify(mMockRunner).onAnimationCancelled();
- verify(mFinishedCallback).onAnimationFinished(eq(adapter));
+ verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION),
+ eq(adapter));
} finally {
mWm.setAnimationScale(2, 1.0f);
}
@@ -205,7 +214,8 @@
new Point(50, 100), new Rect(50, 100, 150, 150), null);
final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win2.mActivityRecord,
new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
- adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+ adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
+ mFinishedCallback);
mController.goodToGo();
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
@@ -225,11 +235,13 @@
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
- adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+ adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
+ mFinishedCallback);
win.mActivityRecord.removeImmediately();
mController.goodToGo();
verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
- verify(mFinishedCallback).onAnimationFinished(eq(adapter));
+ verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION),
+ eq(adapter));
}
@Test
@@ -242,9 +254,10 @@
new Rect(0, 0, 200, 200));
assertNotNull(record.mThumbnailAdapter);
((AnimationAdapter) record.mAdapter)
- .startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+ .startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION,
+ mFinishedCallback);
((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash,
- mMockTransaction, mThumbnailFinishedCallback);
+ mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback);
mController.goodToGo();
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
@@ -272,8 +285,10 @@
verify(mMockTransaction).setPosition(mMockThumbnailLeash, 0, 0);
finishedCaptor.getValue().onAnimationFinished();
- verify(mFinishedCallback).onAnimationFinished(eq(record.mAdapter));
- verify(mThumbnailFinishedCallback).onAnimationFinished(eq(record.mThumbnailAdapter));
+ verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_WINDOW_ANIMATION),
+ eq(record.mAdapter));
+ verify(mThumbnailFinishedCallback).onAnimationFinished(
+ eq(ANIMATION_TYPE_WINDOW_ANIMATION), eq(record.mThumbnailAdapter));
} finally {
mDisplayContent.mChangingApps.clear();
}
@@ -290,7 +305,8 @@
try {
final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
- adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+ adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
+ mFinishedCallback);
mController.goodToGo();
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
@@ -318,7 +334,8 @@
try {
final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord,
new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
- adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+ adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
+ mFinishedCallback);
mController.goodToGo();
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
index 2894241..552c476 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -22,6 +22,8 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -39,6 +41,7 @@
import androidx.test.filters.SmallTest;
import com.android.server.wm.SurfaceAnimator.Animatable;
+import com.android.server.wm.SurfaceAnimator.AnimationType;
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
import org.junit.After;
@@ -89,25 +92,30 @@
@Test
public void testRunAnimation() {
- mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+ mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */,
+ ANIMATION_TYPE_RECENTS);
final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
OnAnimationFinishedCallback.class);
assertAnimating(mAnimatable);
verify(mTransaction).reparent(eq(mAnimatable.mSurface), eq(mAnimatable.mLeash));
- verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture());
+ verify(mSpec).startAnimation(any(), any(), eq(ANIMATION_TYPE_RECENTS),
+ callbackCaptor.capture());
- callbackCaptor.getValue().onAnimationFinished(mSpec);
+ callbackCaptor.getValue().onAnimationFinished(ANIMATION_TYPE_RECENTS, mSpec);
assertNotAnimating(mAnimatable);
assertTrue(mAnimatable.mFinishedCallbackCalled);
+ assertEquals(ANIMATION_TYPE_RECENTS, mAnimatable.mFinishedAnimationType);
verify(mTransaction).remove(eq(mAnimatable.mLeash));
// TODO: Verify reparenting once we use mPendingTransaction to reparent it back
}
@Test
public void testOverrideAnimation() {
- mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+ mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */,
+ ANIMATION_TYPE_APP_TRANSITION);
final SurfaceControl firstLeash = mAnimatable.mLeash;
- mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec2, true /* hidden */);
+ mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec2, true /* hidden */,
+ ANIMATION_TYPE_APP_TRANSITION);
verify(mTransaction).remove(eq(firstLeash));
assertFalse(mAnimatable.mFinishedCallbackCalled);
@@ -115,34 +123,40 @@
final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
OnAnimationFinishedCallback.class);
assertAnimating(mAnimatable);
- verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture());
+ verify(mSpec).startAnimation(any(), any(), eq(ANIMATION_TYPE_APP_TRANSITION),
+ callbackCaptor.capture());
// First animation was finished, but this shouldn't cancel the second animation
- callbackCaptor.getValue().onAnimationFinished(mSpec);
+ callbackCaptor.getValue().onAnimationFinished(ANIMATION_TYPE_APP_TRANSITION, mSpec);
assertTrue(mAnimatable.mSurfaceAnimator.isAnimating());
// Second animation was finished
- verify(mSpec2).startAnimation(any(), any(), callbackCaptor.capture());
- callbackCaptor.getValue().onAnimationFinished(mSpec2);
+ verify(mSpec2).startAnimation(any(), any(), eq(ANIMATION_TYPE_APP_TRANSITION),
+ callbackCaptor.capture());
+ callbackCaptor.getValue().onAnimationFinished(ANIMATION_TYPE_APP_TRANSITION, mSpec2);
assertNotAnimating(mAnimatable);
assertTrue(mAnimatable.mFinishedCallbackCalled);
+ assertEquals(ANIMATION_TYPE_APP_TRANSITION, mAnimatable.mFinishedAnimationType);
}
@Test
public void testCancelAnimation() {
- mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+ mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */,
+ ANIMATION_TYPE_APP_TRANSITION);
assertAnimating(mAnimatable);
mAnimatable.mSurfaceAnimator.cancelAnimation();
assertNotAnimating(mAnimatable);
verify(mSpec).onAnimationCancelled(any());
assertTrue(mAnimatable.mFinishedCallbackCalled);
+ assertEquals(ANIMATION_TYPE_APP_TRANSITION, mAnimatable.mFinishedAnimationType);
verify(mTransaction).remove(eq(mAnimatable.mLeash));
}
@Test
public void testCancelWithNullFinishCallbackAnimation() {
SurfaceAnimator animator = new SurfaceAnimator(mAnimatable, null, mWm);
- animator.startAnimation(mTransaction, mSpec, true /* hidden */);
+ animator.startAnimation(mTransaction, mSpec, true /* hidden */,
+ ANIMATION_TYPE_APP_TRANSITION);
assertTrue(animator.isAnimating());
assertNotNull(animator.getAnimation());
animator.cancelAnimation();
@@ -155,32 +169,37 @@
@Test
public void testDelayingAnimationStart() {
mAnimatable.mSurfaceAnimator.startDelayingAnimationStart();
- mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+ mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */,
+ ANIMATION_TYPE_APP_TRANSITION);
verifyZeroInteractions(mSpec);
assertAnimating(mAnimatable);
assertTrue(mAnimatable.mSurfaceAnimator.isAnimationStartDelayed());
mAnimatable.mSurfaceAnimator.endDelayingAnimationStart();
- verify(mSpec).startAnimation(any(), any(), any());
+ verify(mSpec).startAnimation(any(), any(), eq(ANIMATION_TYPE_APP_TRANSITION), any());
}
@Test
public void testDelayingAnimationStartAndCancelled() {
mAnimatable.mSurfaceAnimator.startDelayingAnimationStart();
- mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+ mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */,
+ ANIMATION_TYPE_APP_TRANSITION);
mAnimatable.mSurfaceAnimator.cancelAnimation();
verifyZeroInteractions(mSpec);
assertNotAnimating(mAnimatable);
assertTrue(mAnimatable.mFinishedCallbackCalled);
+ assertEquals(ANIMATION_TYPE_APP_TRANSITION, mAnimatable.mFinishedAnimationType);
verify(mTransaction).remove(eq(mAnimatable.mLeash));
}
@Test
public void testTransferAnimation() {
- mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+ mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */,
+ ANIMATION_TYPE_APP_TRANSITION);
final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
OnAnimationFinishedCallback.class);
- verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture());
+ verify(mSpec).startAnimation(any(), any(), eq(ANIMATION_TYPE_APP_TRANSITION),
+ callbackCaptor.capture());
final SurfaceControl leash = mAnimatable.mLeash;
mAnimatable2.mSurfaceAnimator.transferAnimation(mAnimatable.mSurfaceAnimator);
@@ -188,15 +207,17 @@
assertAnimating(mAnimatable2);
assertEquals(leash, mAnimatable2.mSurfaceAnimator.mLeash);
verify(mTransaction, never()).remove(eq(leash));
- callbackCaptor.getValue().onAnimationFinished(mSpec);
+ callbackCaptor.getValue().onAnimationFinished(ANIMATION_TYPE_APP_TRANSITION, mSpec);
assertNotAnimating(mAnimatable2);
assertTrue(mAnimatable2.mFinishedCallbackCalled);
+ assertEquals(ANIMATION_TYPE_APP_TRANSITION, mAnimatable.mFinishedAnimationType);
verify(mTransaction).remove(eq(leash));
}
@Test
public void testOnAnimationLeashLostWhenAnimatableParentSurfaceControlNull() {
- mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+ mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */,
+ ANIMATION_TYPE_APP_TRANSITION);
spyOn(mAnimatable);
// Verify onAnimationLeashLost will be called even animatable's parent surface control lost.
@@ -215,13 +236,14 @@
final OnAnimationFinishedCallback onFinishedCallback = startDeferFinishAnimatable(mSpec);
// Finish the animation but then make sure we are deferring.
- onFinishedCallback.onAnimationFinished(mSpec);
+ onFinishedCallback.onAnimationFinished(ANIMATION_TYPE_APP_TRANSITION, mSpec);
assertAnimating(mDeferFinishAnimatable);
// Now end defer finishing.
mDeferFinishAnimatable.mEndDeferFinishCallback.run();
assertNotAnimating(mAnimatable2);
assertTrue(mDeferFinishAnimatable.mFinishedCallbackCalled);
+ assertEquals(ANIMATION_TYPE_APP_TRANSITION, mDeferFinishAnimatable.mFinishedAnimationType);
verify(mTransaction).remove(eq(mDeferFinishAnimatable.mLeash));
}
@@ -229,7 +251,7 @@
public void testDeferFinishDoNotFinishNextAnimation() {
// Start the first animation.
final OnAnimationFinishedCallback onFinishedCallback = startDeferFinishAnimatable(mSpec);
- onFinishedCallback.onAnimationFinished(mSpec);
+ onFinishedCallback.onAnimationFinished(ANIMATION_TYPE_APP_TRANSITION, mSpec);
// The callback is the resetAndInvokeFinish in {@link SurfaceAnimator#getFinishedCallback}.
final Runnable firstDeferFinishCallback = mDeferFinishAnimatable.mEndDeferFinishCallback;
@@ -247,11 +269,12 @@
private OnAnimationFinishedCallback startDeferFinishAnimatable(AnimationAdapter anim) {
mDeferFinishAnimatable.mSurfaceAnimator.startAnimation(mTransaction, anim,
- true /* hidden */);
+ true /* hidden */, ANIMATION_TYPE_APP_TRANSITION);
final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
OnAnimationFinishedCallback.class);
assertAnimating(mDeferFinishAnimatable);
- verify(anim).startAnimation(any(), any(), callbackCaptor.capture());
+ verify(anim).startAnimation(any(), any(), eq(ANIMATION_TYPE_APP_TRANSITION),
+ callbackCaptor.capture());
return callbackCaptor.getValue();
}
@@ -274,6 +297,7 @@
final SurfaceAnimator mSurfaceAnimator;
SurfaceControl mLeash;
boolean mFinishedCallbackCalled;
+ @AnimationType int mFinishedAnimationType;
MyAnimatable(WindowManagerService wm, SurfaceSession session, Transaction transaction) {
mSession = session;
@@ -343,7 +367,11 @@
return 1;
}
- private final Runnable mFinishedCallback = () -> mFinishedCallbackCalled = true;
+ private final SurfaceAnimator.OnAnimationFinishedCallback mFinishedCallback = (
+ type, anim) -> {
+ mFinishedCallbackCalled = true;
+ mFinishedAnimationType = type;
+ };
}
private static class DeferFinishAnimatable extends MyAnimatable {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 05d048d3..4a87701 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -34,6 +34,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -61,6 +62,8 @@
import androidx.test.filters.SmallTest;
+import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
@@ -829,7 +832,8 @@
wc.getDisplayContent().mAppTransition.overridePendingAppTransitionRemote(adapter);
spyOn(wc);
doReturn(true).when(wc).okToAnimate();
- final Runnable onAnimationFinishedCallback = mock(Runnable.class);
+ final OnAnimationFinishedCallback onAnimationFinishedCallback =
+ mock(OnAnimationFinishedCallback.class);
// Make sure animating state is as expected after applied animation.
assertTrue(wc.applyAnimation(null, TRANSIT_TASK_OPEN, true, false,
@@ -837,16 +841,18 @@
assertEquals(wc.getTopMostActivity(), act);
assertTrue(wc.isAnimating());
assertTrue(act.isAnimating(PARENTS));
- verify(onAnimationFinishedCallback, times(0)).run();
+ verify(onAnimationFinishedCallback, times(0)).onAnimationFinished(
+ eq(ANIMATION_TYPE_APP_TRANSITION), any());
// Make sure animation finish callback will be received and reset animating state after
// animation finish.
wc.getDisplayContent().mAppTransition.goodToGo(TRANSIT_TASK_OPEN, act,
mDisplayContent.mOpeningApps);
- verify(wc).onAnimationFinished();
+ verify(wc).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION), any());
assertFalse(wc.isAnimating());
assertFalse(act.isAnimating(PARENTS));
- verify(onAnimationFinishedCallback, times(1)).run();
+ verify(onAnimationFinishedCallback, times(1)).onAnimationFinished(
+ eq(ANIMATION_TYPE_APP_TRANSITION), any());
}
/* Used so we can gain access to some protected members of the {@link WindowContainer} class */
diff --git a/services/tests/wmtests/src/com/android/server/wm/testing/Assert.java b/services/tests/wmtests/src/com/android/server/wm/testing/Assert.java
new file mode 100644
index 0000000..1e98277
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/testing/Assert.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.testing;
+
+/**
+ * Assertions for WM tests.
+ */
+public class Assert {
+
+ /**
+ * Runs {@code r} and asserts that an exception of type {@code expectedThrowable} is thrown.
+ * @param expectedThrowable the type of throwable that is expected to be thrown
+ * @param r the {@link Runnable} which is run and expected to throw.
+ * @throws AssertionError if {@code r} does not throw, or throws a runnable that is not an
+ * instance of {@code expectedThrowable}.
+ */
+ // TODO: remove once Android migrates to JUnit 4.13, which provides assertThrows
+ public static void assertThrows(Class<? extends Throwable> expectedThrowable, Runnable r) {
+ try {
+ r.run();
+ } catch (Throwable t) {
+ if (expectedThrowable.isInstance(t)) {
+ return;
+ } else if (t instanceof Exception) {
+ throw new AssertionError("Expected " + expectedThrowable
+ + ", but got " + t.getClass(), t);
+ } else {
+ // Re-throw Errors and other non-Exception throwables.
+ throw t;
+ }
+ }
+ throw new AssertionError("Expected " + expectedThrowable + ", but nothing was thrown");
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/testing/AssertTest.java b/services/tests/wmtests/src/com/android/server/wm/testing/AssertTest.java
new file mode 100644
index 0000000..df12761
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/testing/AssertTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.testing;
+
+import static com.android.server.wm.testing.Assert.assertThrows;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class AssertTest {
+
+ @Rule
+ public ExpectedException mExpectedException = ExpectedException.none();
+
+ @Test
+ public void assertThrows_runsRunnable() {
+ boolean[] ran = new boolean[] { false };
+ assertThrows(TestException.class, () -> {
+ ran[0] = true;
+ throw new TestException();
+ });
+ assertTrue(ran[0]);
+ }
+
+ @Test
+ public void assertThrows_failsIfNothingThrown() {
+ mExpectedException.expect(AssertionError.class);
+ assertThrows(TestException.class, () -> {
+ });
+ }
+
+ @Test
+ public void assertThrows_failsIfWrongExceptionThrown() {
+ mExpectedException.expect(AssertionError.class);
+ assertThrows(TestException.class, () -> {
+ throw new RuntimeException();
+ });
+ }
+
+ @Test
+ public void assertThrows_succeedsIfGivenExceptionThrown() {
+ assertThrows(TestException.class, () -> {
+ throw new TestException();
+ });
+ }
+
+ @Test
+ public void assertThrows_succeedsIfSubExceptionThrown() {
+ assertThrows(RuntimeException.class, () -> {
+ throw new TestException();
+ });
+ }
+
+ @Test
+ public void assertThrows_rethrowsUnexpectedErrors() {
+ mExpectedException.expect(TestError.class);
+ assertThrows(TestException.class, () -> {
+ throw new TestError();
+ });
+ }
+
+ static class TestException extends RuntimeException {
+ }
+
+ static class TestError extends Error {
+ }
+
+}
diff --git a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
index 7ab4b56..2d5df4f 100644
--- a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
@@ -27,12 +27,18 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import android.content.Context;
import android.os.PersistableBundle;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -52,15 +58,27 @@
private static final Executor INLINE_EXECUTOR = x -> x.run();
+ @Mock private Context mContext;
+ @Mock private IConnectivityManager mService;
@Mock private ConnectivityDiagnosticsCallback mCb;
private ConnectivityDiagnosticsBinder mBinder;
+ private ConnectivityDiagnosticsManager mManager;
@Before
public void setUp() {
+ mContext = mock(Context.class);
+ mService = mock(IConnectivityManager.class);
mCb = mock(ConnectivityDiagnosticsCallback.class);
mBinder = new ConnectivityDiagnosticsBinder(mCb, INLINE_EXECUTOR);
+ mManager = new ConnectivityDiagnosticsManager(mContext, mService);
+ }
+
+ @After
+ public void tearDown() {
+ // clear ConnectivityDiagnosticsManager callbacks map
+ ConnectivityDiagnosticsManager.sCallbacks.clear();
}
private ConnectivityReport createSampleConnectivityReport() {
@@ -245,4 +263,53 @@
// latch without waiting.
verify(mCb).onNetworkConnectivityReported(eq(n), eq(connectivity));
}
+
+ @Test
+ public void testRegisterConnectivityDiagnosticsCallback() throws Exception {
+ final NetworkRequest request = new NetworkRequest.Builder().build();
+
+ mManager.registerConnectivityDiagnosticsCallback(request, INLINE_EXECUTOR, mCb);
+
+ verify(mService).registerConnectivityDiagnosticsCallback(
+ any(ConnectivityDiagnosticsBinder.class), eq(request));
+ assertTrue(ConnectivityDiagnosticsManager.sCallbacks.containsKey(mCb));
+ }
+
+ @Test
+ public void testRegisterDuplicateConnectivityDiagnosticsCallback() throws Exception {
+ final NetworkRequest request = new NetworkRequest.Builder().build();
+
+ mManager.registerConnectivityDiagnosticsCallback(request, INLINE_EXECUTOR, mCb);
+
+ try {
+ mManager.registerConnectivityDiagnosticsCallback(request, INLINE_EXECUTOR, mCb);
+ fail("Duplicate callback registration should fail");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testUnregisterConnectivityDiagnosticsCallback() throws Exception {
+ final NetworkRequest request = new NetworkRequest.Builder().build();
+ mManager.registerConnectivityDiagnosticsCallback(request, INLINE_EXECUTOR, mCb);
+
+ mManager.unregisterConnectivityDiagnosticsCallback(mCb);
+
+ verify(mService).unregisterConnectivityDiagnosticsCallback(
+ any(ConnectivityDiagnosticsBinder.class));
+ assertFalse(ConnectivityDiagnosticsManager.sCallbacks.containsKey(mCb));
+
+ // verify that re-registering is successful
+ mManager.registerConnectivityDiagnosticsCallback(request, INLINE_EXECUTOR, mCb);
+ verify(mService, times(2)).registerConnectivityDiagnosticsCallback(
+ any(ConnectivityDiagnosticsBinder.class), eq(request));
+ assertTrue(ConnectivityDiagnosticsManager.sCallbacks.containsKey(mCb));
+ }
+
+ @Test
+ public void testUnregisterUnknownConnectivityDiagnosticsCallback() throws Exception {
+ mManager.unregisterConnectivityDiagnosticsCallback(mCb);
+
+ verifyNoMoreInteractions(mService);
+ }
}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index a0a1352..5592cd7 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -139,6 +139,7 @@
import android.net.ConnectivityManager.PacketKeepaliveCallback;
import android.net.ConnectivityManager.TooManyRequestsException;
import android.net.ConnectivityThread;
+import android.net.IConnectivityDiagnosticsCallback;
import android.net.IDnsResolver;
import android.net.IIpConnectivityMetrics;
import android.net.INetd;
@@ -180,6 +181,7 @@
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.IBinder;
import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.Parcel;
@@ -210,6 +212,7 @@
import com.android.internal.util.WakeupMessage;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.ConnectivityService.ConnectivityDiagnosticsCallbackInfo;
import com.android.server.connectivity.ConnectivityConstants;
import com.android.server.connectivity.DefaultNetworkMetrics;
import com.android.server.connectivity.IpConnectivityMetrics;
@@ -322,6 +325,8 @@
@Mock UserManager mUserManager;
@Mock NotificationManager mNotificationManager;
@Mock AlarmManager mAlarmManager;
+ @Mock IConnectivityDiagnosticsCallback mConnectivityDiagnosticsCallback;
+ @Mock IBinder mIBinder;
private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
ArgumentCaptor.forClass(ResolverParamsParcel.class);
@@ -6355,4 +6360,70 @@
UserHandle.getAppId(uid));
return packageInfo;
}
+
+ @Test
+ public void testRegisterConnectivityDiagnosticsCallbackInvalidRequest() throws Exception {
+ final NetworkRequest request =
+ new NetworkRequest(
+ new NetworkCapabilities(), TYPE_ETHERNET, 0, NetworkRequest.Type.NONE);
+ try {
+ mService.registerConnectivityDiagnosticsCallback(
+ mConnectivityDiagnosticsCallback, request);
+ fail("registerConnectivityDiagnosticsCallback should throw on invalid NetworkRequest");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testRegisterUnregisterConnectivityDiagnosticsCallback() throws Exception {
+ final NetworkRequest wifiRequest =
+ new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI).build();
+
+ when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
+
+ mService.registerConnectivityDiagnosticsCallback(
+ mConnectivityDiagnosticsCallback, wifiRequest);
+
+ verify(mIBinder, timeout(TIMEOUT_MS))
+ .linkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt());
+ assertTrue(
+ mService.mConnectivityDiagnosticsCallbacks.containsKey(
+ mConnectivityDiagnosticsCallback));
+
+ mService.unregisterConnectivityDiagnosticsCallback(mConnectivityDiagnosticsCallback);
+ verify(mIBinder, timeout(TIMEOUT_MS))
+ .unlinkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt());
+ assertFalse(
+ mService.mConnectivityDiagnosticsCallbacks.containsKey(
+ mConnectivityDiagnosticsCallback));
+ verify(mConnectivityDiagnosticsCallback, atLeastOnce()).asBinder();
+ }
+
+ @Test
+ public void testRegisterDuplicateConnectivityDiagnosticsCallback() throws Exception {
+ final NetworkRequest wifiRequest =
+ new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI).build();
+ when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
+
+ mService.registerConnectivityDiagnosticsCallback(
+ mConnectivityDiagnosticsCallback, wifiRequest);
+
+ verify(mIBinder, timeout(TIMEOUT_MS))
+ .linkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt());
+ verify(mConnectivityDiagnosticsCallback).asBinder();
+ assertTrue(
+ mService.mConnectivityDiagnosticsCallbacks.containsKey(
+ mConnectivityDiagnosticsCallback));
+
+ // Register the same callback again
+ mService.registerConnectivityDiagnosticsCallback(
+ mConnectivityDiagnosticsCallback, wifiRequest);
+
+ // Block until all other events are done processing.
+ HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
+
+ assertTrue(
+ mService.mConnectivityDiagnosticsCallbacks.containsKey(
+ mConnectivityDiagnosticsCallback));
+ }
}